diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2017-07-06 11:16:21 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2017-07-06 12:17:39 +0200 |
| commit | 76f45d87f2da4a2f6f7066797523d9457f71ad12 (patch) | |
| tree | 87814eda96de07824dabea84c3bd64549850db05 | |
| parent | f68d78b5a85be1b59350032055f60321fbbfc116 (diff) | |
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.
| -rw-r--r-- | pkg/repro/repro.go | 8 | ||||
| -rw-r--r-- | 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, |
