aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2024-05-28 16:17:13 +0200
committerDmitry Vyukov <dvyukov@google.com>2024-06-03 15:04:36 +0000
commit701ad974ccc0ba0a61d2a2bd58db569f794bd037 (patch)
tree2bf3b266b9f9a5d77ccee6059bd885eef625265d
parent2addfcda6297288cd48c399dfbef1f5752162011 (diff)
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.
-rw-r--r--pkg/fuzzer/fuzzer.go34
-rw-r--r--pkg/fuzzer/job.go35
-rw-r--r--pkg/fuzzer/job_test.go4
-rw-r--r--syz-manager/hub.go8
-rw-r--r--syz-manager/manager.go26
5 files changed, 48 insertions, 59 deletions
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:
diff --git a/syz-manager/hub.go b/syz-manager/hub.go
index fc9662bb0..5ed0570c3 100644
--- a/syz-manager/hub.go
+++ b/syz-manager/hub.go
@@ -245,16 +245,18 @@ func (hc *HubConnector) processProgs(inputs []rpctype.HubInput) (minimized, smas
continue
}
min, smash := matchDomains(hc.domain, inp.Domain)
+ var flags fuzzer.ProgFlags
if min {
minimized++
+ flags |= fuzzer.ProgMinimized
}
if smash {
smashed++
+ flags |= fuzzer.ProgSmashed
}
candidates = append(candidates, fuzzer.Candidate{
- Prog: p,
- Minimized: min,
- Smashed: smash,
+ Prog: p,
+ Flags: flags,
})
}
hc.mgr.addNewCandidates(candidates)
diff --git a/syz-manager/manager.go b/syz-manager/manager.go
index 8d68b4be2..0837e0251 100644
--- a/syz-manager/manager.go
+++ b/syz-manager/manager.go
@@ -646,30 +646,30 @@ func (mgr *Manager) loadCorpus() {
// By default we don't re-minimize/re-smash programs from corpus,
// it takes lots of time on start and is unnecessary.
// However, on version bumps we can selectively re-minimize/re-smash.
- minimized, smashed := true, true
+ flags := fuzzer.ProgMinimized | fuzzer.ProgSmashed
switch mgr.corpusDB.Version {
case 0:
// Version 0 had broken minimization, so we need to re-minimize.
- minimized = false
+ flags &= ^fuzzer.ProgMinimized
fallthrough
case 1:
// Version 1->2: memory is preallocated so lots of mmaps become unnecessary.
- minimized = false
+ flags &= ^fuzzer.ProgMinimized
fallthrough
case 2:
// Version 2->3: big-endian hints.
- smashed = false
+ flags &= ^fuzzer.ProgSmashed
fallthrough
case 3:
// Version 3->4: to shake things up.
- minimized = false
+ flags &= ^fuzzer.ProgMinimized
fallthrough
case currentDBVersion:
}
var candidates []fuzzer.Candidate
broken := 0
for key, rec := range mgr.corpusDB.Records {
- drop, item := mgr.loadProg(rec.Val, minimized, smashed)
+ drop, item := mgr.loadProg(rec.Val, flags)
if drop {
mgr.corpusDB.Delete(key)
broken++
@@ -681,7 +681,7 @@ func (mgr *Manager) loadCorpus() {
mgr.fresh = len(mgr.corpusDB.Records) == 0
seeds := 0
for _, seed := range mgr.seeds {
- _, item := mgr.loadProg(seed, true, false)
+ _, item := mgr.loadProg(seed, fuzzer.ProgMinimized)
if item != nil {
candidates = append(candidates, *item)
seeds++
@@ -708,7 +708,7 @@ func (mgr *Manager) loadCorpus() {
}
// Returns (delete item from the corpus, a fuzzer.Candidate object).
-func (mgr *Manager) loadProg(data []byte, minimized, smashed bool) (drop bool, candidate *fuzzer.Candidate) {
+func (mgr *Manager) loadProg(data []byte, flags fuzzer.ProgFlags) (drop bool, candidate *fuzzer.Candidate) {
p, disabled, bad := parseProgram(mgr.target, mgr.targetEnabledSyscalls, data)
if bad != nil {
return true, nil
@@ -726,18 +726,16 @@ func (mgr *Manager) loadProg(data []byte, minimized, smashed bool) (drop bool, c
leftover := programLeftover(mgr.target, mgr.targetEnabledSyscalls, data)
if leftover != nil {
candidate = &fuzzer.Candidate{
- Prog: leftover,
- Minimized: false,
- Smashed: smashed,
+ Prog: leftover,
+ Flags: flags & ^fuzzer.ProgMinimized,
}
}
}
return false, candidate
}
return false, &fuzzer.Candidate{
- Prog: p,
- Minimized: minimized,
- Smashed: smashed,
+ Prog: p,
+ Flags: flags,
}
}