diff options
| author | Aleksandr Nogikh <nogikh@google.com> | 2024-01-10 14:28:27 +0100 |
|---|---|---|
| committer | Aleksandr Nogikh <nogikh@google.com> | 2024-01-11 14:13:27 +0000 |
| commit | dda5a9889e432dc7e9efe71a39292073fa6f6c00 (patch) | |
| tree | aff1da5e8b86de5f14c9482f7c983ec34b2060f4 | |
| parent | 00f3cc59cbd59389deb590c4a852ea30d8c93499 (diff) | |
prog: prefer precise constructors
During resource argument generation, we used to randomly select one of
the matching resources. With so many descendants of fd, this becomes
quite inefficient and most of the time syzkaller fails to build correct
programs.
Give precise resource contructions priority. Experiment with other
resource types only in 1/3 of cases.
| -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) |
