aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/syscall_descriptions_syntax.md1
-rw-r--r--prog/any_test.go2
-rw-r--r--prog/prio.go43
-rw-r--r--prog/rand.go16
-rw-r--r--prog/rand_test.go31
-rw-r--r--prog/types.go3
6 files changed, 74 insertions, 22 deletions
diff --git a/docs/syscall_descriptions_syntax.md b/docs/syscall_descriptions_syntax.md
index 29bfd7edd..cf490e5a7 100644
--- a/docs/syscall_descriptions_syntax.md
+++ b/docs/syscall_descriptions_syntax.md
@@ -93,6 +93,7 @@ Call attributes are:
"ignore_return": ignore return value of this syscall in fallback feedback; need to be used for calls
that don't return fixed error codes but rather something else (e.g. the current time).
"breaks_returns": ignore return values of all subsequent calls in the program in fallback feedback (can't be trusted).
+"no_generate": do not try to generate this syscall, i.e. use only seed descriptions to produce it.
```
## Ints
diff --git a/prog/any_test.go b/prog/any_test.go
index 386ce8443..0e2f5e0e4 100644
--- a/prog/any_test.go
+++ b/prog/any_test.go
@@ -20,7 +20,7 @@ func TestIsComplexPtr(t *testing.T) {
r := newRand(target, rs)
compl := make(map[string]bool)
for _, meta := range target.Syscalls {
- if meta.Attrs.Disabled {
+ if meta.Attrs.Disabled || meta.Attrs.NoGenerate {
continue
}
for i := 0; i < iters; i++ {
diff --git a/prog/prio.go b/prog/prio.go
index c89869b95..d0013b75d 100644
--- a/prog/prio.go
+++ b/prog/prio.go
@@ -180,11 +180,12 @@ func normalizePrio(prios [][]int32) {
}
// ChooseTable allows to do a weighted choice of a syscall for a given syscall
-// based on call-to-call priorities and a set of enabled syscalls.
+// based on call-to-call priorities and a set of enabled and generatable syscalls.
type ChoiceTable struct {
- target *Target
- runs [][]int32
- calls []*Syscall
+ target *Target
+ runs [][]int32
+ calls []*Syscall
+ noGenerateCalls map[int]bool
}
func (target *Target) BuildChoiceTable(corpus []*Prog, enabled map[*Syscall]bool) *ChoiceTable {
@@ -194,24 +195,28 @@ func (target *Target) BuildChoiceTable(corpus []*Prog, enabled map[*Syscall]bool
enabled[c] = true
}
}
+ noGenerateCalls := make(map[int]bool)
for call := range enabled {
if call.Attrs.Disabled {
delete(enabled, call)
+ } else if call.Attrs.NoGenerate {
+ noGenerateCalls[call.ID] = true
+ delete(enabled, call)
}
}
- var enabledCalls []*Syscall
+ var generatableCalls []*Syscall
for c := range enabled {
- enabledCalls = append(enabledCalls, c)
+ generatableCalls = append(generatableCalls, c)
}
- if len(enabledCalls) == 0 {
- panic("no syscalls enabled")
+ if len(generatableCalls) == 0 {
+ panic("no syscalls enabled and generatable")
}
- sort.Slice(enabledCalls, func(i, j int) bool {
- return enabledCalls[i].ID < enabledCalls[j].ID
+ sort.Slice(generatableCalls, func(i, j int) bool {
+ return generatableCalls[i].ID < generatableCalls[j].ID
})
for _, p := range corpus {
for _, call := range p.Calls {
- if !enabled[call.Meta] {
+ if !enabled[call.Meta] && !noGenerateCalls[call.Meta.ID] {
fmt.Printf("corpus contains disabled syscall %v\n", call.Meta.Name)
panic("disabled syscall")
}
@@ -232,10 +237,14 @@ func (target *Target) BuildChoiceTable(corpus []*Prog, enabled map[*Syscall]bool
run[i][j] = sum
}
}
- return &ChoiceTable{target, run, enabledCalls}
+ return &ChoiceTable{target, run, generatableCalls, noGenerateCalls}
}
func (ct *ChoiceTable) Enabled(call int) bool {
+ return ct.Generatable(call) || ct.noGenerateCalls[call]
+}
+
+func (ct *ChoiceTable) Generatable(call int) bool {
return ct.runs[call] != nil
}
@@ -243,17 +252,17 @@ func (ct *ChoiceTable) choose(r *rand.Rand, bias int) int {
if bias < 0 {
bias = ct.calls[r.Intn(len(ct.calls))].ID
}
- if !ct.Enabled(bias) {
- fmt.Printf("bias to disabled syscall %v\n", ct.target.Syscalls[bias].Name)
- panic("disabled syscall")
+ if !ct.Generatable(bias) {
+ fmt.Printf("bias to disabled or non-generatable syscall %v\n", ct.target.Syscalls[bias].Name)
+ panic("disabled or non-generatable syscall")
}
run := ct.runs[bias]
x := int32(r.Intn(int(run[len(run)-1])) + 1)
res := sort.Search(len(run), func(i int) bool {
return run[i] >= x
})
- if !ct.Enabled(res) {
- panic("selected disabled syscall")
+ if !ct.Generatable(res) {
+ panic("selected disabled or non-generatable syscall")
}
return res
}
diff --git a/prog/rand.go b/prog/rand.go
index 1db5cd07a..2b08998c6 100644
--- a/prog/rand.go
+++ b/prog/rand.go
@@ -436,7 +436,7 @@ func (r *randGen) createResource(s *state, res *ResourceType, dir Dir) (Arg, []*
func (r *randGen) enabledCtors(s *state, kind string) []*Syscall {
var metas []*Syscall
for _, meta := range r.target.resourceCtors[kind] {
- if s.ct.Enabled(meta.ID) {
+ if s.ct.Generatable(meta.ID) {
metas = append(metas, meta)
}
}
@@ -562,7 +562,11 @@ func (r *randGen) generateCall(s *state, p *Prog, insertionPoint int) []*Call {
biasCall := -1
if insertionPoint > 0 {
// Choosing the base call is based on the insertion point of the new calls sequence.
- biasCall = p.Calls[r.Intn(insertionPoint)].Meta.ID
+ insertionCall := p.Calls[r.Intn(insertionPoint)].Meta
+ if !insertionCall.Attrs.NoGenerate {
+ // We must be careful not to bias towards a non-generatable call.
+ biasCall = insertionCall.ID
+ }
}
idx := s.ct.choose(r.Rand, biasCall)
meta := r.target.Syscalls[idx]
@@ -573,6 +577,9 @@ func (r *randGen) generateParticularCall(s *state, meta *Syscall) (calls []*Call
if meta.Attrs.Disabled {
panic(fmt.Sprintf("generating disabled call %v", meta.Name))
}
+ if meta.Attrs.NoGenerate {
+ panic(fmt.Sprintf("generating no_generate call: %v", meta.Name))
+ }
c := MakeCall(meta, nil)
c.Args, calls = r.generateArgs(s, meta.Args, DirIn)
r.target.assignSizesCall(c)
@@ -588,7 +595,10 @@ func (target *Target) GenerateAllSyzProg(rs rand.Source) *Prog {
s := newState(target, target.DefaultChoiceTable(), nil)
handled := make(map[string]bool)
for _, meta := range target.Syscalls {
- if !strings.HasPrefix(meta.CallName, "syz_") || handled[meta.CallName] || meta.Attrs.Disabled {
+ if !strings.HasPrefix(meta.CallName, "syz_") ||
+ handled[meta.CallName] ||
+ meta.Attrs.Disabled ||
+ meta.Attrs.NoGenerate {
continue
}
handled[meta.CallName] = true
diff --git a/prog/rand_test.go b/prog/rand_test.go
index 5d9b53d48..cc663bf8b 100644
--- a/prog/rand_test.go
+++ b/prog/rand_test.go
@@ -203,3 +203,34 @@ func TestTruncateToBitSize(t *testing.T) {
})
}
}
+
+// Checks that a generated program does not contain any "no_generate" syscalls.
+func TestNoGenerate(t *testing.T) {
+ target, rs, iters := initTest(t)
+
+ // Enable all "no_generate" syscalls and ~10% of all others.
+ enabled := make(map[*Syscall]bool)
+ rnd := newRand(target, rs)
+ for i := 0; i < len(target.Syscalls); i++ {
+ syscall := target.Syscalls[rnd.Intn(len(target.Syscalls))]
+ if syscall.Attrs.NoGenerate || rnd.oneOf(10) {
+ enabled[syscall] = true
+ }
+ }
+ c := target.SyscallMap["clock_gettime"]
+ enabled[c] = true
+ ct := target.BuildChoiceTable(nil, enabled)
+
+ const tries = 10
+ for i := 0; i < tries; i++ {
+ p := target.Generate(rs, 50, ct)
+ for it := 0; it < iters/tries; it++ {
+ p.Mutate(rs, 50, ct, nil)
+ }
+ for _, c := range p.Calls {
+ if c.Meta.Attrs.NoGenerate {
+ t.Fatalf("program contains a no_generate syscall: %v\n", c.Meta.Name)
+ }
+ }
+ }
+}
diff --git a/prog/types.go b/prog/types.go
index cca6f1d6c..a90c1d562 100644
--- a/prog/types.go
+++ b/prog/types.go
@@ -29,7 +29,7 @@ type Syscall struct {
// pkg/compiler uses this structure to parse descriptions.
// syz-sysgen uses this structure to generate code for executor.
//
-// Only bool's and uint64's are currently supported.
+// Only `bool`s and `uint64`s are currently supported.
//
// See docs/syscall_descriptions_syntax.md for description of individual attributes.
type SyscallAttrs struct {
@@ -38,6 +38,7 @@ type SyscallAttrs struct {
ProgTimeout uint64
IgnoreReturn bool
BreaksReturns bool
+ NoGenerate bool
}
// MaxArgs is maximum number of syscall arguments.