aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2017-12-18 10:29:40 +0100
committerDmitry Vyukov <dvyukov@google.com>2017-12-18 11:40:51 +0100
commit9f48e03d80af4edfc99cf01811234cf5bf562aa2 (patch)
tree30d12fd0975d7185f9220e9cbaa93e363217aa2b
parent0d231ceb731d89cab71f6ad2ad1aa9d766b2e243 (diff)
syz-fuzzer: encapsulate corpus in fuzzer
Make corpus a fuzzer member rather than global var. This resolves existing races on corpus.
-rw-r--r--syz-fuzzer/fuzzer.go73
-rw-r--r--syz-fuzzer/proc.go28
2 files changed, 46 insertions, 55 deletions
diff --git a/syz-fuzzer/fuzzer.go b/syz-fuzzer/fuzzer.go
index e907f715a..774326a15 100644
--- a/syz-fuzzer/fuzzer.go
+++ b/syz-fuzzer/fuzzer.go
@@ -49,10 +49,6 @@ var (
maxSignal map[uint32]struct{}
newSignal map[uint32]struct{}
- corpusMu sync.RWMutex
- corpus []*prog.Prog
- corpusHashes map[hash.Sig]struct{}
-
allTriaged uint32
noCover bool
faultInjectionEnabled bool
@@ -67,6 +63,10 @@ type Fuzzer struct {
workQueue *WorkQueue
choiceTable *prog.ChoiceTable
stats [StatCount]uint64
+
+ corpusMu sync.RWMutex
+ corpus []*prog.Prog
+ corpusHashes map[hash.Sig]struct{}
}
type Stat int
@@ -126,7 +126,6 @@ func main() {
corpusSignal = make(map[uint32]struct{})
maxSignal = make(map[uint32]struct{})
newSignal = make(map[uint32]struct{})
- corpusHashes = make(map[hash.Sig]struct{})
Logf(0, "dialing manager at %v", *flagManager)
a := &ConnectArgs{*flagName}
@@ -136,12 +135,6 @@ func main() {
}
calls := buildCallList(target, r.EnabledCalls)
ct := target.BuildChoiceTable(r.Prios, calls)
- for _, inp := range r.Inputs {
- addInput(inp)
- }
- for _, s := range r.MaxSignal {
- maxSignal[s] = struct{}{}
- }
// This requires "fault-inject: support systematic fault injection" kernel commit.
if fd, err := syscall.Open("/proc/self/fail-nth", syscall.O_RDWR, 0); err == nil {
@@ -222,22 +215,27 @@ func main() {
needPoll := make(chan struct{}, 1)
needPoll <- struct{}{}
fuzzer := &Fuzzer{
- config: config,
- execOpts: execOpts,
- gate: ipc.NewGate(2**flagProcs, leakCallback),
- workQueue: newWorkQueue(*flagProcs, needPoll),
- choiceTable: ct,
+ config: config,
+ execOpts: execOpts,
+ gate: ipc.NewGate(2**flagProcs, leakCallback),
+ workQueue: newWorkQueue(*flagProcs, needPoll),
+ choiceTable: ct,
+ corpusHashes: make(map[hash.Sig]struct{}),
}
+ for _, inp := range r.Inputs {
+ fuzzer.addInput(inp)
+ }
+ for _, s := range r.MaxSignal {
+ maxSignal[s] = struct{}{}
+ }
for _, candidate := range r.Candidates {
p, err := target.Deserialize(candidate.Prog)
if err != nil {
panic(err)
}
if noCover {
- corpusMu.Lock()
- corpus = append(corpus, p)
- corpusMu.Unlock()
+ fuzzer.addInputToCorpus(p, hash.Hash(candidate.Prog))
} else {
fuzzer.workQueue.enqueue(&WorkCandidate{p, candidate.Minimized})
}
@@ -318,7 +316,7 @@ func main() {
signalMu.Unlock()
}
for _, inp := range r.NewInputs {
- addInput(inp)
+ fuzzer.addInput(inp)
}
for _, candidate := range r.Candidates {
p, err := target.Deserialize(candidate.Prog)
@@ -326,9 +324,7 @@ func main() {
panic(err)
}
if noCover {
- corpusMu.Lock()
- corpus = append(corpus, p)
- corpusMu.Unlock()
+ fuzzer.addInputToCorpus(p, hash.Hash(candidate.Prog))
} else {
fuzzer.workQueue.enqueue(&WorkCandidate{p, candidate.Minimized})
}
@@ -383,12 +379,7 @@ func buildCallList(target *prog.Target, enabledCalls string) map[*prog.Syscall]b
return calls
}
-func addInput(inp RpcInput) {
- corpusMu.Lock()
- defer corpusMu.Unlock()
- signalMu.Lock()
- defer signalMu.Unlock()
-
+func (fuzzer *Fuzzer) addInput(inp RpcInput) {
if noCover {
panic("should not be called when coverage is disabled")
}
@@ -396,13 +387,27 @@ func addInput(inp RpcInput) {
if err != nil {
panic(err)
}
- sig := hash.Hash(inp.Prog)
- if _, ok := corpusHashes[sig]; !ok {
- corpus = append(corpus, p)
- corpusHashes[sig] = struct{}{}
- }
+ fuzzer.addInputToCorpus(p, hash.Hash(inp.Prog))
+
+ signalMu.Lock()
+ defer signalMu.Unlock()
if diff := cover.SignalDiff(maxSignal, inp.Signal); len(diff) != 0 {
cover.SignalAdd(corpusSignal, diff)
cover.SignalAdd(maxSignal, diff)
}
}
+
+func (fuzzer *Fuzzer) addInputToCorpus(p *prog.Prog, sig hash.Sig) {
+ fuzzer.corpusMu.Lock()
+ defer fuzzer.corpusMu.Unlock()
+ if _, ok := fuzzer.corpusHashes[sig]; !ok {
+ fuzzer.corpus = append(fuzzer.corpus, p)
+ fuzzer.corpusHashes[sig] = struct{}{}
+ }
+}
+
+func (fuzzer *Fuzzer) corpusSnapshot() []*prog.Prog {
+ fuzzer.corpusMu.RLock()
+ defer fuzzer.corpusMu.RUnlock()
+ return fuzzer.corpus
+}
diff --git a/syz-fuzzer/proc.go b/syz-fuzzer/proc.go
index 9d4b86f8e..70e86c130 100644
--- a/syz-fuzzer/proc.go
+++ b/syz-fuzzer/proc.go
@@ -71,18 +71,15 @@ func (proc *Proc) loop() {
continue
}
- corpusMu.RLock()
+ corpus := proc.fuzzer.corpusSnapshot()
if len(corpus) == 0 || i%100 == 0 {
// Generate a new prog.
- corpusMu.RUnlock()
p := target.Generate(proc.rnd, programLength, ct)
Logf(1, "#%v: generated", pid)
proc.execute(execOpts, p, false, false, false, false, StatGenerate)
} else {
// Mutate an existing prog.
p := corpus[proc.rnd.Intn(len(corpus))].Clone()
- corpusMu.RUnlock()
- // TODO: it seems that access to corpus is not proceted here.
p.Mutate(proc.rnd, programLength, ct, corpus)
Logf(1, "#%v: mutated", pid)
proc.execute(execOpts, p, false, false, false, false, StatFuzz)
@@ -107,10 +104,8 @@ func (proc *Proc) triageInput(item *WorkTriage) {
newSignal = cover.Canonicalize(newSignal)
call := item.p.Calls[item.call].Meta
- data := item.p.Serialize()
- sig := hash.Hash(data)
- Logf(3, "triaging input for %v (new signal=%v):\n%s", call.CallName, len(newSignal), data)
+ Logf(3, "triaging input for %v (new signal=%v)", call.CallName, len(newSignal))
var inputCover cover.Cover
opts := *execOpts
opts.Flags |= ipc.FlagCollectCover
@@ -166,6 +161,9 @@ func (proc *Proc) triageInput(item *WorkTriage) {
}, false)
}
+ data := item.p.Serialize()
+ sig := hash.Hash(data)
+
Logf(2, "added new input for %v to corpus:\n%s", call.CallName, data)
a := &NewInputArgs{
Name: *flagName,
@@ -184,12 +182,7 @@ func (proc *Proc) triageInput(item *WorkTriage) {
cover.SignalAdd(corpusSignal, item.signal)
signalMu.Unlock()
- corpusMu.Lock()
- if _, ok := corpusHashes[sig]; !ok {
- corpus = append(corpus, item.p)
- corpusHashes[sig] = struct{}{}
- }
- corpusMu.Unlock()
+ proc.fuzzer.addInputToCorpus(item.p, sig)
if !item.minimized {
proc.fuzzer.workQueue.enqueue(&WorkSmash{item.p, item.call})
@@ -200,9 +193,9 @@ func (proc *Proc) smashInput(item *WorkSmash) {
if faultInjectionEnabled {
proc.failCall(item.p, item.call)
}
+ corpus := proc.fuzzer.corpusSnapshot()
for i := 0; i < 100; i++ {
p := item.p.Clone()
- // TODO: it seems that access to corpus is not proceted here.
p.Mutate(proc.rnd, programLength, proc.fuzzer.choiceTable, corpus)
Logf(1, "#%v: smash mutated", proc.pid)
proc.execute(proc.fuzzer.execOpts, p, false, false, false, false, StatSmash)
@@ -287,13 +280,6 @@ func (proc *Proc) execute(execOpts *ipc.ExecOpts, p *prog.Prog,
var logMu sync.Mutex
func (proc *Proc) executeRaw(opts *ipc.ExecOpts, p *prog.Prog, stat Stat) []ipc.CallInfo {
- if false {
- // For debugging, this function must not be executed with locks held.
- corpusMu.Lock()
- corpusMu.Unlock()
- signalMu.Lock()
- signalMu.Unlock()
- }
pid := proc.pid
if opts.Flags&ipc.FlagDedupCover == 0 {
panic("dedup cover is not enabled")