From 701ad974ccc0ba0a61d2a2bd58db569f794bd037 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Tue, 28 May 2024 16:17:13 +0200 Subject: pkg/fuzzer: refactor progTypes The next commit will add another Candidate flag. Candidate flags duplicate progTypes enum, so to avoid conversions of one to another use progTypes in Candidate struct directly. Rename progTypes to progFlags since multiple can be set, so this is effectively flags rather than a single type. --- pkg/fuzzer/fuzzer.go | 34 ++++++++++++++++++++++++---------- pkg/fuzzer/job.go | 35 +++++------------------------------ pkg/fuzzer/job_test.go | 4 ++-- 3 files changed, 31 insertions(+), 42 deletions(-) (limited to 'pkg/fuzzer') diff --git a/pkg/fuzzer/fuzzer.go b/pkg/fuzzer/fuzzer.go index 8be912139..b0ceaf9c0 100644 --- a/pkg/fuzzer/fuzzer.go +++ b/pkg/fuzzer/fuzzer.go @@ -100,26 +100,26 @@ func (fuzzer *Fuzzer) execute(executor queue.Executor, req *queue.Request) *queu return fuzzer.executeWithFlags(executor, req, 0) } -func (fuzzer *Fuzzer) executeWithFlags(executor queue.Executor, req *queue.Request, flags ProgTypes) *queue.Result { +func (fuzzer *Fuzzer) executeWithFlags(executor queue.Executor, req *queue.Request, flags ProgFlags) *queue.Result { executor.Submit(req) res := req.Wait(fuzzer.ctx) fuzzer.processResult(req, res, flags) return res } -func (fuzzer *Fuzzer) prepare(req *queue.Request, flags ProgTypes) { +func (fuzzer *Fuzzer) prepare(req *queue.Request, flags ProgFlags) { req.OnDone(func(req *queue.Request, res *queue.Result) bool { fuzzer.processResult(req, res, flags) return true }) } -func (fuzzer *Fuzzer) enqueue(executor queue.Executor, req *queue.Request, flags ProgTypes) { +func (fuzzer *Fuzzer) enqueue(executor queue.Executor, req *queue.Request, flags ProgFlags) { fuzzer.prepare(req, flags) executor.Submit(req) } -func (fuzzer *Fuzzer) processResult(req *queue.Request, res *queue.Result, flags ProgTypes) { +func (fuzzer *Fuzzer) processResult(req *queue.Request, res *queue.Result, flags ProgFlags) { inTriage := flags&progInTriage > 0 // Triage individual calls. // We do it before unblocking the waiting threads because @@ -154,7 +154,7 @@ type Config struct { NewInputFilter func(call string) bool } -func (fuzzer *Fuzzer) triageProgCall(p *prog.Prog, info *flatrpc.CallInfo, call int, flags ProgTypes) { +func (fuzzer *Fuzzer) triageProgCall(p *prog.Prog, info *flatrpc.CallInfo, call int, flags ProgFlags) { if info == nil { return } @@ -243,17 +243,31 @@ func (fuzzer *Fuzzer) Logf(level int, msg string, args ...interface{}) { fuzzer.Config.Logf(level, msg, args...) } +type ProgFlags int + +const ( + ProgMinimized ProgFlags = 1 << iota + ProgSmashed + + progCandidate + progInTriage +) + type Candidate struct { - Prog *prog.Prog - Smashed bool - Minimized bool + Prog *prog.Prog + Flags ProgFlags } func (fuzzer *Fuzzer) AddCandidates(candidates []Candidate) { fuzzer.statCandidates.Add(len(candidates)) for _, candidate := range candidates { - req, flags := candidateRequest(fuzzer, candidate) - fuzzer.enqueue(fuzzer.candidateQueue, req, flags) + req := &queue.Request{ + Prog: candidate.Prog, + ExecOpts: setFlags(flatrpc.ExecFlagCollectSignal), + Stat: fuzzer.statExecCandidate, + Important: true, + } + fuzzer.enqueue(fuzzer.candidateQueue, req, candidate.Flags|progCandidate) } } diff --git a/pkg/fuzzer/job.go b/pkg/fuzzer/job.go index 00c7c034b..9d51ec2d8 100644 --- a/pkg/fuzzer/job.go +++ b/pkg/fuzzer/job.go @@ -20,15 +20,6 @@ type job interface { run(fuzzer *Fuzzer) } -type ProgTypes int - -const ( - progCandidate ProgTypes = 1 << iota - progMinimized - progSmashed - progInTriage -) - func genProgRequest(fuzzer *Fuzzer, rnd *rand.Rand) *queue.Request { p := fuzzer.target.Generate(rnd, prog.RecommendedCalls, @@ -59,22 +50,6 @@ func mutateProgRequest(fuzzer *Fuzzer, rnd *rand.Rand) *queue.Request { } } -func candidateRequest(fuzzer *Fuzzer, input Candidate) (*queue.Request, ProgTypes) { - flags := progCandidate - if input.Minimized { - flags |= progMinimized - } - if input.Smashed { - flags |= progSmashed - } - return &queue.Request{ - Prog: input.Prog, - ExecOpts: setFlags(flatrpc.ExecFlagCollectSignal), - Stat: fuzzer.statExecCandidate, - Important: true, - }, flags -} - // triageJob are programs for which we noticed potential new coverage during // first execution. But we are not sure yet if the coverage is real or not. // During triage we understand if these programs in fact give new coverage, @@ -84,12 +59,12 @@ type triageJob struct { call int info *flatrpc.CallInfo newSignal signal.Signal - flags ProgTypes + flags ProgFlags fuzzer *Fuzzer queue queue.Executor } -func (job *triageJob) execute(req *queue.Request, flags ProgTypes) *queue.Result { +func (job *triageJob) execute(req *queue.Request, flags ProgFlags) *queue.Result { req.Important = true // All triage executions are important. return job.fuzzer.executeWithFlags(job.queue, req, flags) } @@ -106,7 +81,7 @@ func (job *triageJob) run(fuzzer *Fuzzer) { if stop || info.newStableSignal.Empty() { return } - if job.flags&progMinimized == 0 { + if job.flags&ProgMinimized == 0 { stop = job.minimize(info.newStableSignal) if stop { return @@ -116,7 +91,7 @@ func (job *triageJob) run(fuzzer *Fuzzer) { return } fuzzer.Logf(2, "added new input for %v to the corpus: %s", callName, job.p) - if job.flags&progSmashed == 0 { + if job.flags&ProgSmashed == 0 { fuzzer.startJob(fuzzer.statJobsSmash, &smashJob{ p: job.p.Clone(), call: job.call, @@ -139,7 +114,7 @@ type deflakedCover struct { rawCover []uint64 } -func (job *triageJob) deflake(exec func(*queue.Request, ProgTypes) *queue.Result, stat *stats.Val, +func (job *triageJob) deflake(exec func(*queue.Request, ProgFlags) *queue.Result, stat *stats.Val, rawCover bool) (info deflakedCover, stop bool) { // As demonstrated in #4639, programs reproduce with a very high, but not 100% probability. // The triage algorithm must tolerate this, so let's pick the signal that is common diff --git a/pkg/fuzzer/job_test.go b/pkg/fuzzer/job_test.go index cbb99daec..c975d0128 100644 --- a/pkg/fuzzer/job_test.go +++ b/pkg/fuzzer/job_test.go @@ -29,7 +29,7 @@ func TestDeflakeFail(t *testing.T) { } run := 0 - ret, stop := testJob.deflake(func(_ *queue.Request, _ ProgTypes) *queue.Result { + ret, stop := testJob.deflake(func(_ *queue.Request, _ ProgFlags) *queue.Result { run++ // For first, we return 0 and 1. For second, 1 and 2. And so on. return fakeResult(0, []uint64{uint64(run), uint64(run + 1)}, []uint64{10, 20}) @@ -54,7 +54,7 @@ func TestDeflakeSuccess(t *testing.T) { newSignal: signal.FromRaw([]uint64{0, 1, 2}, 0), } run := 0 - ret, stop := testJob.deflake(func(_ *queue.Request, _ ProgTypes) *queue.Result { + ret, stop := testJob.deflake(func(_ *queue.Request, _ ProgFlags) *queue.Result { run++ switch run { case 1: -- cgit mrf-deployment