diff options
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/corpus/corpus.go | 20 | ||||
| -rw-r--r-- | pkg/cover/canonicalizer.go | 38 | ||||
| -rw-r--r-- | pkg/cover/canonicalizer_test.go | 46 | ||||
| -rw-r--r-- | pkg/fuzzer/cover.go | 6 | ||||
| -rw-r--r-- | pkg/fuzzer/fuzzer.go | 77 | ||||
| -rw-r--r-- | pkg/fuzzer/fuzzer_test.go | 7 | ||||
| -rw-r--r-- | pkg/fuzzer/job.go | 29 | ||||
| -rw-r--r-- | pkg/fuzzer/stats.go | 26 | ||||
| -rw-r--r-- | pkg/rpctype/rpctype.go | 72 | ||||
| -rw-r--r-- | pkg/signal/signal.go | 71 |
10 files changed, 165 insertions, 227 deletions
diff --git a/pkg/corpus/corpus.go b/pkg/corpus/corpus.go index 772037178..8eed1fc63 100644 --- a/pkg/corpus/corpus.go +++ b/pkg/corpus/corpus.go @@ -9,7 +9,6 @@ import ( "github.com/google/syzkaller/pkg/cover" "github.com/google/syzkaller/pkg/hash" - "github.com/google/syzkaller/pkg/rpctype" "github.com/google/syzkaller/pkg/signal" "github.com/google/syzkaller/prog" ) @@ -63,15 +62,6 @@ func (item Item) StringCall() string { return stringCall(item.Prog, item.Call) } -// RPCInputShort() does not include coverage. -func (item Item) RPCInputShort() rpctype.Input { - return rpctype.Input{ - Call: item.Call, - Prog: item.ProgData, - Signal: item.Signal.Serialize(), - } -} - func stringCall(p *prog.Prog, call int) string { if call != -1 { return p.Calls[call].Meta.Name @@ -91,16 +81,6 @@ func (item NewInput) StringCall() string { return stringCall(item.Prog, item.Call) } -func (item NewInput) RPCInput() rpctype.Input { - return rpctype.Input{ - Call: item.Call, - Prog: item.Prog.Serialize(), - Signal: item.Signal.Serialize(), - Cover: item.Cover, - RawCover: item.RawCover, - } -} - type NewItemEvent struct { Sig string Exists bool diff --git a/pkg/cover/canonicalizer.go b/pkg/cover/canonicalizer.go index c7c385aed..d3b014af8 100644 --- a/pkg/cover/canonicalizer.go +++ b/pkg/cover/canonicalizer.go @@ -9,7 +9,6 @@ import ( "github.com/google/syzkaller/pkg/host" "github.com/google/syzkaller/pkg/log" - "github.com/google/syzkaller/pkg/signal" ) type Canonicalizer struct { @@ -120,18 +119,18 @@ func (can *Canonicalizer) NewInstance(modules []host.KernelModule) *Canonicalize } } -func (ci *CanonicalizerInstance) Canonicalize(cov []uint32, sign signal.Serial) ([]uint32, signal.Serial) { +func (ci *CanonicalizerInstance) Canonicalize(elems []uint32) []uint32 { if ci.canonical.moduleKeys == nil { - return cov, sign + return elems } - return ci.canonicalize.convertPCs(cov, sign) + return ci.canonicalize.convertPCs(elems) } -func (ci *CanonicalizerInstance) Decanonicalize(cov []uint32, sign signal.Serial) ([]uint32, signal.Serial) { +func (ci *CanonicalizerInstance) Decanonicalize(elems []uint32) []uint32 { if ci.canonical.moduleKeys == nil { - return cov, sign + return elems } - return ci.decanonicalize.convertPCs(cov, sign) + return ci.decanonicalize.convertPCs(elems) } func (ci *CanonicalizerInstance) DecanonicalizeFilter(bitmap map[uint32]uint32) map[uint32]uint32 { @@ -177,34 +176,21 @@ func findModule(pc uint32, moduleKeys []uint32) (moduleIdx int) { return moduleIdx - 1 } -func (convert *Convert) convertPCs(cov []uint32, sign signal.Serial) ([]uint32, signal.Serial) { +func (convert *Convert) convertPCs(pcs []uint32) []uint32 { // Convert coverage. - var retCov []uint32 + var ret []uint32 convCtx := &convertContext{convert: convert} - for _, pc := range cov { + for _, pc := range pcs { if newPC, ok := convert.convertPC(pc); ok { - retCov = append(retCov, newPC) + ret = append(ret, newPC) } else { convCtx.discard(pc) } } if msg := convCtx.discarded(); msg != "" { - log.Logf(4, "error in PC conversion: %v", msg) - } - // Convert signals. - retSign := &signal.Serial{} - convCtx = &convertContext{convert: convert} - for idx, elem := range sign.Elems { - if newSign, ok := convert.convertPC(uint32(elem)); ok { - retSign.AddElem(newSign, sign.Prios[idx]) - } else { - convCtx.discard(uint32(elem)) - } - } - if msg := convCtx.discarded(); msg != "" { - log.Logf(4, "error in signal conversion: %v", msg) + log.Logf(4, "error in PC/signal conversion: %v", msg) } - return retCov, *retSign + return ret } func (convert *Convert) convertPC(pc uint32) (uint32, bool) { diff --git a/pkg/cover/canonicalizer_test.go b/pkg/cover/canonicalizer_test.go index db418abe1..aa840a96f 100644 --- a/pkg/cover/canonicalizer_test.go +++ b/pkg/cover/canonicalizer_test.go @@ -13,7 +13,6 @@ import ( "github.com/google/syzkaller/pkg/cover" "github.com/google/syzkaller/pkg/host" - "github.com/google/syzkaller/pkg/signal" ) type RPCServer struct { @@ -28,8 +27,8 @@ type Fuzzer struct { goalCov []uint32 bitmap map[uint32]uint32 goalBitmap map[uint32]uint32 - sign signal.Serial - goalSign signal.Serial + sign []uint32 + goalSign []uint32 } type canonicalizeValue int @@ -49,13 +48,9 @@ func TestNilModules(t *testing.T) { serv.fuzzers["f1"].cov = []uint32{0x00010000, 0x00020000, 0x00030000, 0x00040000} serv.fuzzers["f1"].goalCov = []uint32{0x00010000, 0x00020000, 0x00030000, 0x00040000} - serv.fuzzers["f1"].sign = signal.FromRaw(serv.fuzzers["f1"].cov, 0).Serialize() - serv.fuzzers["f1"].goalSign = signal.FromRaw(serv.fuzzers["f1"].goalCov, 0).Serialize() serv.fuzzers["f2"].cov = []uint32{0x00010000, 0x00020000, 0x00030000, 0x00040000} serv.fuzzers["f2"].goalCov = []uint32{0x00010000, 0x00020000, 0x00030000, 0x00040000} - serv.fuzzers["f2"].sign = signal.FromRaw(serv.fuzzers["f2"].cov, 0).Serialize() - serv.fuzzers["f2"].goalSign = signal.FromRaw(serv.fuzzers["f2"].goalCov, 0).Serialize() serv.fuzzers["f1"].bitmap = map[uint32]uint32{ 0x00010011: 1, @@ -87,15 +82,15 @@ func TestNilModules(t *testing.T) { } serv.fuzzers["f1"].goalCov = []uint32{0x00010000, 0x00020000, 0x00030000, 0x00040000} - serv.fuzzers["f1"].goalSign = signal.FromRaw(serv.fuzzers["f1"].goalCov, 0).Serialize() + serv.fuzzers["f1"].goalSign = serv.fuzzers["f1"].goalCov serv.fuzzers["f2"].goalCov = []uint32{0x00010000, 0x00020000, 0x00030000, 0x00040000} - serv.fuzzers["f2"].goalSign = signal.FromRaw(serv.fuzzers["f2"].goalCov, 0).Serialize() + serv.fuzzers["f2"].goalSign = serv.fuzzers["f2"].goalCov if err := serv.runTest(Decanonicalize); err != "" { t.Fatalf("failed in decanonicalization: %v", err) } } -// Confirms there is no change to signals if coverage is disabled and fallback signals are used. +// Confirms there is no change to PCs if coverage is disabled and fallback signals are used. func TestDisabledSignals(t *testing.T) { serv := &RPCServer{ fuzzers: make(map[string]*Fuzzer), @@ -112,18 +107,18 @@ func TestDisabledSignals(t *testing.T) { serv.connect("f2", f2Modules, false) pcs := []uint32{0x00010000, 0x00020000, 0x00030000, 0x00040000} - serv.fuzzers["f1"].sign = signal.FromRaw(pcs, 0).Serialize() - serv.fuzzers["f1"].goalSign = signal.FromRaw(pcs, 0).Serialize() + serv.fuzzers["f1"].cov = pcs + serv.fuzzers["f1"].goalCov = pcs - serv.fuzzers["f2"].sign = signal.FromRaw(pcs, 0).Serialize() - serv.fuzzers["f2"].goalSign = signal.FromRaw(pcs, 0).Serialize() + serv.fuzzers["f2"].sign = pcs + serv.fuzzers["f2"].goalSign = pcs if err := serv.runTest(Canonicalize); err != "" { t.Fatalf("failed in canonicalization: %v", err) } - serv.fuzzers["f1"].goalSign = signal.FromRaw(pcs, 0).Serialize() - serv.fuzzers["f2"].goalSign = signal.FromRaw(pcs, 0).Serialize() + serv.fuzzers["f1"].goalSign = pcs + serv.fuzzers["f2"].goalSign = pcs if err := serv.runTest(Decanonicalize); err != "" { t.Fatalf("failed in decanonicalization: %v", err) } @@ -152,8 +147,6 @@ func TestModules(t *testing.T) { 0x00035000, 0x00040000, 0x00045000, 0x00050000, 0x00055000} serv.fuzzers["f1"].goalCov = []uint32{0x00010000, 0x00015000, 0x00020000, 0x00025000, 0x00030000, 0x00035000, 0x00040000, 0x00045000, 0x00050000, 0x00055000} - serv.fuzzers["f1"].sign = signal.FromRaw(serv.fuzzers["f1"].cov, 0).Serialize() - serv.fuzzers["f1"].goalSign = signal.FromRaw(serv.fuzzers["f1"].goalCov, 0).Serialize() // The modules addresss are inverted between: (2 and 4), (3 and 5), // affecting the output canonical coverage values in these ranges. @@ -161,8 +154,6 @@ func TestModules(t *testing.T) { 0x00035000, 0x00040000, 0x00045000, 0x00050000, 0x00055000} serv.fuzzers["f2"].goalCov = []uint32{0x00010000, 0x00015000, 0x00040000, 0x00025000, 0x00045000, 0x0004a000, 0x00020000, 0x00030000, 0x0003b000, 0x00055000} - serv.fuzzers["f2"].sign = signal.FromRaw(serv.fuzzers["f2"].cov, 0).Serialize() - serv.fuzzers["f2"].goalSign = signal.FromRaw(serv.fuzzers["f2"].goalCov, 0).Serialize() serv.fuzzers["f1"].bitmap = map[uint32]uint32{ 0x00010011: 1, @@ -195,10 +186,8 @@ func TestModules(t *testing.T) { serv.fuzzers["f1"].goalCov = []uint32{0x00010000, 0x00015000, 0x00020000, 0x00025000, 0x00030000, 0x00035000, 0x00040000, 0x00045000, 0x00050000, 0x00055000} - serv.fuzzers["f1"].goalSign = signal.FromRaw(serv.fuzzers["f1"].goalCov, 0).Serialize() serv.fuzzers["f2"].goalCov = []uint32{0x00010000, 0x00015000, 0x00020000, 0x00025000, 0x00030000, 0x00035000, 0x00040000, 0x00045000, 0x00050000, 0x00055000} - serv.fuzzers["f2"].goalSign = signal.FromRaw(serv.fuzzers["f2"].goalCov, 0).Serialize() if err := serv.runTest(Decanonicalize); err != "" { t.Fatalf("failed in decanonicalization: %v", err) } @@ -225,15 +214,12 @@ func TestChangingModules(t *testing.T) { // in this range should be deleted. serv.fuzzers["f2"].cov = []uint32{0x00010000, 0x00015000, 0x00020000, 0x00025000} serv.fuzzers["f2"].goalCov = []uint32{0x00010000, 0x00015000, 0x00025000} - serv.fuzzers["f2"].sign = signal.FromRaw(serv.fuzzers["f2"].cov, 0).Serialize() - serv.fuzzers["f2"].goalSign = signal.FromRaw(serv.fuzzers["f2"].goalCov, 0).Serialize() if err := serv.runTest(Canonicalize); err != "" { t.Fatalf("failed in canonicalization: %v", err) } serv.fuzzers["f2"].goalCov = []uint32{0x00010000, 0x00015000, 0x00025000} - serv.fuzzers["f2"].goalSign = signal.FromRaw(serv.fuzzers["f2"].goalCov, 0).Serialize() if err := serv.runTest(Decanonicalize); err != "" { t.Fatalf("failed in decanonicalization: %v", err) } @@ -241,12 +227,11 @@ func TestChangingModules(t *testing.T) { func (serv *RPCServer) runTest(val canonicalizeValue) string { var cov []uint32 - var sign signal.Serial for name, fuzzer := range serv.fuzzers { if val == Canonicalize { - cov, sign = fuzzer.instModules.Canonicalize(fuzzer.cov, fuzzer.sign) + cov = fuzzer.instModules.Canonicalize(fuzzer.cov) } else { - cov, sign = fuzzer.instModules.Decanonicalize(fuzzer.cov, fuzzer.sign) + cov = fuzzer.instModules.Decanonicalize(fuzzer.cov) instBitmap := fuzzer.instModules.DecanonicalizeFilter(fuzzer.bitmap) if !reflect.DeepEqual(instBitmap, fuzzer.goalBitmap) { return fmt.Sprintf("failed in bitmap conversion. Fuzzer %v.\nExpected: 0x%x.\nReturned: 0x%x", @@ -257,12 +242,7 @@ func (serv *RPCServer) runTest(val canonicalizeValue) string { return fmt.Sprintf("failed in coverage conversion. Fuzzer %v.\nExpected: 0x%x.\nReturned: 0x%x", name, fuzzer.goalCov, cov) } - if !reflect.DeepEqual(sign.Deserialize(), fuzzer.goalSign.Deserialize()) { - return fmt.Sprintf("failed in signal conversion. Fuzzer %v.\nExpected: 0x%x.\nReturned: 0x%x", - name, fuzzer.goalSign, sign) - } fuzzer.cov = cov - fuzzer.sign = sign } return "" } diff --git a/pkg/fuzzer/cover.go b/pkg/fuzzer/cover.go index 886da004d..5af00f167 100644 --- a/pkg/fuzzer/cover.go +++ b/pkg/fuzzer/cover.go @@ -35,6 +35,12 @@ func (cover *Cover) addRawMaxSignal(signal []uint32, prio uint8) signal.Signal { return diff } +func (cover *Cover) CopyMaxSignal() signal.Signal { + cover.mu.RLock() + defer cover.mu.RUnlock() + return cover.maxSignal.Copy() +} + func (cover *Cover) GrabNewSignal() signal.Signal { cover.mu.Lock() defer cover.mu.Unlock() diff --git a/pkg/fuzzer/fuzzer.go b/pkg/fuzzer/fuzzer.go index 196e59506..14c3f5902 100644 --- a/pkg/fuzzer/fuzzer.go +++ b/pkg/fuzzer/fuzzer.go @@ -13,15 +13,15 @@ import ( "time" "github.com/google/syzkaller/pkg/corpus" - "github.com/google/syzkaller/pkg/hash" "github.com/google/syzkaller/pkg/ipc" + "github.com/google/syzkaller/pkg/rpctype" + "github.com/google/syzkaller/pkg/signal" "github.com/google/syzkaller/prog" ) type Fuzzer struct { - Config *Config - Cover *Cover - NeedCandidates chan struct{} + Config *Config + Cover *Cover ctx context.Context mu sync.Mutex @@ -39,18 +39,16 @@ type Fuzzer struct { runningJobs atomic.Int64 queuedCandidates atomic.Int64 - // If the source of candidates runs out of them, we risk - // generating too many needCandidate requests (one for - // each Config.MinCandidates). We prevent this with candidatesRequested. - candidatesRequested atomic.Bool + + outOfQueue atomic.Bool + outOfQueueNext atomic.Int64 } func NewFuzzer(ctx context.Context, cfg *Config, rnd *rand.Rand, target *prog.Target) *Fuzzer { f := &Fuzzer{ - Config: cfg, - Cover: &Cover{}, - NeedCandidates: make(chan struct{}, 1), + Config: cfg, + Cover: &Cover{}, ctx: ctx, stats: map[string]uint64{}, @@ -82,18 +80,16 @@ type Config struct { EnabledCalls map[*prog.Syscall]bool NoMutateCalls map[int]bool FetchRawCover bool - // If the number of queued candidates is less than MinCandidates, - // NeedCandidates is triggered. - MinCandidates uint - NewInputs chan corpus.NewInput + NewInputFilter func(input *corpus.NewInput) bool } type Request struct { Prog *prog.Prog NeedCover bool NeedRawCover bool - NeedSignal bool + NeedSignal rpctype.SignalType NeedHints bool + SignalFilter signal.Signal // If specified, the resulting signal MAY be a subset of it. // Fields that are only relevant within pkg/fuzzer. flags ProgTypes stat string @@ -110,7 +106,7 @@ func (fuzzer *Fuzzer) Done(req *Request, res *Result) { // Triage individual calls. // We do it before unblocking the waiting threads because // it may result it concurrent modification of req.Prog. - if req.NeedSignal && res.Info != nil { + if req.NeedSignal != rpctype.NoSignal && res.Info != nil { for call, info := range res.Info.Calls { fuzzer.triageProgCall(req.Prog, &info, call, req.flags) } @@ -166,7 +162,6 @@ func signalPrio(p *prog.Prog, info *ipc.CallInfo, call int) (prio uint8) { type Candidate struct { Prog *prog.Prog - Hash hash.Sig Smashed bool Minimized bool } @@ -178,20 +173,19 @@ func (fuzzer *Fuzzer) NextInput() *Request { panic("queuedCandidates is out of sync") } } - if fuzzer.NeedCandidatesNow() && - !fuzzer.candidatesRequested.CompareAndSwap(false, true) { - select { - case fuzzer.NeedCandidates <- struct{}{}: - default: - } - } return req } func (fuzzer *Fuzzer) nextInput() *Request { - nextExec := fuzzer.nextExec.tryPop() - if nextExec != nil { - return nextExec.value + // The fuzzer may get biased to one specific part of the kernel. + // Periodically generate random programs to ensure that the coverage + // is more uniform. + if !fuzzer.outOfQueue.Load() || + fuzzer.outOfQueueNext.Add(1)%400 > 0 { + nextExec := fuzzer.nextExec.tryPop() + if nextExec != nil { + return nextExec.value + } } // Either generate a new input or mutate an existing one. mutateRate := 0.95 @@ -227,16 +221,15 @@ func (fuzzer *Fuzzer) Logf(level int, msg string, args ...interface{}) { fuzzer.Config.Logf(level, msg, args...) } -func (fuzzer *Fuzzer) NeedCandidatesNow() bool { - return fuzzer.queuedCandidates.Load() < int64(fuzzer.Config.MinCandidates) -} - func (fuzzer *Fuzzer) AddCandidates(candidates []Candidate) { fuzzer.queuedCandidates.Add(int64(len(candidates))) for _, candidate := range candidates { fuzzer.pushExec(candidateRequest(candidate), priority{candidatePrio}) } - fuzzer.candidatesRequested.Store(false) +} + +func (fuzzer *Fuzzer) EnableOutOfQueue() { + fuzzer.outOfQueue.Store(true) } func (fuzzer *Fuzzer) rand() *rand.Rand { @@ -250,7 +243,7 @@ func (fuzzer *Fuzzer) pushExec(req *Request, prio priority) { if req.stat == "" { panic("Request.Stat field must be set") } - if req.NeedHints && (req.NeedCover || req.NeedSignal) { + if req.NeedHints && (req.NeedCover || req.NeedSignal != rpctype.NoSignal) { panic("Request.NeedHints is mutually exclusive with other fields") } fuzzer.nextExec.push(&priorityQueueItem[*Request]{ @@ -330,19 +323,3 @@ func (fuzzer *Fuzzer) logCurrentStats() { fuzzer.Logf(0, "%s", str) } } - -type Stats struct { - CoverStats - corpus.Stats - Candidates int - RunningJobs int -} - -func (fuzzer *Fuzzer) Stats() Stats { - return Stats{ - CoverStats: fuzzer.Cover.Stats(), - Stats: fuzzer.Config.Corpus.Stats(), - Candidates: int(fuzzer.queuedCandidates.Load()), - RunningJobs: int(fuzzer.runningJobs.Load()), - } -} diff --git a/pkg/fuzzer/fuzzer_test.go b/pkg/fuzzer/fuzzer_test.go index 4f8cf41c5..bd6d9a8fe 100644 --- a/pkg/fuzzer/fuzzer_test.go +++ b/pkg/fuzzer/fuzzer_test.go @@ -22,6 +22,7 @@ import ( "github.com/google/syzkaller/pkg/csource" "github.com/google/syzkaller/pkg/ipc" "github.com/google/syzkaller/pkg/ipc/ipcconfig" + "github.com/google/syzkaller/pkg/rpctype" "github.com/google/syzkaller/pkg/testutil" "github.com/google/syzkaller/prog" "github.com/google/syzkaller/sys/targets" @@ -54,7 +55,6 @@ func TestFuzz(t *testing.T) { EnabledCalls: map[*prog.Syscall]bool{ target.SyscallMap["syz_test_fuzzer1"]: true, }, - NewInputs: make(chan corpus.NewInput), }, rand.New(testutil.RandSource(t)), target) go func() { @@ -129,7 +129,7 @@ func emulateExec(req *Request) (*Result, string, error) { if req.NeedCover { callInfo.Cover = []uint32{cover} } - if req.NeedSignal { + if req.NeedSignal != rpctype.NoSignal { callInfo.Signal = []uint32{cover} } info.Calls = append(info.Calls, callInfo) @@ -205,7 +205,6 @@ func (f *testFuzzer) wait() { for title, cnt := range f.crashes { t.Logf("%s: %d", title, cnt) } - t.Logf("stats:\n%v", f.fuzzer.GrabStats()) } // TODO: it's already implemented in syz-fuzzer/proc.go, @@ -239,7 +238,7 @@ var crashRe = regexp.MustCompile(`{{CRASH: (.*?)}}`) func (proc *executorProc) execute(req *Request) (*Result, string, error) { execOpts := proc.execOpts // TODO: it's duplicated from fuzzer.go. - if req.NeedSignal { + if req.NeedSignal != rpctype.NoSignal { execOpts.Flags |= ipc.FlagCollectSignal } if req.NeedCover { diff --git a/pkg/fuzzer/job.go b/pkg/fuzzer/job.go index dfc0b807e..f567fc2cc 100644 --- a/pkg/fuzzer/job.go +++ b/pkg/fuzzer/job.go @@ -10,6 +10,7 @@ import ( "github.com/google/syzkaller/pkg/corpus" "github.com/google/syzkaller/pkg/cover" "github.com/google/syzkaller/pkg/ipc" + "github.com/google/syzkaller/pkg/rpctype" "github.com/google/syzkaller/pkg/signal" "github.com/google/syzkaller/prog" ) @@ -64,7 +65,7 @@ func genProgRequest(fuzzer *Fuzzer, rnd *rand.Rand) *Request { fuzzer.ChoiceTable()) return &Request{ Prog: p, - NeedSignal: true, + NeedSignal: rpctype.NewSignal, stat: statGenerate, } } @@ -83,7 +84,7 @@ func mutateProgRequest(fuzzer *Fuzzer, rnd *rand.Rand) *Request { ) return &Request{ Prog: newP, - NeedSignal: true, + NeedSignal: rpctype.NewSignal, stat: statFuzz, } } @@ -98,7 +99,7 @@ func candidateRequest(input Candidate) *Request { } return &Request{ Prog: input.Prog, - NeedSignal: true, + NeedSignal: rpctype.NewSignal, stat: statCandidate, flags: flags, } @@ -157,13 +158,10 @@ func (job *triageJob) run(fuzzer *Fuzzer) { Cover: info.cover.Serialize(), RawCover: info.rawCover, } - fuzzer.Config.Corpus.Save(input) - if fuzzer.Config.NewInputs != nil { - select { - case <-fuzzer.ctx.Done(): - case fuzzer.Config.NewInputs <- input: - } + if filter := fuzzer.Config.NewInputFilter; filter != nil && !filter(&input) { + return } + fuzzer.Config.Corpus.Save(input) } type deflakedCover struct { @@ -179,7 +177,7 @@ func (job *triageJob) deflake(fuzzer *Fuzzer) (info deflakedCover, stop bool) { for i := 0; i < signalRuns; i++ { result := fuzzer.exec(job, &Request{ Prog: job.p, - NeedSignal: true, + NeedSignal: rpctype.AllSignal, NeedCover: true, NeedRawCover: fuzzer.Config.FetchRawCover, stat: statTriage, @@ -226,9 +224,10 @@ func (job *triageJob) minimize(fuzzer *Fuzzer, newSignal signal.Signal) (stop bo } for i := 0; i < minimizeAttempts; i++ { result := fuzzer.exec(job, &Request{ - Prog: p1, - NeedSignal: true, - stat: statMinimize, + Prog: p1, + NeedSignal: rpctype.AllSignal, + SignalFilter: newSignal, + stat: statMinimize, }) if result.Stop { stop = true @@ -298,7 +297,7 @@ func (job *smashJob) run(fuzzer *Fuzzer) { fuzzer.Config.Corpus.Programs()) result := fuzzer.exec(job, &Request{ Prog: p, - NeedSignal: true, + NeedSignal: rpctype.NewSignal, stat: statSmash, }) if result.Stop { @@ -386,7 +385,7 @@ func (job *hintsJob) run(fuzzer *Fuzzer) { func(p *prog.Prog) bool { result := fuzzer.exec(job, &Request{ Prog: p, - NeedSignal: true, + NeedSignal: rpctype.NewSignal, stat: statHint, }) return !result.Stop diff --git a/pkg/fuzzer/stats.go b/pkg/fuzzer/stats.go index 17bc6131c..044febc64 100644 --- a/pkg/fuzzer/stats.go +++ b/pkg/fuzzer/stats.go @@ -3,6 +3,8 @@ package fuzzer +import "github.com/google/syzkaller/pkg/corpus" + const ( statGenerate = "exec gen" statFuzz = "exec fuzz" @@ -17,10 +19,28 @@ const ( statBufferTooSmall = "buffer too small" ) -func (fuzzer *Fuzzer) GrabStats() map[string]uint64 { +type Stats struct { + CoverStats + corpus.Stats + Candidates int + RunningJobs int + // Let's keep stats in Named as long as the rest of the code does not depend + // on their specific values. + Named map[string]uint64 +} + +func (fuzzer *Fuzzer) Stats() Stats { + ret := Stats{ + CoverStats: fuzzer.Cover.Stats(), + Stats: fuzzer.Config.Corpus.Stats(), + Candidates: int(fuzzer.queuedCandidates.Load()), + RunningJobs: int(fuzzer.runningJobs.Load()), + Named: make(map[string]uint64), + } fuzzer.mu.Lock() defer fuzzer.mu.Unlock() - ret := fuzzer.stats - fuzzer.stats = map[string]uint64{} + for k, v := range fuzzer.stats { + ret.Named[k] = v + } return ret } diff --git a/pkg/rpctype/rpctype.go b/pkg/rpctype/rpctype.go index efd9d6589..9f0b6846e 100644 --- a/pkg/rpctype/rpctype.go +++ b/pkg/rpctype/rpctype.go @@ -13,20 +13,47 @@ import ( "github.com/google/syzkaller/pkg/signal" ) -type Input struct { - Call int // seq number of call in the prog to which the item is related (-1 for extra) - Prog []byte - Signal signal.Serial - Cover []uint32 - RawCover []uint32 +type SignalType int + +const ( + NoSignal SignalType = 0 // we don't need any signal + NewSignal SignalType = 1 // we need the newly seen signal + AllSignal SignalType = 2 // we need all signal +) + +// ExecutionRequest describes the task of executing a particular program. +// Corresponds to Fuzzer.Request. +type ExecutionRequest struct { + ID int64 + ProgData []byte + NeedCover bool + NeedRawCover bool + NeedHints bool + NeedSignal SignalType + SignalFilter signal.Signal +} + +// ExecutionResult is sent after ExecutionRequest is completed. +type ExecutionResult struct { + ID int64 + Info ipc.ProgInfo +} + +// ExchangeInfoRequest is periodically sent by syz-fuzzer to syz-manager. +type ExchangeInfoRequest struct { + Name string + NeedProgs int + StatsDelta map[string]uint64 + Results []ExecutionResult } -type Candidate struct { - Prog []byte - Minimized bool - Smashed bool +// ExchangeInfoReply is a reply to ExchangeInfoRequest. +type ExchangeInfoReply struct { + Requests []ExecutionRequest + NewMaxSignal []uint32 } +// TODO: merge ExecutionRequest and ExecTask. type ExecTask struct { Prog []byte ID int64 @@ -40,7 +67,6 @@ type ConnectArgs struct { type ConnectRes struct { EnabledCalls []int - NoMutateCalls map[int]bool GitRevision string TargetRevision string AllSandboxes bool @@ -64,24 +90,6 @@ type SyscallReason struct { Reason string } -type NewInputArgs struct { - Name string - Input -} - -type PollArgs struct { - Name string - NeedCandidates bool - MaxSignal signal.Serial - Stats map[string]uint64 -} - -type PollRes struct { - Candidates []Candidate - NewInputs []Input - MaxSignal signal.Serial -} - type RunnerConnectArgs struct { Pool, VM int } @@ -199,9 +207,3 @@ type RunTestDoneArgs struct { Info []*ipc.ProgInfo Error string } - -type LogMessageReq struct { - Level int - Name string - Message string -} diff --git a/pkg/signal/signal.go b/pkg/signal/signal.go index 7a2a8bd16..2860be95e 100644 --- a/pkg/signal/signal.go +++ b/pkg/signal/signal.go @@ -11,11 +11,6 @@ type ( type Signal map[elemType]prioType -type Serial struct { - Elems []elemType - Prios []prioType -} - func (s Signal) Len() int { return len(s) } @@ -64,42 +59,6 @@ func FromRaw(raw []uint32, prio uint8) Signal { return s } -func (s Signal) Serialize() Serial { - if s.Empty() { - return Serial{} - } - res := Serial{ - Elems: make([]elemType, len(s)), - Prios: make([]prioType, len(s)), - } - i := 0 - for e, p := range s { - res.Elems[i] = e - res.Prios[i] = p - i++ - } - return res -} - -func (ser *Serial) AddElem(elem uint32, prio prioType) { - ser.Elems = append(ser.Elems, elemType(elem)) - ser.Prios = append(ser.Prios, prio) -} - -func (ser Serial) Deserialize() Signal { - if len(ser.Elems) != len(ser.Prios) { - panic("corrupted Serial") - } - if len(ser.Elems) == 0 { - return nil - } - s := make(Signal, len(ser.Elems)) - for i, e := range ser.Elems { - s[e] = ser.Prios[i] - } - return s -} - func (s Signal) Diff(s1 Signal) Signal { if s1.Empty() { return nil @@ -160,6 +119,36 @@ func (s *Signal) Merge(s1 Signal) { } } +// FilterRaw returns a subset of original raw elements that coincides with the one in Signal. +func (s Signal) FilterRaw(raw []uint32) []uint32 { + var ret []uint32 + for _, e := range raw { + if _, ok := s[elemType(e)]; ok { + ret = append(ret, e) + } + } + return ret +} + +// DiffFromRaw returns a subset of the raw elements that is not present in Signal. +func (s Signal) DiffFromRaw(raw []uint32) []uint32 { + var ret []uint32 + for _, e := range raw { + if _, ok := s[elemType(e)]; !ok { + ret = append(ret, e) + } + } + return ret +} + +func (s Signal) ToRaw() []uint32 { + var raw []uint32 + for e := range s { + raw = append(raw, uint32(e)) + } + return raw +} + type Context struct { Signal Signal Context interface{} |
