diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2016-10-18 21:07:40 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2016-11-19 10:00:36 +0100 |
| commit | 59f7c210d0584164a821bde6686debe169660f30 (patch) | |
| tree | ffb942e9f3af91fb6e6fb26ca1ae4e48f9a54962 /tools/syz-repro | |
| parent | dbc7ff38051cba31976238c743b1d8c53ce64470 (diff) | |
repro: factor out of syz-repro tool
Factor out repro logic from syz-repro tool,
so that it can be used in syz-manager.
Also, support sandboxes in code generated by
csoure. This is required to reproduce crashes
that require e.g. namespace sandbox.
Diffstat (limited to 'tools/syz-repro')
| -rw-r--r-- | tools/syz-repro/repro.go | 241 |
1 files changed, 22 insertions, 219 deletions
diff --git a/tools/syz-repro/repro.go b/tools/syz-repro/repro.go index 27e6c1cd9..fb9ad0ef6 100644 --- a/tools/syz-repro/repro.go +++ b/tools/syz-repro/repro.go @@ -9,17 +9,12 @@ import ( "io/ioutil" "os" "os/signal" - "path/filepath" - "sort" "syscall" - "time" "github.com/google/syzkaller/config" "github.com/google/syzkaller/csource" - "github.com/google/syzkaller/fileutil" . "github.com/google/syzkaller/log" - "github.com/google/syzkaller/prog" - "github.com/google/syzkaller/report" + "github.com/google/syzkaller/repro" "github.com/google/syzkaller/vm" _ "github.com/google/syzkaller/vm/adb" _ "github.com/google/syzkaller/vm/gce" @@ -30,20 +25,10 @@ import ( var ( flagConfig = flag.String("config", "", "configuration file") flagCount = flag.Int("count", 0, "number of VMs to use (overrides config count param)") - - instances chan VM - bootRequests chan int - shutdown = make(chan struct{}) ) -type VM struct { - vm.Instance - index int - execprogBin string - executorBin string -} - func main() { + os.Args = append(append([]string{}, os.Args[0], "-v=10"), os.Args[1:]...) flag.Parse() cfg, _, _, err := config.Parse(*flagConfig) if err != nil { @@ -52,10 +37,9 @@ func main() { if *flagCount > 0 { cfg.Count = *flagCount } - if _, err := os.Stat(filepath.Join(cfg.Syzkaller, "bin/syz-execprog")); err != nil { - Fatalf("bin/syz-execprog is missing (run 'make execprog')") + if cfg.Count > 4 { + cfg.Count = 4 } - if len(flag.Args()) != 1 { Fatalf("usage: syz-repro -config=config.file execution.log") } @@ -63,220 +47,39 @@ func main() { if err != nil { Fatalf("failed to open log file: %v", err) } - entries := prog.ParseLog(data) - Logf(0, "parsed %v programs", len(entries)) - - crashDesc, _, crashStart, _ := report.Parse(data) - if crashDesc == "" { - crashStart = len(data) // assuming VM hanged - } - - instances = make(chan VM, cfg.Count) - bootRequests = make(chan int, cfg.Count) - for i := 0; i < cfg.Count; i++ { - bootRequests <- i - go func() { - for index := range bootRequests { - vmCfg, err := config.CreateVMConfig(cfg, index) - if err != nil { - Fatalf("failed to create VM config: %v", err) - } - inst, err := vm.Create(cfg.Type, vmCfg) - if err != nil { - Fatalf("failed to create VM: %v", err) - } - execprogBin, err := inst.Copy(filepath.Join(cfg.Syzkaller, "bin/syz-execprog")) - if err != nil { - Fatalf("failed to copy to VM: %v", err) - } - executorBin, err := inst.Copy(filepath.Join(cfg.Syzkaller, "bin/syz-executor")) - if err != nil { - Fatalf("failed to copy to VM: %v", err) - } - instances <- VM{inst, index, execprogBin, executorBin} - } - }() + vmIndexes := make([]int, cfg.Count) + for i := range vmIndexes { + vmIndexes[i] = i } go func() { c := make(chan os.Signal, 2) signal.Notify(c, syscall.SIGINT) <-c - close(shutdown) + close(vm.Shutdown) Logf(-1, "shutting down...") <-c Fatalf("terminating") }() - repro(cfg, entries, crashStart) - exit() -} - -func exit() { - for { - select { - case inst := <-instances: - inst.Close() - default: - os.Exit(0) - } - } -} - -func repro(cfg *config.Config, entries []*prog.LogEntry, crashStart int) { - // Cut programs that were executed after crash. - for i, ent := range entries { - if ent.Start > crashStart { - entries = entries[:i] - break - } - } - // Extract last program on every proc. - procs := make(map[int]int) - for i, ent := range entries { - procs[ent.Proc] = i - } - var indices []int - for _, idx := range procs { - indices = append(indices, idx) - } - sort.Ints(indices) - var suspected []*prog.LogEntry - for i := len(indices) - 1; i >= 0; i-- { - suspected = append(suspected, entries[indices[i]]) - } - // Execute the suspected programs. - Logf(0, "the suspected programs are:") - for _, ent := range suspected { - Logf(0, "on proc %v:\n%s\n", ent.Proc, ent.P.Serialize()) - } - var p *prog.Prog - multiplier := 1 - for ; p == nil && multiplier <= 100; multiplier *= 10 { - for _, ent := range suspected { - if testProg(cfg, ent.P, multiplier, true, true) { - p = ent.P - break - } - } + res, err := repro.Run(data, cfg, vmIndexes) + if err != nil { + Logf(0, "reproduction failed: %v", err) } - if p == nil { - Logf(0, "no program crashed") + if res == nil { return } - Logf(0, "minimizing program") - - p, _ = prog.Minimize(p, -1, func(p1 *prog.Prog, callIndex int) bool { - return testProg(cfg, p1, multiplier, true, true) - }) - opts := csource.Options{ - Threaded: true, - Collide: true, - } - if testProg(cfg, p, multiplier, true, false) { - opts.Collide = false - if testProg(cfg, p, multiplier, false, false) { - opts.Threaded = false + fmt.Printf("opts: %+v crepro: %v\n\n", res.Opts, res.CRepro) + fmt.Printf("%s\n", res.Prog.Serialize()) + if res.CRepro { + src, err := csource.Write(res.Prog, res.Opts) + if err != nil { + Fatalf("failed to generate C repro: %v", err) } + if formatted, err := csource.Format(src); err == nil { + src = formatted + } + fmt.Printf("%s\n", src) } - - src := csource.Write(p, opts) - src, _ = csource.Format(src) - Logf(0, "C source:\n%s\n", src) - srcf, err := fileutil.WriteTempFile(src) - if err != nil { - Fatalf("%v", err) - } - bin, err := csource.Build(srcf) - if err != nil { - Fatalf("%v", err) - } - defer os.Remove(bin) - testBin(cfg, bin) -} - -func returnInstance(inst VM, res bool) { - if res { - // The test crashed, discard the VM and issue another boot request. - bootRequests <- inst.index - inst.Close() - } else { - // The test did not crash, reuse the same VM in future. - instances <- inst - } -} - -func testProg(cfg *config.Config, p *prog.Prog, multiplier int, threaded, collide bool) (res bool) { - Logf(0, "booting VM") - var inst VM - select { - case inst = <-instances: - case <-shutdown: - exit() - } - defer func() { - returnInstance(inst, res) - }() - - pstr := p.Serialize() - progFile, err := fileutil.WriteTempFile(pstr) - if err != nil { - Fatalf("%v", err) - } - defer os.Remove(progFile) - bin, err := inst.Copy(progFile) - if err != nil { - Fatalf("failed to copy to VM: %v", err) - } - - repeat := 100 - timeoutSec := 10 * repeat / cfg.Procs - if threaded { - repeat *= 10 - timeoutSec *= 1 - } - repeat *= multiplier - timeoutSec *= multiplier - timeout := time.Duration(timeoutSec) * time.Second - command := fmt.Sprintf("%v -executor %v -cover=0 -procs=%v -repeat=%v -sandbox %v -threaded=%v -collide=%v %v", - inst.execprogBin, inst.executorBin, cfg.Procs, repeat, cfg.Sandbox, threaded, collide, bin) - Logf(0, "testing program (threaded=%v, collide=%v, repeat=%v, timeout=%v):\n%s\n", - threaded, collide, repeat, timeout, pstr) - return testImpl(inst, command, timeout) -} - -func testBin(cfg *config.Config, bin string) (res bool) { - Logf(0, "booting VM") - var inst VM - select { - case inst = <-instances: - case <-shutdown: - exit() - } - defer func() { - returnInstance(inst, res) - }() - - bin, err := inst.Copy(bin) - if err != nil { - Fatalf("failed to copy to VM: %v", err) - } - Logf(0, "testing compiled C program") - return testImpl(inst, bin, 10*time.Second) -} - -func testImpl(inst vm.Instance, command string, timeout time.Duration) (res bool) { - outc, errc, err := inst.Run(timeout, command) - if err != nil { - Fatalf("failed to run command in VM: %v", err) - } - desc, text, output, crashed, timedout := vm.MonitorExecution(outc, errc, false, false) - _, _ = text, output - if crashed || timedout { - Logf(0, "program crashed with: %v", desc) - return true - } - Logf(0, "program did not crash") - return false } |
