diff options
| -rw-r--r-- | pkg/mgrconfig/config.go | 2 | ||||
| -rw-r--r-- | pkg/mgrconfig/load.go | 28 | ||||
| -rw-r--r-- | pkg/rpctype/rpctype.go | 1 | ||||
| -rw-r--r-- | prog/checksum_test.go | 2 | ||||
| -rw-r--r-- | prog/mutation.go | 41 | ||||
| -rw-r--r-- | prog/mutation_test.go | 8 | ||||
| -rw-r--r-- | prog/prog_test.go | 2 | ||||
| -rw-r--r-- | prog/rand_test.go | 6 | ||||
| -rw-r--r-- | prog/size_test.go | 2 | ||||
| -rw-r--r-- | prog/test/fuzz.go | 2 | ||||
| -rw-r--r-- | syz-fuzzer/fuzzer.go | 2 | ||||
| -rw-r--r-- | syz-fuzzer/proc.go | 4 | ||||
| -rw-r--r-- | syz-manager/rpc.go | 1 | ||||
| -rw-r--r-- | tools/syz-mutate/mutate.go | 2 | ||||
| -rw-r--r-- | tools/syz-stress/stress.go | 6 |
15 files changed, 74 insertions, 35 deletions
diff --git a/pkg/mgrconfig/config.go b/pkg/mgrconfig/config.go index 9073c482c..46270ab0c 100644 --- a/pkg/mgrconfig/config.go +++ b/pkg/mgrconfig/config.go @@ -166,6 +166,8 @@ type Config struct { EnabledSyscalls []string `json:"enable_syscalls,omitempty"` // List of system calls that should be treated as disabled (optional). DisabledSyscalls []string `json:"disable_syscalls,omitempty"` + // List of syscalls that should not be mutated by the fuzzer (optional). + NoMutateSyscalls []string `json:"no_mutate_syscalls,omitempty"` // List of regexps for known bugs. // Don't save reports matching these regexps, but reboot VM after them, // matched against whole report output. diff --git a/pkg/mgrconfig/load.go b/pkg/mgrconfig/load.go index 2b49d0cbd..f1063ed17 100644 --- a/pkg/mgrconfig/load.go +++ b/pkg/mgrconfig/load.go @@ -34,8 +34,9 @@ type Derived struct { ExecprogBin string ExecutorBin string - Syscalls []int - Timeouts targets.Timeouts + Syscalls []int + NoMutateCalls map[int]bool // Set of IDs of syscalls which should not be mutated. + Timeouts targets.Timeouts } func LoadData(data []byte) (*Config, error) { @@ -177,6 +178,10 @@ func Complete(cfg *Config) error { if err != nil { return err } + cfg.NoMutateCalls, err = ParseNoMutateSyscalls(cfg.Target, cfg.NoMutateSyscalls) + if err != nil { + return err + } cfg.initTimeouts() return nil } @@ -329,6 +334,25 @@ func ParseEnabledSyscalls(target *prog.Target, enabled, disabled []string) ([]in return arr, nil } +func ParseNoMutateSyscalls(target *prog.Target, syscalls []string) (map[int]bool, error) { + var result = make(map[int]bool) + + for _, c := range syscalls { + n := 0 + for _, call := range target.Syscalls { + if MatchSyscall(call.Name, c) { + result[call.ID] = true + n++ + } + } + if n == 0 { + return nil, fmt.Errorf("unknown no_mutate syscall: %v", c) + } + } + + return result, nil +} + func MatchSyscall(name, pattern string) bool { if pattern == name || strings.HasPrefix(name, pattern+"$") { return true diff --git a/pkg/rpctype/rpctype.go b/pkg/rpctype/rpctype.go index f1d889a20..fc582e576 100644 --- a/pkg/rpctype/rpctype.go +++ b/pkg/rpctype/rpctype.go @@ -41,6 +41,7 @@ type ConnectArgs struct { type ConnectRes struct { EnabledCalls []int + NoMutateCalls map[int]bool GitRevision string TargetRevision string AllSandboxes bool diff --git a/prog/checksum_test.go b/prog/checksum_test.go index 46fdce6b7..abed9803c 100644 --- a/prog/checksum_test.go +++ b/prog/checksum_test.go @@ -18,7 +18,7 @@ func TestChecksumCalcRandom(t *testing.T) { for _, call := range p.Calls { CalcChecksumsCall(call) } - p.Mutate(rs, 10, ct, nil) + p.Mutate(rs, 10, ct, nil, nil) for _, call := range p.Calls { CalcChecksumsCall(call) } diff --git a/prog/mutation.go b/prog/mutation.go index b03987671..ca53b1c7b 100644 --- a/prog/mutation.go +++ b/prog/mutation.go @@ -16,22 +16,24 @@ const maxBlobLen = uint64(100 << 10) // Mutate program p. // -// p: The program to mutate. -// rs: Random source. -// ncalls: The allowed maximum calls in mutated program. -// ct: ChoiceTable for syscalls. -// corpus: The entire corpus, including original program p. -func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable, corpus []*Prog) { +// p: The program to mutate. +// rs: Random source. +// ncalls: The allowed maximum calls in mutated program. +// ct: ChoiceTable for syscalls. +// noMutate: Set of IDs of syscalls which should not be mutated. +// corpus: The entire corpus, including original program p. +func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable, noMutate map[int]bool, corpus []*Prog) { r := newRand(p.Target, rs) if ncalls < len(p.Calls) { ncalls = len(p.Calls) } ctx := &mutator{ - p: p, - r: r, - ncalls: ncalls, - ct: ct, - corpus: corpus, + p: p, + r: r, + ncalls: ncalls, + ct: ct, + noMutate: noMutate, + corpus: corpus, } for stop, ok := false, false; !stop; stop = ok && len(p.Calls) != 0 && r.oneOf(3) { switch { @@ -59,11 +61,12 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable, corpus []*Pro // Internal state required for performing mutations -- currently this matches // the arguments passed to Mutate(). type mutator struct { - p *Prog // The program to mutate. - r *randGen // The randGen instance. - ncalls int // The allowed maximum calls in mutated program. - ct *ChoiceTable // ChoiceTable for syscalls. - corpus []*Prog // The entire corpus, including original program p. + p *Prog // The program to mutate. + r *randGen // The randGen instance. + ncalls int // The allowed maximum calls in mutated program. + ct *ChoiceTable // ChoiceTable for syscalls. + noMutate map[int]bool // Set of IDs of syscalls which should not be mutated. + corpus []*Prog // The entire corpus, including original program p. } // This function selects a random other program p0 out of the corpus, and @@ -93,6 +96,9 @@ func (ctx *mutator) squashAny() bool { return false } ptr := complexPtrs[r.Intn(len(complexPtrs))] + if ctx.noMutate[ptr.call.Meta.ID] { + return false + } if !p.Target.isAnyPtr(ptr.arg.Type()) { p.Target.squashPtr(ptr.arg) } @@ -172,6 +178,9 @@ func (ctx *mutator) mutateArg() bool { return false } c := p.Calls[idx] + if ctx.noMutate[c.Meta.ID] { + return false + } updateSizes := true for stop, ok := false, false; !stop; stop = ok && r.oneOf(3) { ok = true diff --git a/prog/mutation_test.go b/prog/mutation_test.go index 5edba991c..0c084d1fe 100644 --- a/prog/mutation_test.go +++ b/prog/mutation_test.go @@ -221,7 +221,7 @@ func TestMutateRandom(t *testing.T) { // There is a chance that mutation will produce the same program. // So we check that at least 1 out of 20 mutations actually change the program. for try := 0; try < 20; try++ { - p1.Mutate(rs, 10, ct, nil) + p1.Mutate(rs, 10, ct, nil, nil) data := p.Serialize() if !bytes.Equal(data0, data) { t.Fatalf("program changed after mutate\noriginal:\n%s\n\nnew:\n%s\n", @@ -251,7 +251,7 @@ func TestMutateCorpus(t *testing.T) { } for i := 0; i < iters; i++ { p1 := target.Generate(rs, 10, ct) - p1.Mutate(rs, 10, ct, corpus) + p1.Mutate(rs, 10, ct, nil, corpus) } } @@ -383,7 +383,7 @@ func BenchmarkMutate(b *testing.B) { b.RunParallel(func(pb *testing.PB) { rs := rand.NewSource(0) for pb.Next() { - p.Clone().Mutate(rs, progLen, ct, nil) + p.Clone().Mutate(rs, progLen, ct, nil, nil) } }) } @@ -422,7 +422,7 @@ func runMutationTests(t *testing.T, tests [][2]string, valid bool) { } for i := 0; i < iters; i++ { p1 := p.Clone() - p1.Mutate(rs, len(goal.Calls), ct, nil) + p1.Mutate(rs, len(goal.Calls), ct, nil, nil) data1 := p1.Serialize() if bytes.Equal(want, data1) { if !valid { diff --git a/prog/prog_test.go b/prog/prog_test.go index 2247694a1..9e347b4e8 100644 --- a/prog/prog_test.go +++ b/prog/prog_test.go @@ -193,7 +193,7 @@ func testCrossTarget(t *testing.T, target *Target, crossTargets []*Target) { t.Fatal(err) } testCrossArchProg(t, p, crossTargets) - p.Mutate(rs, 20, ct, nil) + p.Mutate(rs, 20, ct, nil, nil) testCrossArchProg(t, p, crossTargets) p, _ = Minimize(p, -1, false, func(*Prog, int) bool { return rs.Int63()%2 == 0 diff --git a/prog/rand_test.go b/prog/rand_test.go index cc663bf8b..ffe9bd822 100644 --- a/prog/rand_test.go +++ b/prog/rand_test.go @@ -56,7 +56,7 @@ func TestDeterminism(t *testing.T) { func generateProg(t *testing.T, target *Target, rs rand.Source, ct *ChoiceTable, corpus []*Prog) *Prog { p := target.Generate(rs, 5, ct) - p.Mutate(rs, 10, ct, corpus) + p.Mutate(rs, 10, ct, nil, corpus) for i, c := range p.Calls { comps := make(CompMap) for v := range extractValues(c) { @@ -101,7 +101,7 @@ func TestEnabledCalls(t *testing.T) { 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) + p.Mutate(rs, 50, ct, nil, nil) } for _, c := range p.Calls { if _, ok := enabledCalls[c.Meta.Name]; !ok { @@ -225,7 +225,7 @@ func TestNoGenerate(t *testing.T) { 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) + p.Mutate(rs, 50, ct, nil, nil) } for _, c := range p.Calls { if c.Meta.Attrs.NoGenerate { diff --git a/prog/size_test.go b/prog/size_test.go index 6e8a28c4d..ea8b7a58a 100644 --- a/prog/size_test.go +++ b/prog/size_test.go @@ -20,7 +20,7 @@ func TestAssignSizeRandom(t *testing.T) { if data1 := p.Serialize(); !bytes.Equal(data0, data1) { t.Fatalf("different lens assigned, initial:\n%s\nnew:\n%s\n", data0, data1) } - p.Mutate(rs, 10, ct, nil) + p.Mutate(rs, 10, ct, nil, nil) p.Serialize() for _, call := range p.Calls { target.assignSizesCall(call) diff --git a/prog/test/fuzz.go b/prog/test/fuzz.go index 0d2807f7f..5857ff98d 100644 --- a/prog/test/fuzz.go +++ b/prog/test/fuzz.go @@ -53,7 +53,7 @@ func FuzzDeserialize(data []byte) int { panic(err) } } - p3.Mutate(rand.NewSource(0), 3, fuzzChoiceTable, nil) + p3.Mutate(rand.NewSource(0), 3, fuzzChoiceTable, nil, nil) return 0 } diff --git a/syz-fuzzer/fuzzer.go b/syz-fuzzer/fuzzer.go index d281c38c6..cce2b6630 100644 --- a/syz-fuzzer/fuzzer.go +++ b/syz-fuzzer/fuzzer.go @@ -40,6 +40,7 @@ type Fuzzer struct { workQueue *WorkQueue needPoll chan struct{} choiceTable *prog.ChoiceTable + noMutate map[int]bool stats [StatCount]uint64 manager *rpctype.RPCClient target *prog.Target @@ -275,6 +276,7 @@ func main() { corpusHashes: make(map[hash.Sig]struct{}), checkResult: r.CheckResult, fetchRawCover: *flagRawCover, + noMutate: r.NoMutateCalls, } gateCallback := fuzzer.useBugFrames(r, *flagProcs) fuzzer.gate = ipc.NewGate(2**flagProcs, gateCallback) diff --git a/syz-fuzzer/proc.go b/syz-fuzzer/proc.go index d19490fc8..a215d81c8 100644 --- a/syz-fuzzer/proc.go +++ b/syz-fuzzer/proc.go @@ -92,7 +92,7 @@ func (proc *Proc) loop() { } else { // Mutate an existing prog. p := fuzzerSnapshot.chooseProgram(proc.rnd).Clone() - p.Mutate(proc.rnd, prog.RecommendedCalls, ct, fuzzerSnapshot.corpus) + p.Mutate(proc.rnd, prog.RecommendedCalls, ct, proc.fuzzer.noMutate, fuzzerSnapshot.corpus) log.Logf(1, "#%v: mutated", proc.pid) proc.executeAndCollide(proc.execOpts, p, ProgNormal, StatFuzz) } @@ -216,7 +216,7 @@ func (proc *Proc) smashInput(item *WorkSmash) { fuzzerSnapshot := proc.fuzzer.snapshot() for i := 0; i < 100; i++ { p := item.p.Clone() - p.Mutate(proc.rnd, prog.RecommendedCalls, proc.fuzzer.choiceTable, fuzzerSnapshot.corpus) + p.Mutate(proc.rnd, prog.RecommendedCalls, proc.fuzzer.choiceTable, proc.fuzzer.noMutate, fuzzerSnapshot.corpus) log.Logf(1, "#%v: smash mutated", proc.pid) proc.executeAndCollide(proc.execOpts, p, ProgNormal, StatSmash) } diff --git a/syz-manager/rpc.go b/syz-manager/rpc.go index 397484783..ea91e601c 100644 --- a/syz-manager/rpc.go +++ b/syz-manager/rpc.go @@ -109,6 +109,7 @@ func (serv *RPCServer) Connect(a *rpctype.ConnectArgs, r *rpctype.ConnectRes) er r.DataRaceFrames = bugFrames.dataRaces r.CoverFilterBitmap = coverBitmap r.EnabledCalls = serv.cfg.Syscalls + r.NoMutateCalls = serv.cfg.NoMutateCalls r.GitRevision = prog.GitRevision r.TargetRevision = serv.cfg.Target.Revision if serv.mgr.rotateCorpus() && serv.rnd.Intn(5) == 0 { diff --git a/tools/syz-mutate/mutate.go b/tools/syz-mutate/mutate.go index b6b43f48f..7619acbd9 100644 --- a/tools/syz-mutate/mutate.go +++ b/tools/syz-mutate/mutate.go @@ -79,7 +79,7 @@ func main() { fmt.Fprintf(os.Stderr, "failed to deserialize the program: %v\n", err) os.Exit(1) } - p.Mutate(rs, *flagLen, ct, corpus) + p.Mutate(rs, *flagLen, ct, nil, corpus) } fmt.Printf("%s\n", p.Serialize()) } diff --git a/tools/syz-stress/stress.go b/tools/syz-stress/stress.go index 15847df16..26d17844b 100644 --- a/tools/syz-stress/stress.go +++ b/tools/syz-stress/stress.go @@ -104,13 +104,13 @@ func main() { if *flagGenerate && len(corpus) == 0 || i%4 != 0 { p = target.Generate(rs, prog.RecommendedCalls, ct) execute(pid, env, execOpts, p) - p.Mutate(rs, prog.RecommendedCalls, ct, corpus) + p.Mutate(rs, prog.RecommendedCalls, ct, nil, corpus) execute(pid, env, execOpts, p) } else { p = corpus[rnd.Intn(len(corpus))].Clone() - p.Mutate(rs, prog.RecommendedCalls, ct, corpus) + p.Mutate(rs, prog.RecommendedCalls, ct, nil, corpus) execute(pid, env, execOpts, p) - p.Mutate(rs, prog.RecommendedCalls, ct, corpus) + p.Mutate(rs, prog.RecommendedCalls, ct, nil, corpus) execute(pid, env, execOpts, p) } } |
