aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pkg/mgrconfig/config.go2
-rw-r--r--pkg/mgrconfig/load.go28
-rw-r--r--pkg/rpctype/rpctype.go1
-rw-r--r--prog/checksum_test.go2
-rw-r--r--prog/mutation.go41
-rw-r--r--prog/mutation_test.go8
-rw-r--r--prog/prog_test.go2
-rw-r--r--prog/rand_test.go6
-rw-r--r--prog/size_test.go2
-rw-r--r--prog/test/fuzz.go2
-rw-r--r--syz-fuzzer/fuzzer.go2
-rw-r--r--syz-fuzzer/proc.go4
-rw-r--r--syz-manager/rpc.go1
-rw-r--r--tools/syz-mutate/mutate.go2
-rw-r--r--tools/syz-stress/stress.go6
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)
}
}