diff options
| -rw-r--r-- | docs/syscall_descriptions_syntax.md | 1 | ||||
| -rw-r--r-- | prog/any_test.go | 2 | ||||
| -rw-r--r-- | prog/prio.go | 43 | ||||
| -rw-r--r-- | prog/rand.go | 16 | ||||
| -rw-r--r-- | prog/rand_test.go | 31 | ||||
| -rw-r--r-- | prog/types.go | 3 |
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. |
