aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pkg/fuzzer/fuzzer.go16
-rw-r--r--pkg/fuzzer/queue/queue.go11
-rw-r--r--pkg/fuzzer/queue/queue_test.go12
-rw-r--r--pkg/fuzzer/stats.go43
-rw-r--r--syz-manager/manager.go63
-rw-r--r--tools/syz-testbed/instance.go25
-rw-r--r--tools/syz-testbed/targets.go2
-rw-r--r--tools/syz-testbed/testbed.go2
8 files changed, 101 insertions, 73 deletions
diff --git a/pkg/fuzzer/fuzzer.go b/pkg/fuzzer/fuzzer.go
index 9ad218433..8be912139 100644
--- a/pkg/fuzzer/fuzzer.go
+++ b/pkg/fuzzer/fuzzer.go
@@ -76,7 +76,7 @@ type execQueues struct {
func newExecQueues(fuzzer *Fuzzer) execQueues {
ret := execQueues{
triageCandidateQueue: queue.DynamicOrder(),
- candidateQueue: queue.PlainWithStat(fuzzer.StatCandidates),
+ candidateQueue: queue.Plain(),
triageQueue: queue.DynamicOrder(),
smashQueue: queue.Plain(),
}
@@ -92,6 +92,10 @@ func newExecQueues(fuzzer *Fuzzer) execQueues {
return ret
}
+func (fuzzer *Fuzzer) CandidateTriageFinished() bool {
+ return fuzzer.statCandidates.Val()+fuzzer.statJobsTriageCandidate.Val() == 0
+}
+
func (fuzzer *Fuzzer) execute(executor queue.Executor, req *queue.Request) *queue.Result {
return fuzzer.executeWithFlags(executor, req, 0)
}
@@ -130,6 +134,9 @@ func (fuzzer *Fuzzer) processResult(req *queue.Request, res *queue.Result, flags
if res.Info != nil {
fuzzer.statExecTime.Add(int(res.Info.Elapsed / 1e6))
}
+ if flags&progCandidate != 0 {
+ fuzzer.statCandidates.Add(-1)
+ }
}
type Config struct {
@@ -161,11 +168,11 @@ func (fuzzer *Fuzzer) triageProgCall(p *prog.Prog, info *flatrpc.CallInfo, call
}
fuzzer.Logf(2, "found new signal in call %d in %s", call, p)
- queue := fuzzer.triageQueue
+ queue, stat := fuzzer.triageQueue, fuzzer.statJobsTriage
if flags&progCandidate > 0 {
- queue = fuzzer.triageCandidateQueue
+ queue, stat = fuzzer.triageCandidateQueue, fuzzer.statJobsTriageCandidate
}
- fuzzer.startJob(fuzzer.statJobsTriage, &triageJob{
+ fuzzer.startJob(stat, &triageJob{
p: p.Clone(),
call: call,
info: info,
@@ -243,6 +250,7 @@ type Candidate struct {
}
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)
diff --git a/pkg/fuzzer/queue/queue.go b/pkg/fuzzer/queue/queue.go
index a9411c1bd..cb70f5a9b 100644
--- a/pkg/fuzzer/queue/queue.go
+++ b/pkg/fuzzer/queue/queue.go
@@ -173,7 +173,6 @@ type Source interface {
// PlainQueue is a straighforward thread-safe Request queue implementation.
type PlainQueue struct {
- stat *stats.Val
mu sync.Mutex
queue []*Request
pos int
@@ -183,10 +182,6 @@ func Plain() *PlainQueue {
return &PlainQueue{}
}
-func PlainWithStat(val *stats.Val) *PlainQueue {
- return &PlainQueue{stat: val}
-}
-
func (pq *PlainQueue) Len() int {
pq.mu.Lock()
defer pq.mu.Unlock()
@@ -194,9 +189,6 @@ func (pq *PlainQueue) Len() int {
}
func (pq *PlainQueue) Submit(req *Request) {
- if pq.stat != nil {
- pq.stat.Add(1)
- }
pq.mu.Lock()
defer pq.mu.Unlock()
@@ -235,9 +227,6 @@ func (pq *PlainQueue) nextLocked() *Request {
ret := pq.queue[pq.pos]
pq.queue[pq.pos] = nil
pq.pos++
- if pq.stat != nil {
- pq.stat.Add(-1)
- }
return ret
}
diff --git a/pkg/fuzzer/queue/queue_test.go b/pkg/fuzzer/queue/queue_test.go
index a89ec0d3d..5b6a03ed0 100644
--- a/pkg/fuzzer/queue/queue_test.go
+++ b/pkg/fuzzer/queue/queue_test.go
@@ -6,29 +6,19 @@ package queue
import (
"testing"
- "github.com/google/syzkaller/pkg/stats"
"github.com/stretchr/testify/assert"
)
func TestPlainQueue(t *testing.T) {
- val := stats.Create("v0", "desc0")
- pq := PlainWithStat(val)
+ pq := Plain()
req1, req2, req3 := &Request{}, &Request{}, &Request{}
pq.Submit(req1)
- assert.Equal(t, 1, val.Val())
pq.Submit(req2)
- assert.Equal(t, 2, val.Val())
-
assert.Equal(t, req1, pq.Next())
- assert.Equal(t, 1, val.Val())
-
assert.Equal(t, req2, pq.Next())
- assert.Equal(t, 0, val.Val())
-
pq.Submit(req3)
- assert.Equal(t, 1, val.Val())
assert.Equal(t, req3, pq.Next())
assert.Nil(t, pq.Next())
}
diff --git a/pkg/fuzzer/stats.go b/pkg/fuzzer/stats.go
index c860b8bb9..2129c048a 100644
--- a/pkg/fuzzer/stats.go
+++ b/pkg/fuzzer/stats.go
@@ -6,35 +6,38 @@ package fuzzer
import "github.com/google/syzkaller/pkg/stats"
type Stats struct {
- StatCandidates *stats.Val
- statNewInputs *stats.Val
- statJobs *stats.Val
- statJobsTriage *stats.Val
- statJobsSmash *stats.Val
- statJobsHints *stats.Val
- statExecTime *stats.Val
- statExecGenerate *stats.Val
- statExecFuzz *stats.Val
- statExecCandidate *stats.Val
- statExecTriage *stats.Val
- statExecMinimize *stats.Val
- statExecSmash *stats.Val
- statExecHint *stats.Val
- statExecSeed *stats.Val
- statExecCollide *stats.Val
+ statCandidates *stats.Val
+ statNewInputs *stats.Val
+ statJobs *stats.Val
+ statJobsTriage *stats.Val
+ statJobsTriageCandidate *stats.Val
+ statJobsSmash *stats.Val
+ statJobsHints *stats.Val
+ statExecTime *stats.Val
+ statExecGenerate *stats.Val
+ statExecFuzz *stats.Val
+ statExecCandidate *stats.Val
+ statExecTriage *stats.Val
+ statExecMinimize *stats.Val
+ statExecSmash *stats.Val
+ statExecHint *stats.Val
+ statExecSeed *stats.Val
+ statExecCollide *stats.Val
}
func newStats() Stats {
return Stats{
- StatCandidates: stats.Create("candidates", "Number of candidate programs in triage queue",
+ statCandidates: stats.Create("candidates", "Number of candidate programs in triage queue",
stats.Console, stats.Graph("corpus")),
statNewInputs: stats.Create("new inputs", "Potential untriaged corpus candidates",
stats.Graph("corpus")),
statJobs: stats.Create("fuzzer jobs", "Total running fuzzer jobs", stats.NoGraph),
statJobsTriage: stats.Create("triage jobs", "Running triage jobs", stats.StackedGraph("jobs")),
- statJobsSmash: stats.Create("smash jobs", "Running smash jobs", stats.StackedGraph("jobs")),
- statJobsHints: stats.Create("hints jobs", "Running hints jobs", stats.StackedGraph("jobs")),
- statExecTime: stats.Create("prog exec time", "Test program execution time (ms)", stats.Distribution{}),
+ statJobsTriageCandidate: stats.Create("candidate triage jobs", "Running candidate triage jobs",
+ stats.StackedGraph("jobs")),
+ statJobsSmash: stats.Create("smash jobs", "Running smash jobs", stats.StackedGraph("jobs")),
+ statJobsHints: stats.Create("hints jobs", "Running hints jobs", stats.StackedGraph("jobs")),
+ statExecTime: stats.Create("prog exec time", "Test program execution time (ms)", stats.Distribution{}),
statExecGenerate: stats.Create("exec gen", "Executions of generated programs", stats.Rate{},
stats.StackedGraph("exec")),
statExecFuzz: stats.Create("exec fuzz", "Executions of mutated programs",
diff --git a/syz-manager/manager.go b/syz-manager/manager.go
index 44df6d9fd..8d68b4be2 100644
--- a/syz-manager/manager.go
+++ b/syz-manager/manager.go
@@ -55,7 +55,9 @@ var (
" The test consists of booting VMs and running some simple test programs\n"+
" to ensure that fuzzing can proceed in general. After completing the test\n"+
" the process exits and the exit status indicates success/failure.\n"+
- " If the kernel oopses during testing, the report is saved to workdir/report.json.\n")
+ " If the kernel oopses during testing, the report is saved to workdir/report.json.\n"+
+ " - corpus-triage: triage corpus and exit\n"+
+ " This is useful mostly for benchmarking with testbed.\n")
)
type Manager struct {
@@ -103,6 +105,9 @@ type Manager struct {
// Maps file name to modification time.
usedFiles map[string]time.Time
+ benchMu sync.Mutex
+ benchFile *os.File
+
assetStorage *asset.Storage
bootTime stats.AverageValue[time.Duration]
@@ -116,6 +121,7 @@ type Mode int
const (
ModeFuzzing Mode = iota
ModeSmokeTest
+ ModeCorpusTriage
)
const (
@@ -168,6 +174,10 @@ func RunManager(cfg *mgrconfig.Config) {
mode = ModeSmokeTest
cfg.DashboardClient = ""
cfg.HubClient = ""
+ case "corpus-triage":
+ mode = ModeCorpusTriage
+ cfg.DashboardClient = ""
+ cfg.HubClient = ""
default:
flag.PrintDefaults()
log.Fatalf("unknown mode: %v", *flagMode)
@@ -258,6 +268,15 @@ func RunManager(cfg *mgrconfig.Config) {
mgr.vmLoop()
}
+// Exit successfully in special operation modes.
+func (mgr *Manager) exit(reason string) {
+ log.Logf(0, "%v finished, shutting down...", reason)
+ mgr.writeBench()
+ close(vm.Shutdown)
+ time.Sleep(10 * time.Second)
+ os.Exit(0)
+}
+
func (mgr *Manager) heartbeatLoop() {
lastTime := time.Now()
for now := range time.NewTicker(10 * time.Second).C {
@@ -280,23 +299,33 @@ func (mgr *Manager) initBench() {
if err != nil {
log.Fatalf("failed to open bench file: %v", err)
}
+ mgr.benchFile = f
go func() {
for range time.NewTicker(time.Minute).C {
- vals := make(map[string]int)
- for _, stat := range stats.Collect(stats.All) {
- vals[stat.Name] = stat.V
- }
- data, err := json.MarshalIndent(vals, "", " ")
- if err != nil {
- log.Fatalf("failed to serialize bench data")
- }
- if _, err := f.Write(append(data, '\n')); err != nil {
- log.Fatalf("failed to write bench data")
- }
+ mgr.writeBench()
}
}()
}
+func (mgr *Manager) writeBench() {
+ if mgr.benchFile == nil {
+ return
+ }
+ mgr.benchMu.Lock()
+ defer mgr.benchMu.Unlock()
+ vals := make(map[string]int)
+ for _, stat := range stats.Collect(stats.All) {
+ vals[stat.Name] = stat.V
+ }
+ data, err := json.MarshalIndent(vals, "", " ")
+ if err != nil {
+ log.Fatalf("failed to serialize bench data")
+ }
+ if _, err := mgr.benchFile.Write(append(data, '\n')); err != nil {
+ log.Fatalf("failed to write bench data")
+ }
+}
+
type RunResult struct {
idx int
crash *Crash
@@ -1409,10 +1438,7 @@ func (mgr *Manager) currentBugFrames() BugFrames {
func (mgr *Manager) machineChecked(features flatrpc.Feature, enabledSyscalls map[*prog.Syscall]bool,
opts flatrpc.ExecOpts) queue.Source {
if mgr.mode == ModeSmokeTest {
- log.Logf(0, "smoke test succeeded, shutting down...")
- close(vm.Shutdown)
- time.Sleep(10 * time.Second)
- os.Exit(0)
+ mgr.exit("smoke test")
}
mgr.mu.Lock()
@@ -1517,7 +1543,10 @@ func (mgr *Manager) fuzzerLoop(fuzzer *fuzzer.Fuzzer) {
}
// Update the state machine.
- if fuzzer.StatCandidates.Val() == 0 {
+ if fuzzer.CandidateTriageFinished() {
+ if mgr.mode == ModeCorpusTriage {
+ mgr.exit("corpus triage")
+ }
mgr.mu.Lock()
if mgr.phase == phaseLoadedCorpus {
if mgr.enabledFeatures&flatrpc.FeatureLeak != 0 {
diff --git a/tools/syz-testbed/instance.go b/tools/syz-testbed/instance.go
index 2ea3161e3..5ec278944 100644
--- a/tools/syz-testbed/instance.go
+++ b/tools/syz-testbed/instance.go
@@ -118,9 +118,10 @@ func (inst *SyzManagerInstance) Run() error {
select {
case err := <-ret:
- // Syz-managers are not supposed to stop themselves under normal circumstances.
- // If one of them did stop, there must have been a very good reason to do so.
- return fmt.Errorf("[%s] stopped: %w", inst.Name, err)
+ if err != nil {
+ return fmt.Errorf("[%s] stopped: %w", inst.Name, err)
+ }
+ return nil
case <-time.After(inst.RunTime):
inst.Stop()
<-ret
@@ -131,6 +132,7 @@ func (inst *SyzManagerInstance) Run() error {
type SyzkallerInfo struct {
Workdir string
CfgFile string
+ Mode string
BenchFile string
}
@@ -162,7 +164,8 @@ func SetupSyzkallerInstance(mgrName, folder string, checkout *Checkout) (*Syzkal
}, nil
}
-func (t *SyzManagerTarget) newSyzManagerInstance(slotName, uniqName string, checkout *Checkout) (Instance, error) {
+func (t *SyzManagerTarget) newSyzManagerInstance(slotName, uniqName, mode string, checkout *Checkout) (
+ Instance, error) {
folder := filepath.Join(checkout.Path, fmt.Sprintf("run-%s", uniqName))
common, err := SetupSyzkallerInstance(slotName, folder, checkout)
if err != nil {
@@ -178,11 +181,15 @@ func (t *SyzManagerTarget) newSyzManagerInstance(slotName, uniqName string, chec
}
return &SyzManagerInstance{
InstanceCommon: InstanceCommon{
- Name: uniqName,
- LogFile: filepath.Join(folder, "log.txt"),
- ExecCommand: filepath.Join(checkout.Path, "bin", "syz-manager"),
- ExecCommandArgs: []string{"-config", common.CfgFile, "-bench", common.BenchFile},
- stopChannel: make(chan bool, 1),
+ Name: uniqName,
+ LogFile: filepath.Join(folder, "log.txt"),
+ ExecCommand: filepath.Join(checkout.Path, "bin", "syz-manager"),
+ ExecCommandArgs: []string{
+ "-config", common.CfgFile,
+ "-mode", mode,
+ "-bench", common.BenchFile,
+ },
+ stopChannel: make(chan bool, 1),
},
SyzkallerInfo: *common,
RunTime: t.config.RunTime.Duration,
diff --git a/tools/syz-testbed/targets.go b/tools/syz-testbed/targets.go
index ef0b189a4..6ba08e504 100644
--- a/tools/syz-testbed/targets.go
+++ b/tools/syz-testbed/targets.go
@@ -113,7 +113,7 @@ func (t *SyzManagerTarget) NewJob(slotName string, checkouts []*Checkout) (*Chec
t.nextInstanceID++
t.mu.Unlock()
uniqName := fmt.Sprintf("%s-%d", checkout.Name, instanceID)
- instance, err := t.newSyzManagerInstance(slotName, uniqName, checkout)
+ instance, err := t.newSyzManagerInstance(slotName, uniqName, t.config.ManagerMode, checkout)
if err != nil {
return nil, nil, err
}
diff --git a/tools/syz-testbed/testbed.go b/tools/syz-testbed/testbed.go
index c07ac3bc0..52e63b4be 100644
--- a/tools/syz-testbed/testbed.go
+++ b/tools/syz-testbed/testbed.go
@@ -40,6 +40,7 @@ type TestbedConfig struct {
Workdir string `json:"workdir"` // instances will be checked out there
ReproConfig ReproTestConfig `json:"repro_config"` // syz-repro benchmarking config
ManagerConfig json.RawMessage `json:"manager_config"` // base manager config
+ ManagerMode string `json:"manager_mode"` // manager mode flag
Checkouts []CheckoutConfig `json:"checkouts"`
}
@@ -81,6 +82,7 @@ func main() {
ReproConfig: ReproTestConfig{
CrashesPerBug: 1,
},
+ ManagerMode: "fuzzing",
}
err := config.LoadFile(*flagConfig, &cfg)
if err != nil {