From 76f45d87f2da4a2f6f7066797523d9457f71ad12 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Thu, 6 Jul 2017 11:16:21 +0200 Subject: syz-manager: save proper report for reproducers We can start reproducing one crash, but end up reproducing another. Currently we still attribute the resulting repro to the original crash. This is wrong. Save the resulting desc/report for reproducers and use that in manager. --- pkg/repro/repro.go | 8 +++++- syz-manager/manager.go | 71 +++++++++++++++++++++++++++----------------------- 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/pkg/repro/repro.go b/pkg/repro/repro.go index b5e265301..bce549b0a 100644 --- a/pkg/repro/repro.go +++ b/pkg/repro/repro.go @@ -36,7 +36,10 @@ type Result struct { Opts csource.Options CRepro bool Stats Stats - Report []byte + // Description and report of the final crash that we reproduced. + // Can be different from what we started reproducing. + Desc string + Report []byte } type context struct { @@ -45,6 +48,7 @@ type context struct { instances chan *instance bootRequests chan int stats Stats + desc string report []byte } @@ -137,6 +141,7 @@ func Run(crashLog []byte, cfg *mgrconfig.Config, vmPool *vm.Pool, vmIndexes []in if res != nil { ctx.reproLog(3, "repro crashed as:\n%s", string(ctx.report)) res.Stats = ctx.stats + res.Desc = ctx.desc res.Report = ctx.report } @@ -666,6 +671,7 @@ func (ctx *context) testImpl(inst *vm.Instance, command string, duration time.Du ctx.reproLog(2, "program did not crash") return false, nil } + ctx.desc = desc ctx.report = report ctx.reproLog(2, "program crashed: %v", desc) return true, nil diff --git a/syz-manager/manager.go b/syz-manager/manager.go index 2eaa94778..9cb47d5f2 100644 --- a/syz-manager/manager.go +++ b/syz-manager/manager.go @@ -305,7 +305,7 @@ type RunResult struct { type ReproResult struct { instances []int - crash *Crash + desc0 string res *repro.Result err error } @@ -373,7 +373,7 @@ func (mgr *Manager) vmLoop() { Logf(1, "loop: starting repro of '%v' on instances %+v", crash.desc, vmIndexes) go func() { res, err := repro.Run(crash.output, mgr.cfg, mgr.vmPool, vmIndexes) - reproDone <- &ReproResult{vmIndexes, crash, res, err} + reproDone <- &ReproResult{vmIndexes, crash.desc, res, err} }() } for !canRepro() && len(instances) != 0 { @@ -415,18 +415,24 @@ func (mgr *Manager) vmLoop() { } case res := <-reproDone: crepro := false + desc := "" if res.res != nil { crepro = res.res.CRepro + desc = res.res.Desc } - Logf(1, "loop: repro on instances %+v finished '%v', repro=%v crepro=%v", - res.instances, res.crash.desc, res.res != nil, crepro) + Logf(1, "loop: repro on %+v finished '%v', repro=%v crepro=%v desc='%v'", + res.instances, res.desc0, res.res != nil, crepro, desc) if res.err != nil { Logf(0, "repro failed: %v", res.err) } - delete(reproducing, res.crash.desc) + delete(reproducing, res.desc0) instances = append(instances, res.instances...) reproInstances -= instancesPerRepro - mgr.saveRepro(res.crash, res.res) + if res.res == nil { + mgr.saveFailedRepro(res.desc0) + } else { + mgr.saveRepro(res.res) + } case <-shutdown: Logf(1, "loop: shutting down...") shutdown = nil @@ -599,37 +605,38 @@ func (mgr *Manager) needRepro(desc string) bool { return false } -func (mgr *Manager) saveRepro(crash *Crash, res *repro.Result) { - sig := hash.Hash([]byte(crash.desc)) - dir := filepath.Join(mgr.crashdir, sig.String()) - if res == nil { - if mgr.dash != nil { - fr := &dashapi.FailedRepro{ - Manager: mgr.cfg.Name, - BuildID: mgr.cfg.Tag, - Title: crash.desc, - } - if err := mgr.dash.ReportFailedRepro(fr); err != nil { - Logf(0, "failed to report failed repro to dashboard: %v", err) - } +func (mgr *Manager) saveFailedRepro(desc string) { + if mgr.dash != nil { + fr := &dashapi.FailedRepro{ + Manager: mgr.cfg.Name, + BuildID: mgr.cfg.Tag, + Title: desc, } - for i := 0; i < maxReproAttempts; i++ { - name := filepath.Join(dir, fmt.Sprintf("repro%v", i)) - if !osutil.IsExist(name) { - osutil.WriteFile(name, nil) - break - } + if err := mgr.dash.ReportFailedRepro(fr); err != nil { + Logf(0, "failed to report failed repro to dashboard: %v", err) } - return } + dir := filepath.Join(mgr.crashdir, hash.String([]byte(desc))) + for i := 0; i < maxReproAttempts; i++ { + name := filepath.Join(dir, fmt.Sprintf("repro%v", i)) + if !osutil.IsExist(name) { + osutil.WriteFile(name, nil) + break + } + } +} + +func (mgr *Manager) saveRepro(res *repro.Result) { + dir := filepath.Join(mgr.crashdir, hash.String([]byte(res.Desc))) + opts := fmt.Sprintf("# %+v\n", res.Opts) prog := res.Prog.Serialize() osutil.WriteFile(filepath.Join(dir, "repro.prog"), append([]byte(opts), prog...)) if len(mgr.cfg.Tag) > 0 { osutil.WriteFile(filepath.Join(dir, "repro.tag"), []byte(mgr.cfg.Tag)) } - if len(crash.text) > 0 { - osutil.WriteFile(filepath.Join(dir, "repro.report"), []byte(crash.text)) + if len(res.Report) > 0 { + osutil.WriteFile(filepath.Join(dir, "repro.report"), []byte(res.Report)) } osutil.WriteFile(filepath.Join(dir, "repro.log"), res.Stats.Log) stats := fmt.Sprintf("Extracting prog: %s\nMinimizing prog: %s\nSimplifying prog options: %s\nExtracting C: %s\nSimplifying C: %s\n", @@ -652,7 +659,7 @@ func (mgr *Manager) saveRepro(crash *Crash, res *repro.Result) { if mgr.dash != nil { var maintainers []string - guiltyFile := report.ExtractGuiltyFile(crash.text) + guiltyFile := report.ExtractGuiltyFile(res.Report) if guiltyFile != "" { var err error maintainers, err = report.GetMaintainers(mgr.cfg.Kernel_Src, guiltyFile) @@ -663,10 +670,10 @@ func (mgr *Manager) saveRepro(crash *Crash, res *repro.Result) { dc := &dashapi.Crash{ Manager: mgr.cfg.Name, BuildID: mgr.cfg.Tag, - Title: crash.desc, + Title: res.Desc, Maintainers: maintainers, - Log: crash.output, - Report: crash.text, + Log: nil, + Report: res.Report, ReproOpts: []byte(fmt.Sprintf("%+v", res.Opts)), ReproSyz: []byte(res.Prog.Serialize()), ReproC: cprogText, -- cgit mrf-deployment