aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--prog/rand.go42
-rw-r--r--prog/resources.go20
-rw-r--r--prog/resources_test.go21
-rw-r--r--prog/target.go4
-rw-r--r--prog/types.go2
-rw-r--r--sys/test/test.txt10
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)