diff options
| -rw-r--r-- | prog/expr.go | 13 | ||||
| -rw-r--r-- | prog/expr_test.go | 22 | ||||
| -rw-r--r-- | prog/rand.go | 8 | ||||
| -rw-r--r-- | sys/test/expressions.txt | 23 |
4 files changed, 56 insertions, 10 deletions
diff --git a/prog/expr.go b/prog/expr.go index cfe6eb70b..e9446ab2c 100644 --- a/prog/expr.go +++ b/prog/expr.go @@ -67,11 +67,14 @@ func makeArgFinder(t *Target, c *Call, unionArg *UnionArg, parents parentStack) } func (r *randGen) patchConditionalFields(c *Call, s *state) (extra []*Call, changed bool) { - if r.inPatchConditional { - return nil, false - } - r.inPatchConditional = true - defer func() { r.inPatchConditional = false }() + if r.patchConditionalDepth > 1 { + // Some nested patchConditionalFields() calls are fine as we could trigger a resource + // constructor via generateArg(). But since nested createResource() calls are prohibited, + // patchConditionalFields() should never be nested more than 2 times. + panic("third nested patchConditionalFields call") + } + r.patchConditionalDepth++ + defer func() { r.patchConditionalDepth-- }() var extraCalls []*Call var anyPatched bool diff --git a/prog/expr_test.go b/prog/expr_test.go index aaae6a74a..74818ea55 100644 --- a/prog/expr_test.go +++ b/prog/expr_test.go @@ -280,3 +280,25 @@ func TestConditionalUnionFields(t *testing.T) { assert.Greater(t, zeroU2, 0) assert.Greater(t, nonzeroU2, 0) } + +func TestNestedConditionalCall(t *testing.T) { + // Ensure that we reach different combinations of conditional fields. + target, rs, _ := initRandomTargetTest(t, "test", "64") + ct := target.DefaultChoiceTable() + r := newRand(target, rs) + + for i := 0; i < 100; i++ { + for _, name := range []string{"test$conditional_struct_nested", "test$conditional_struct_nested2"} { + s := newState(target, ct, nil) + calls := r.generateParticularCall(s, target.SyscallMap[name]) + p := &Prog{ + Target: target, + Calls: calls, + } + err := p.checkConditions() + if err != nil { + t.Fatal(err) + } + } + } +} diff --git a/prog/rand.go b/prog/rand.go index b15871b8a..760f7a904 100644 --- a/prog/rand.go +++ b/prog/rand.go @@ -25,10 +25,10 @@ const ( type randGen struct { *rand.Rand - target *Target - inGenerateResource bool - inPatchConditional bool - recDepth map[string]int + target *Target + inGenerateResource bool + patchConditionalDepth int + recDepth map[string]int } func newRand(target *Target, rs rand.Source) *randGen { diff --git a/sys/test/expressions.txt b/sys/test/expressions.txt index 1c1cfbca5..500bdec20 100644 --- a/sys/test/expressions.txt +++ b/sys/test/expressions.txt @@ -16,7 +16,28 @@ conditional_struct { f2 int64 (if[value[mask] & FIELD_FLAG2]) } [packed] -test$conditional_struct(a ptr[in, conditional_struct]) +resource some_res[int32] + +test$conditional_struct(a ptr[in, conditional_struct]) some_res + +# To generate this call, we need to recursively also generate a test$conditional_struct() call. + +needs_some_res { + switch int32 + field some_res (if[value[switch] != 0]) +} + +resource some_res_nested[int32] +test$conditional_struct_nested(a ptr[in, needs_some_res]) some_res_nested + +# .. and one more level. + +needs_some_res_nested { + switch int32 + field some_res_nested (if[value[switch] != 0]) +} + +test$conditional_struct_nested2(a ptr[in, needs_some_res_nested]) parent_conditions { mask int32 |
