diff options
| author | Aleksandr Nogikh <nogikh@google.com> | 2024-08-30 15:58:57 +0200 |
|---|---|---|
| committer | Aleksandr Nogikh <nogikh@google.com> | 2024-09-02 18:39:02 +0000 |
| commit | 8d09a3b3b53116077d125020ea2db71db49bc8de (patch) | |
| tree | 06e802a01b7f0efda0808bca19486135df4a9948 /syz-manager/manager.go | |
| parent | 1eda0d1459e5ff07903ffa2f8cedf55ae7b24af0 (diff) | |
syz-manager: move repro loop to pkg/manager
This is a potentially reusable piece of functionality.
Diffstat (limited to 'syz-manager/manager.go')
| -rw-r--r-- | syz-manager/manager.go | 146 |
1 files changed, 59 insertions, 87 deletions
diff --git a/syz-manager/manager.go b/syz-manager/manager.go index 90aa4198a..fc38b74ed 100644 --- a/syz-manager/manager.go +++ b/syz-manager/manager.go @@ -34,6 +34,7 @@ import ( "github.com/google/syzkaller/pkg/gce" "github.com/google/syzkaller/pkg/hash" "github.com/google/syzkaller/pkg/log" + "github.com/google/syzkaller/pkg/manager" "github.com/google/syzkaller/pkg/mgrconfig" "github.com/google/syzkaller/pkg/osutil" "github.com/google/syzkaller/pkg/report" @@ -112,15 +113,15 @@ type Manager struct { dataRaceFrames map[string]bool saturatedCalls map[string]bool - externalReproQueue chan *Crash - crashes chan *Crash + externalReproQueue chan *manager.Crash + crashes chan *manager.Crash benchMu sync.Mutex benchFile *os.File assetStorage *asset.Storage - reproMgr *reproManager + reproLoop *manager.ReproLoop Stats } @@ -153,27 +154,6 @@ const ( const currentDBVersion = 5 -type Crash struct { - instanceIndex int - fromHub bool // this crash was created based on a repro from syz-hub - fromDashboard bool // .. or from dashboard - manual bool - *report.Report -} - -func (c *Crash) FullTitle() string { - if c.Report.Title != "" { - return c.Report.Title - } - // Just use some unique, but stable titles. - if c.fromDashboard { - return fmt.Sprintf("dashboard crash %p", c) - } else if c.fromHub { - return fmt.Sprintf("crash from hub %p", c) - } - panic("the crash is expected to have a report") -} - func main() { if prog.GitRevision == "" { log.Fatalf("bad syz-manager build: build with make, run bin/syz-manager") @@ -249,8 +229,8 @@ func RunManager(mode Mode, cfg *mgrconfig.Config) { memoryLeakFrames: make(map[string]bool), dataRaceFrames: make(map[string]bool), fresh: true, - externalReproQueue: make(chan *Crash, 10), - crashes: make(chan *Crash, 10), + externalReproQueue: make(chan *manager.Crash, 10), + crashes: make(chan *manager.Crash, 10), saturatedCalls: make(map[string]bool), } @@ -313,10 +293,10 @@ func RunManager(mode Mode, cfg *mgrconfig.Config) { return } mgr.pool = vm.NewDispatcher(mgr.vmPool, mgr.fuzzerInstance) - mgr.reproMgr = newReproManager(mgr, mgr.vmPool.Count()-mgr.cfg.FuzzingVMs, mgr.cfg.DashboardOnlyRepro) + mgr.reproLoop = manager.NewReproLoop(mgr, mgr.vmPool.Count()-mgr.cfg.FuzzingVMs, mgr.cfg.DashboardOnlyRepro) ctx := vm.ShutdownCtx() go mgr.processFuzzingResults(ctx) - go mgr.reproMgr.Loop(ctx) + go mgr.reproLoop.Loop(ctx) mgr.pool.Loop(ctx) } @@ -378,14 +358,6 @@ func (mgr *Manager) writeBench() { } } -type ReproResult struct { - crash *Crash // the original crash - repro *repro.Result - strace *repro.StraceResult - stats *repro.Stats - err error -} - func (mgr *Manager) processFuzzingResults(ctx context.Context) { for { select { @@ -394,7 +366,7 @@ func (mgr *Manager) processFuzzingResults(ctx context.Context) { case crash := <-mgr.crashes: needRepro := mgr.saveCrash(crash) if mgr.cfg.Reproduce && needRepro { - mgr.reproMgr.Enqueue(crash) + mgr.reproLoop.Enqueue(crash) } case err := <-mgr.pool.BootErrors: crash := mgr.convertBootError(err) @@ -402,14 +374,14 @@ func (mgr *Manager) processFuzzingResults(ctx context.Context) { mgr.saveCrash(crash) } case crash := <-mgr.externalReproQueue: - if mgr.needRepro(crash) { - mgr.reproMgr.Enqueue(crash) + if mgr.NeedRepro(crash) { + mgr.reproLoop.Enqueue(crash) } } } } -func (mgr *Manager) convertBootError(err error) *Crash { +func (mgr *Manager) convertBootError(err error) *manager.Crash { var bootErr vm.BootErrorer if errors.As(err, &bootErr) { title, output := bootErr.BootError() @@ -424,7 +396,7 @@ func (mgr *Manager) convertBootError(err error) *Crash { Output: output, } } - return &Crash{ + return &manager.Crash{ Report: rep, } } @@ -453,13 +425,13 @@ func reportReproError(err error) { log.Errorf("repro failed: %v", err) } -func (mgr *Manager) runRepro(crash *Crash) *ReproResult { +func (mgr *Manager) RunRepro(crash *manager.Crash) *manager.ReproResult { res, stats, err := repro.Run(crash.Output, mgr.cfg, mgr.enabledFeatures, mgr.reporter, mgr.pool) - ret := &ReproResult{ - crash: crash, - repro: res, - stats: stats, - err: err, + ret := &manager.ReproResult{ + Crash: crash, + Repro: res, + Stats: stats, + Err: err, } if err == nil && res != nil && mgr.cfg.StraceBin != "" { const straceAttempts = 2 @@ -471,7 +443,7 @@ func (mgr *Manager) runRepro(crash *Crash) *ReproResult { // We only want to save strace output if it resulted in the same bug. // Otherwise, it will be hard to reproduce on syzbot and will confuse users. if sameBug { - ret.strace = strace + ret.Strace = strace break } } @@ -482,17 +454,17 @@ func (mgr *Manager) runRepro(crash *Crash) *ReproResult { return ret } -func (mgr *Manager) processRepro(res *ReproResult) { - if res.err != nil { - reportReproError(res.err) +func (mgr *Manager) processRepro(res *manager.ReproResult) { + if res.Err != nil { + reportReproError(res.Err) } - if res.repro == nil { - if res.crash.Title == "" { + if res.Repro == nil { + if res.Crash.Title == "" { log.Logf(1, "repro '%v' not from dashboard, so not reporting the failure", - res.crash.FullTitle()) + res.Crash.FullTitle()) } else { - log.Logf(1, "report repro failure of '%v'", res.crash.Title) - mgr.saveFailedRepro(res.crash.Report, res.stats) + log.Logf(1, "report repro failure of '%v'", res.Crash.Title) + mgr.saveFailedRepro(res.Crash.Report, res.Stats) } } else { mgr.saveRepro(res) @@ -766,8 +738,8 @@ func (mgr *Manager) fuzzerInstance(ctx context.Context, inst *vm.Instance, updIn rep.MachineInfo = machineInfo } if err == nil && rep != nil { - mgr.crashes <- &Crash{ - instanceIndex: inst.Index(), + mgr.crashes <- &manager.Crash{ + InstanceIndex: inst.Index(), Report: rep, } } @@ -820,7 +792,7 @@ func (mgr *Manager) runInstanceInner(ctx context.Context, inst *vm.Instance, inj return rep, vmInfo, nil } -func (mgr *Manager) emailCrash(crash *Crash) { +func (mgr *Manager) emailCrash(crash *manager.Crash) { if len(mgr.cfg.EmailAddrs) == 0 { return } @@ -835,7 +807,7 @@ func (mgr *Manager) emailCrash(crash *Crash) { } } -func (mgr *Manager) saveCrash(crash *Crash) bool { +func (mgr *Manager) saveCrash(crash *manager.Crash) bool { if err := mgr.reporter.Symbolize(crash.Report); err != nil { log.Errorf("failed to symbolize report: %v", err) } @@ -856,7 +828,7 @@ func (mgr *Manager) saveCrash(crash *Crash) bool { if crash.Suppressed { flags += " [suppressed]" } - log.Logf(0, "VM %v: crash: %v%v", crash.instanceIndex, crash.Title, flags) + log.Logf(0, "VM %v: crash: %v%v", crash.InstanceIndex, crash.Title, flags) if mgr.mode == ModeSmokeTest { data, err := json.Marshal(crash.Report) @@ -949,12 +921,12 @@ func (mgr *Manager) saveCrash(crash *Crash) bool { writeOrRemove("tag", []byte(mgr.cfg.Tag)) writeOrRemove("report", crash.Report.Report) writeOrRemove("machineInfo", crash.MachineInfo) - return mgr.needRepro(crash) + return mgr.NeedRepro(crash) } const maxReproAttempts = 3 -func (mgr *Manager) needLocalRepro(crash *Crash) bool { +func (mgr *Manager) needLocalRepro(crash *manager.Crash) bool { if !mgr.cfg.Reproduce || crash.Corrupted || crash.Suppressed { return false } @@ -971,11 +943,11 @@ func (mgr *Manager) needLocalRepro(crash *Crash) bool { return false } -func (mgr *Manager) needRepro(crash *Crash) bool { +func (mgr *Manager) NeedRepro(crash *manager.Crash) bool { if !mgr.cfg.Reproduce { return false } - if crash.fromHub || crash.fromDashboard { + if crash.FromHub || crash.FromDashboard { return true } if !mgr.checkDone.Load() || (mgr.enabledFeatures&flatrpc.FeatureLeak != 0 && @@ -1043,13 +1015,13 @@ func (mgr *Manager) saveFailedRepro(rep *report.Report, stats *repro.Stats) { } } -func (mgr *Manager) saveRepro(res *ReproResult) { - repro := res.repro +func (mgr *Manager) saveRepro(res *manager.ReproResult) { + repro := res.Repro opts := fmt.Sprintf("# %+v\n", repro.Opts) progText := repro.Prog.Serialize() // Append this repro to repro list to send to hub if it didn't come from hub originally. - if !res.crash.fromHub { + if !res.Crash.FromHub { progForHub := []byte(fmt.Sprintf("# %+v\n# %v\n# %v\n%s", repro.Opts, repro.Report.Title, mgr.cfg.Tag, progText)) mgr.mu.Lock() @@ -1082,11 +1054,11 @@ func (mgr *Manager) saveRepro(res *ReproResult) { output := report.Output var crashFlags dashapi.CrashFlags - if res.strace != nil { + if res.Strace != nil { // If syzkaller managed to successfully run the repro with strace, send // the report and the output generated under strace. - report = res.strace.Report - output = res.strace.Output + report = res.Strace.Report + output = res.Strace.Output crashFlags = dashapi.CrashUnderStrace } @@ -1102,9 +1074,9 @@ func (mgr *Manager) saveRepro(res *ReproResult) { ReproOpts: repro.Opts.Serialize(), ReproSyz: progText, ReproC: cprogText, - ReproLog: truncateReproLog(res.stats.FullLog()), + ReproLog: truncateReproLog(res.Stats.FullLog()), Assets: mgr.uploadReproAssets(repro), - OriginalTitle: res.crash.Title, + OriginalTitle: res.Crash.Title, } setGuiltyFiles(dc, report) if _, err := mgr.dash.ReportCrash(dc); err != nil { @@ -1142,22 +1114,22 @@ func (mgr *Manager) saveRepro(res *ReproResult) { log.Logf(0, "failed to write crash asset: type %d, write error %v", typ, err) } }) - if res.strace != nil { + if res.Strace != nil { // Unlike dashboard reporting, we save strace output separately from the original log. - if res.strace.Error != nil { + if res.Strace.Error != nil { osutil.WriteFile(filepath.Join(dir, "strace.error"), - []byte(fmt.Sprintf("%v", res.strace.Error))) + []byte(fmt.Sprintf("%v", res.Strace.Error))) } - if len(res.strace.Output) > 0 { - osutil.WriteFile(filepath.Join(dir, "strace.log"), res.strace.Output) + if len(res.Strace.Output) > 0 { + osutil.WriteFile(filepath.Join(dir, "strace.log"), res.Strace.Output) } } - if reproLog := res.stats.FullLog(); len(reproLog) > 0 { + if reproLog := res.Stats.FullLog(); len(reproLog) > 0 { osutil.WriteFile(filepath.Join(dir, "repro.stats"), reproLog) } } -func (mgr *Manager) resizeReproPool(size int) { +func (mgr *Manager) ResizeReproPool(size int) { mgr.pool.ReserveForRun(size) } @@ -1534,11 +1506,11 @@ func (mgr *Manager) fuzzerLoop(fuzzer *fuzzer.Fuzzer) { go mgr.hubSyncLoop(pickGetter(mgr.cfg.HubKey)) } else { mgr.phase = phaseTriagedHub - mgr.reproMgr.StartReproduction() + mgr.reproLoop.StartReproduction() } } else if mgr.phase == phaseQueriedHub { mgr.phase = phaseTriagedHub - mgr.reproMgr.StartReproduction() + mgr.reproLoop.StartReproduction() } mgr.mu.Unlock() } @@ -1555,7 +1527,7 @@ func (mgr *Manager) hubIsUnreachable() { if mgr.phase == phaseTriagedCorpus { dash = mgr.dash mgr.phase = phaseTriagedHub - mgr.reproMgr.StartReproduction() + mgr.reproLoop.StartReproduction() log.Errorf("did not manage to connect to syz-hub; moving forward") } mgr.mu.Unlock() @@ -1646,7 +1618,7 @@ func (mgr *Manager) dashboardReporter() { func (mgr *Manager) dashboardReproTasks() { for range time.NewTicker(20 * time.Minute).C { - if !mgr.reproMgr.CanReproMore() { + if !mgr.reproLoop.CanReproMore() { // We don't need reproducers at the moment. continue } @@ -1656,9 +1628,9 @@ func (mgr *Manager) dashboardReproTasks() { continue } if len(resp.CrashLog) > 0 { - mgr.externalReproQueue <- &Crash{ - fromDashboard: true, - manual: resp.Type == dashapi.ManualLog, + mgr.externalReproQueue <- &manager.Crash{ + FromDashboard: true, + Manual: resp.Type == dashapi.ManualLog, Report: &report.Report{ Title: resp.Title, Output: resp.CrashLog, |
