func testImpl(inst vm.Instance, command string, timeout time.Duration) (res bool) { outc, errc, err := inst.Run(timeout, command) if err != nil { log.Fatalf("failed to run command in VM: %v", err) } var output []byte for { select { case out := <-outc: output = append(output, out...) if desc, _, _, found := vm.FindCrash(output); found { log.Printf("program crashed with '%s'", desc) return true } case err := <-errc: if err != nil { log.Printf("program crashed with result '%v'", err) return true } log.Printf("program did not crash") return false } } }
func (mgr *Manager) runInstance(vmCfg *vm.Config, first bool) bool { inst, err := vm.Create(mgr.cfg.Type, vmCfg) if err != nil { logf(0, "failed to create instance: %v", err) return false } defer inst.Close() fwdAddr, err := inst.Forward(mgr.port) if err != nil { logf(0, "failed to setup port forwarding: %v", err) return false } fuzzerBin, err := inst.Copy(filepath.Join(mgr.cfg.Syzkaller, "bin", "syz-fuzzer")) if err != nil { logf(0, "failed to copy binary: %v", err) return false } executorBin, err := inst.Copy(filepath.Join(mgr.cfg.Syzkaller, "bin", "syz-executor")) if err != nil { logf(0, "failed to copy binary: %v", err) return false } // TODO: this should be present in the image. _, errc, err := inst.Run(10*time.Second, "echo -n 0 > /proc/sys/debug/exception-trace") if err == nil { <-errc } // Run the fuzzer binary. cover := "" if mgr.cfg.NoCover { cover = "-nocover=1" } dropprivs := "" if mgr.cfg.NoDropPrivs { dropprivs = "-dropprivs=0" } calls := "" if mgr.enabledSyscalls != "" { calls = "-calls=" + mgr.enabledSyscalls } // Leak detection significantly slows down fuzzing, so detect leaks only on the first instance. leak := first && mgr.cfg.Leak outputC, errorC, err := inst.Run(time.Hour, fmt.Sprintf("%v -executor %v -name %v -manager %v -output=%v -procs %v -leak=%v %v %v %v", fuzzerBin, executorBin, vmCfg.Name, fwdAddr, mgr.cfg.Output, mgr.cfg.Procs, leak, cover, dropprivs, calls)) if err != nil { logf(0, "failed to run fuzzer: %v", err) return false } var output []byte matchPos := 0 const ( beforeContext = 256 << 10 afterContext = 64 << 10 ) ticker := time.NewTimer(time.Minute) for { if !ticker.Reset(time.Minute) { <-ticker.C } select { case err := <-errorC: switch err { case vm.TimeoutErr: logf(0, "%v: running long enough, restarting", vmCfg.Name) return true default: logf(0, "%v: lost connection: %v", vmCfg.Name, err) mgr.saveCrasher(vmCfg.Name, "lost connection", output) return true } case out := <-outputC: output = append(output, out...) if _, _, _, found := vm.FindCrash(output[matchPos:]); found { // Give it some time to finish writing the error message. timer := time.NewTimer(10 * time.Second).C loop: for { select { case out = <-outputC: output = append(output, out...) case <-timer: break loop } } desc, start, end, _ := vm.FindCrash(output[matchPos:]) start = start + matchPos - beforeContext if start < 0 { start = 0 } end = end + matchPos + afterContext if end > len(output) { end = len(output) } mgr.saveCrasher(vmCfg.Name, desc, output[start:end]) } if len(output) > 2*beforeContext { copy(output, output[len(output)-beforeContext:]) output = output[:beforeContext] } matchPos = len(output) - 128 if matchPos < 0 { matchPos = 0 } case <-ticker.C: mgr.saveCrasher(vmCfg.Name, "no output", output) return true } } }
func (mgr *Manager) runInstance(vmCfg *vm.Config, first bool) bool { inst, err := vm.Create(mgr.cfg.Type, vmCfg) if err != nil { logf(0, "failed to create instance: %v", err) return false } defer inst.Close() fwdAddr, err := inst.Forward(mgr.port) if err != nil { logf(0, "failed to setup port forwarding: %v", err) return false } fuzzerBin, err := inst.Copy(filepath.Join(mgr.cfg.Syzkaller, "bin", "syz-fuzzer")) if err != nil { logf(0, "failed to copy binary: %v", err) return false } executorBin, err := inst.Copy(filepath.Join(mgr.cfg.Syzkaller, "bin", "syz-executor")) if err != nil { logf(0, "failed to copy binary: %v", err) return false } // TODO: this should be present in the image. _, errc, err := inst.Run(10*time.Second, "echo -n 0 > /proc/sys/debug/exception-trace") if err == nil { <-errc } // Leak detection significantly slows down fuzzing, so detect leaks only on the first instance. leak := first && mgr.cfg.Leak // Run the fuzzer binary. outputC, errorC, err := inst.Run(time.Hour, fmt.Sprintf("%v -executor %v -name %v -manager %v -output=%v -procs %v -leak=%v -cover=%v -nobody=%v -v %d", fuzzerBin, executorBin, vmCfg.Name, fwdAddr, mgr.cfg.Output, mgr.cfg.Procs, leak, mgr.cfg.Cover, mgr.cfg.DropPrivs, *flagV)) if err != nil { logf(0, "failed to run fuzzer: %v", err) return false } startTime := time.Now() var crashes []string saveCrasher := func(what string, output []byte) { for _, re := range mgr.suppressions { if re.Match(output) { logf(1, "%v: suppressing '%v' with '%v'", vmCfg.Name, what, re.String()) return } } buf := new(bytes.Buffer) fmt.Fprintf(buf, "\n\n") if len(crashes) != 0 { fmt.Fprintf(buf, "previous crashes:\n") for _, c := range crashes { fmt.Fprintf(buf, "\t%s\n", c) } } crashes = append(crashes, what) fmt.Fprintf(buf, "after running for %v:\n", time.Since(startTime)) fmt.Fprintf(buf, "%v\n", what) output = append([]byte{}, output...) output = append(output, buf.Bytes()...) filename := fmt.Sprintf("crash-%v-%v", vmCfg.Name, time.Now().UnixNano()) logf(0, "%v: saving crash '%v' to %v", vmCfg.Name, what, filename) ioutil.WriteFile(filepath.Join(mgr.crashdir, filename), output, 0660) } var output []byte matchPos := 0 const ( beforeContext = 256 << 10 afterContext = 128 << 10 ) lastExecuteTime := time.Now() ticker := time.NewTimer(time.Minute) for { if !ticker.Reset(time.Minute) { <-ticker.C } select { case err := <-errorC: switch err { case vm.TimeoutErr: logf(0, "%v: running long enough, restarting", vmCfg.Name) return true default: logf(0, "%v: lost connection: %v", vmCfg.Name, err) saveCrasher("lost connection", output) return true } case out := <-outputC: output = append(output, out...) if bytes.Index(output[matchPos:], []byte("executing program")) != -1 { lastExecuteTime = time.Now() } if _, _, _, found := vm.FindCrash(output[matchPos:]); found { // Give it some time to finish writing the error message. timer := time.NewTimer(10 * time.Second).C loop: for { select { case out = <-outputC: output = append(output, out...) case <-timer: break loop } } desc, start, end, _ := vm.FindCrash(output[matchPos:]) start = start + matchPos - beforeContext if start < 0 { start = 0 } end = end + matchPos + afterContext if end > len(output) { end = len(output) } saveCrasher(desc, output[start:end]) } if len(output) > 2*beforeContext { copy(output, output[len(output)-beforeContext:]) output = output[:beforeContext] } matchPos = len(output) - 128 if matchPos < 0 { matchPos = 0 } // In some cases kernel constantly prints something to console, // but fuzzer is not actually executing programs. if mgr.cfg.Type != "local" && time.Since(lastExecuteTime) > 3*time.Minute { saveCrasher("not executing programs", output) return true } case <-ticker.C: if mgr.cfg.Type != "local" { saveCrasher("no output", output) return true } } } }
func main() { flag.Parse() cfg, _, _, err := config.Parse(*flagConfig) if err != nil { log.Fatalf("%v", err) } if *flagCount > 0 { cfg.Count = *flagCount } if _, err := os.Stat(filepath.Join(cfg.Syzkaller, "bin/syz-execprog")); err != nil { log.Fatalf("bin/syz-execprog is missing (run 'make execprog')") } if len(flag.Args()) != 1 { log.Fatalf("usage: syz-repro -config=config.file execution.log") } data, err := ioutil.ReadFile(flag.Args()[0]) if err != nil { log.Fatalf("failed to open log file: %v", err) } entries := prog.ParseLog(data) log.Printf("parsed %v programs", len(entries)) crashDesc, crashStart, _, found := vm.FindCrash(data) if !found { log.Fatalf("can't find crash message in the log") } log.Printf("target crash: '%s'", crashDesc) instances = make(chan VM, cfg.Count) bootRequests = make(chan bool, cfg.Count) for i := 0; i < cfg.Count; i++ { bootRequests <- true go func() { for range bootRequests { vmCfg, err := config.CreateVMConfig(cfg) if err != nil { log.Fatalf("failed to create VM config: %v", err) } inst, err := vm.Create(cfg.Type, vmCfg) if err != nil { log.Fatalf("failed to create VM: %v", err) } execprogBin, err := inst.Copy(filepath.Join(cfg.Syzkaller, "bin/syz-execprog")) if err != nil { log.Fatalf("failed to copy to VM: %v", err) } executorBin, err := inst.Copy(filepath.Join(cfg.Syzkaller, "bin/syz-executor")) if err != nil { log.Fatalf("failed to copy to VM: %v", err) } instances <- VM{inst, execprogBin, executorBin} } }() } repro(cfg, entries, crashStart) for { select { case inst := <-instances: inst.Close() default: return } } }