diff options
| -rw-r--r-- | prog/rand.go | 42 | ||||
| -rw-r--r-- | prog/resources.go | 20 | ||||
| -rw-r--r-- | prog/resources_test.go | 21 | ||||
| -rw-r--r-- | prog/target.go | 4 | ||||
| -rw-r--r-- | prog/types.go | 2 | ||||
| -rw-r--r-- | sys/test/test.txt | 10 |
6 files changed, 73 insertions, 26 deletions
diff --git a/prog/rand.go b/prog/rand.go index 560e1e176..3fbaaaa87 100644 --- a/prog/rand.go +++ b/prog/rand.go @@ -377,8 +377,7 @@ func (r *randGen) createResource(s *state, res *ResourceType, dir Dir) (Arg, []* } kind := res.Desc.Name // Find calls that produce the necessary resources. - // TODO: reduce priority of less specialized ctors. - metas := r.enabledCtors(s, kind) + ctors := r.enabledCtors(s, kind) // We may have no resources, but still be in createResource due to ANYRES. if len(r.target.resourceMap) != 0 && r.oneOf(1000) { // Spoof resource subkind. @@ -394,24 +393,41 @@ func (r *randGen) createResource(s *state, res *ResourceType, dir Dir) (Arg, []* } sort.Strings(all) kind1 := all[r.Intn(len(all))] - metas1 := r.enabledCtors(s, kind1) - if len(metas1) != 0 { + ctors1 := r.enabledCtors(s, kind1) + if len(ctors1) != 0 { // Don't use the resource for which we don't have any ctors. // It's fine per-se because below we just return nil in such case. // But in TestCreateResource tests we want to ensure that we don't fail // to create non-optional resources, and if we spoof a non-optional // resource with ctors with a optional resource w/o ctors, then that check will fail. - kind, metas = kind1, metas1 + kind, ctors = kind1, ctors1 } } - if len(metas) == 0 { + if len(ctors) == 0 { // We may not have any constructors for optional input resources because we don't disable // syscalls based on optional inputs resources w/o ctors in TransitivelyEnabledCalls. return nil, nil } // Now we have a set of candidate calls that can create the necessary resource. // Generate one of them. - meta := metas[r.Intn(len(metas))] + var meta *Syscall + // Prefer precise constructors. + var precise []*Syscall + for _, info := range ctors { + if info.Precise { + precise = append(precise, info.Call) + } + } + if len(precise) > 0 { + // If the argument is optional, it's not guaranteed that there'd be a + // precise constructor. + meta = precise[r.Intn(len(precise))] + } + if meta == nil || r.oneOf(3) { + // Sometimes just take a random one. + meta = ctors[r.Intn(len(ctors))].Call + } + calls := r.generateParticularCall(s, meta) s1 := newState(r.target, s.ct, nil) s1.analyze(calls[len(calls)-1]) @@ -433,14 +449,14 @@ func (r *randGen) createResource(s *state, res *ResourceType, dir Dir) (Arg, []* return arg, calls } -func (r *randGen) enabledCtors(s *state, kind string) []*Syscall { - var metas []*Syscall - for _, meta := range r.target.resourceCtors[kind] { - if s.ct.Generatable(meta.ID) { - metas = append(metas, meta) +func (r *randGen) enabledCtors(s *state, kind string) []ResourceCtor { + var ret []ResourceCtor + for _, info := range r.target.resourceCtors[kind] { + if s.ct.Generatable(info.Call.ID) { + ret = append(ret, info) } } - return metas + return ret } func (r *randGen) generateText(kind TextKind) []byte { diff --git a/prog/resources.go b/prog/resources.go index 61b30581f..d739b528e 100644 --- a/prog/resources.go +++ b/prog/resources.go @@ -27,19 +27,19 @@ var ( } ) -func (target *Target) calcResourceCtors(res *ResourceDesc, precise bool) []*Syscall { - var metas []*Syscall +func (target *Target) calcResourceCtors(res *ResourceDesc, preciseOnly bool) []ResourceCtor { + var ret []ResourceCtor for _, ctor := range res.Ctors { - if !precise || ctor.Precise { - metas = append(metas, target.Syscalls[ctor.Call]) + if !preciseOnly || ctor.Precise { + ret = append(ret, ctor) } } if res.Kind[0] == timespecRes.Name { if c := target.SyscallMap["clock_gettime"]; c != nil { - metas = append(metas, c) + ret = append(ret, ResourceCtor{c, true}) } } - return metas + return ret } func (target *Target) populateResourceCtors() { @@ -91,10 +91,10 @@ func (target *Target) populateResourceCtors() { } } if preciseOk { - res.Ctors = append(res.Ctors, ResourceCtor{call, true}) + res.Ctors = append(res.Ctors, ResourceCtor{target.Syscalls[call], true}) } if impreciseOk { - res.Ctors = append(res.Ctors, ResourceCtor{call, false}) + res.Ctors = append(res.Ctors, ResourceCtor{target.Syscalls[call], false}) } } } @@ -208,8 +208,8 @@ func (target *Target) TransitivelyEnabledCalls(enabled map[*Syscall]bool) (map[* } if ctors[res.Name] == nil { var names []string - for _, call := range target.calcResourceCtors(res, true) { - names = append(names, call.Name) + for _, ctor := range target.calcResourceCtors(res, true) { + names = append(names, ctor.Call.Name) } ctors[res.Name] = names } diff --git a/prog/resources_test.go b/prog/resources_test.go index 222e93c00..ca1a25965 100644 --- a/prog/resources_test.go +++ b/prog/resources_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/google/syzkaller/pkg/testutil" + "github.com/stretchr/testify/assert" ) func TestResourceCtors(t *testing.T) { @@ -190,3 +191,23 @@ func testCreateResource(t *testing.T, target *Target, calls map[*Syscall]bool, r }) } } + +func TestPreferPreciseResources(t *testing.T) { + target, rs, _ := initRandomTargetTest(t, "test", "64") + r := newRand(target, rs) + counts := map[string]int{} + for i := 0; i < 1500; i++ { + s := newState(target, target.DefaultChoiceTable(), nil) + calls := r.generateParticularCall(s, + target.SyscallMap["test$consume_subtype_of_common"]) + for _, call := range calls { + if call.Meta.Name == "test$consume_subtype_of_common" { + continue + } + counts[call.Meta.Name]++ + } + } + assert.Greater(t, counts["test$produce_common"], 15) + assert.Greater(t, counts["test$also_produce_common"], 15) + assert.Greater(t, counts["test$produce_subtype_of_common"], 100) +} diff --git a/prog/target.go b/prog/target.go index 36fde13de..761dce34a 100644 --- a/prog/target.go +++ b/prog/target.go @@ -69,7 +69,7 @@ type Target struct { types []Type resourceMap map[string]*ResourceDesc // Maps resource name to a list of calls that can create the resource. - resourceCtors map[string][]*Syscall + resourceCtors map[string][]ResourceCtor any anyTypes // The default ChoiceTable is used only by tests and utilities, so we initialize it lazily. @@ -169,7 +169,7 @@ func (target *Target) initTarget() { } target.populateResourceCtors() - target.resourceCtors = make(map[string][]*Syscall) + target.resourceCtors = make(map[string][]ResourceCtor) for _, res := range target.Resources { target.resourceCtors[res.Name] = target.calcResourceCtors(res, false) } diff --git a/prog/types.go b/prog/types.go index cbba259c9..51f5e0cbe 100644 --- a/prog/types.go +++ b/prog/types.go @@ -249,7 +249,7 @@ type ResourceDesc struct { } type ResourceCtor struct { - Call int // index in Target.Syscalls + Call *Syscall Precise bool } diff --git a/sys/test/test.txt b/sys/test/test.txt index 2edb462d1..1b63cd525 100644 --- a/sys/test/test.txt +++ b/sys/test/test.txt @@ -927,3 +927,13 @@ test_args0 { test$output_res(arg ptr[out, test_args0]) test$optional_res(arg ptr[in, test_args1]) + +resource common[int32] +resource subtype_of_common[common] + +test$produce_common() common +test$also_produce_common() common +test$produce_subtype_of_common() subtype_of_common + +test$consume_common(val common) +test$consume_subtype_of_common(val subtype_of_common) |
