aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/fuzzer
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/fuzzer')
-rw-r--r--pkg/fuzzer/cover.go6
-rw-r--r--pkg/fuzzer/fuzzer.go77
-rw-r--r--pkg/fuzzer/fuzzer_test.go7
-rw-r--r--pkg/fuzzer/job.go29
-rw-r--r--pkg/fuzzer/stats.go26
5 files changed, 73 insertions, 72 deletions
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
}