func main() { flag.Parse() cfg, syscalls := parseConfig() params, err := json.Marshal(cfg.Params) if err != nil { fatalf("failed to marshal config params: %v", err) } enabledSyscalls := "" if len(syscalls) != 0 { buf := new(bytes.Buffer) for c := range syscalls { fmt.Fprintf(buf, ",%v", c) } enabledSyscalls = buf.String()[1:] } vmCfg := &vm.Config{ Workdir: cfg.Workdir, ManagerPort: cfg.Port, Params: params, EnabledSyscalls: enabledSyscalls, NoCover: cfg.Nocover, } var instances []vm.Instance for i := 0; i < cfg.Count; i++ { inst, err := vm.Create(cfg.Type, vmCfg, i) if err != nil { fatalf("failed to create an instance: %v", err) } instances = append(instances, inst) } RunManager(cfg, syscalls, instances) }
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 } // Leak detection significantly slows down fuzzing, so detect leaks only on the first instance. leak := first && mgr.cfg.Leak fuzzerV := 0 if *flagDebug { fuzzerV = 100 } // Run the fuzzer binary. outc, errc, err := inst.Run(time.Hour, fmt.Sprintf( "%v -executor=%v -name=%v -manager=%v -output=%v -procs=%v -leak=%v -cover=%v -sandbox=%v -debug=%v -v=%d", fuzzerBin, executorBin, vmCfg.Name, fwdAddr, mgr.cfg.Output, mgr.cfg.Procs, leak, mgr.cfg.Cover, mgr.cfg.Sandbox, *flagDebug, fuzzerV)) if err != nil { Logf(0, "failed to run fuzzer: %v", err) return false } desc, text, output, crashed, timedout := vm.MonitorExecution(outc, errc, mgr.cfg.Type == "local", true) if timedout { // This is the only "OK" outcome. Logf(0, "%v: running long enough, restarting", vmCfg.Name) } else { if !crashed { // syz-fuzzer exited, but it should not. desc = "lost connection to test machine" } mgr.saveCrasher(vmCfg, desc, text, output) } return true }
func runInstance(cfg *config.Config, vmCfg *vm.Config) { inst, err := vm.Create(cfg.Type, vmCfg) if err != nil { Logf(0, "failed to create instance: %v", err) return } defer inst.Close() execprogBin, err := inst.Copy(filepath.Join(cfg.Syzkaller, "bin", "syz-execprog")) if err != nil { Logf(0, "failed to copy execprog: %v", err) return } executorBin, err := inst.Copy(filepath.Join(cfg.Syzkaller, "bin", "syz-executor")) if err != nil { Logf(0, "failed to copy executor: %v", err) return } logFile, err := inst.Copy(flag.Args()[0]) if err != nil { Logf(0, "failed to copy log: %v", err) return } cmd := fmt.Sprintf("%v -executor=%v -repeat=0 -procs=%v -cover=0 -sandbox=%v %v", execprogBin, executorBin, cfg.Procs, cfg.Sandbox, logFile) outc, errc, err := inst.Run(time.Hour, nil, cmd) if err != nil { Logf(0, "failed to run execprog: %v", err) return } Logf(0, "%v: crushing...", vmCfg.Name) desc, _, output, crashed, timedout := vm.MonitorExecution(outc, errc, cfg.Type == "local", true, cfg.ParsedIgnores) if timedout { // This is the only "OK" outcome. Logf(0, "%v: running long enough, restarting", vmCfg.Name) } else { if !crashed { // syz-execprog exited, but it should not. desc = "lost connection to test machine" } f, err := ioutil.TempFile(".", "syz-crush") if err != nil { Logf(0, "failed to create temp file: %v", err) return } defer f.Close() Logf(0, "%v: crashed: %v, saving to %v", vmCfg.Name, desc, f.Name()) f.Write(output) } return }
func (mgr *Manager) runInstance(vmCfg *vm.Config, first bool) (*Crash, error) { inst, err := vm.Create(mgr.cfg.Type, vmCfg) if err != nil { return nil, fmt.Errorf("failed to create instance: %v", err) } defer inst.Close() fwdAddr, err := inst.Forward(mgr.port) if err != nil { return nil, fmt.Errorf("failed to setup port forwarding: %v", err) } fuzzerBin, err := inst.Copy(filepath.Join(mgr.cfg.Syzkaller, "bin", "syz-fuzzer")) if err != nil { return nil, fmt.Errorf("failed to copy binary: %v", err) } executorBin, err := inst.Copy(filepath.Join(mgr.cfg.Syzkaller, "bin", "syz-executor")) if err != nil { return nil, fmt.Errorf("failed to copy binary: %v", err) } // Leak detection significantly slows down fuzzing, so detect leaks only on the first instance. leak := first && mgr.cfg.Leak fuzzerV := 0 procs := mgr.cfg.Procs if *flagDebug { fuzzerV = 100 procs = 1 } // Run the fuzzer binary. start := time.Now() cmd := fmt.Sprintf("%v -executor=%v -name=%v -manager=%v -output=%v -procs=%v -leak=%v -cover=%v -sandbox=%v -debug=%v -v=%d", fuzzerBin, executorBin, vmCfg.Name, fwdAddr, mgr.cfg.Output, procs, leak, mgr.cfg.Cover, mgr.cfg.Sandbox, *flagDebug, fuzzerV) outc, errc, err := inst.Run(time.Hour, mgr.vmStop, cmd) if err != nil { return nil, fmt.Errorf("failed to run fuzzer: %v", err) } desc, text, output, crashed, timedout := vm.MonitorExecution(outc, errc, mgr.cfg.Type == "local", true, mgr.cfg.ParsedIgnores) if timedout { // This is the only "OK" outcome. Logf(0, "%v: running for %v, restarting (%v)", vmCfg.Name, time.Since(start), desc) return nil, nil } if !crashed { // syz-fuzzer exited, but it should not. desc = "lost connection to test machine" } return &Crash{vmCfg.Name, desc, text, output}, nil }
func main() { flag.Parse() cfg, syscalls := parseConfig() params, err := json.Marshal(cfg.Params) if err != nil { fatalf("failed to marshal config params: %v", err) } enabledSyscalls := "" if len(syscalls) != 0 { buf := new(bytes.Buffer) for c := range syscalls { fmt.Fprintf(buf, ",%v", c) } enabledSyscalls = buf.String()[1:] logf(1, "enabled syscalls: %v", enabledSyscalls) } vmCfg := &vm.Config{ Workdir: cfg.Workdir, ManagerPort: cfg.Port, Params: params, EnabledSyscalls: enabledSyscalls, NoCover: cfg.Nocover, } // Add some builtin suppressions. cfg.Suppressions = append(cfg.Suppressions, []string{ "panic: failed to start executor binary", "panic: executor failed: pthread_create failed", "panic: failed to create temp dir", "Out of memory: Kill process .* \\(syzkaller_fuzze\\)", }...) for _, s := range cfg.Suppressions { re, err := regexp.Compile(s) if err != nil { fatalf("failed to compile suppression '%v': %v", s, err) } vmCfg.Suppressions = append(vmCfg.Suppressions, re) } var instances []vm.Instance for i := 0; i < cfg.Count; i++ { inst, err := vm.Create(cfg.Type, vmCfg, i) if err != nil { fatalf("failed to create an instance: %v", err) } instances = append(instances, inst) } RunManager(cfg, syscalls, instances) }
func main() { flag.Parse() cfg, syscalls := parseConfig() var instances []vm.Instance for i := 0; i < cfg.Count; i++ { params, err := json.Marshal(cfg.Params) if err != nil { fatalf("failed to marshal config params: %v", err) } inst, err := vm.Create(cfg.Type, cfg.Workdir, syscalls, cfg.Port, i, params) if err != nil { fatalf("failed to create an instance: %v", err) } instances = append(instances, inst) } RunManager(cfg, syscalls, instances) }
func main() { flag.Parse() cfg, _, _, err := config.Parse(*flagConfig) if err != nil { log.Fatalf("%v", err) } if *flagCount > 0 { cfg.Count = *flagCount } 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)) crashLoc := vm.CrashRe.FindIndex(data) if crashLoc == nil { log.Fatalf("can't find crash message in the log") } log.Printf("target crash: '%s'", data[crashLoc[0]:crashLoc[1]]) 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, crashLoc) for { select { case inst := <-instances: inst.Close() default: return } } }
func Run(crashLog []byte, cfg *config.Config, vmIndexes []int) (*Result, error) { if len(vmIndexes) == 0 { return nil, fmt.Errorf("no VMs provided") } if _, err := os.Stat(filepath.Join(cfg.Syzkaller, "bin/syz-execprog")); err != nil { return nil, fmt.Errorf("bin/syz-execprog is missing (run 'make execprog')") } entries := prog.ParseLog(crashLog) if len(entries) == 0 { return nil, fmt.Errorf("crash log does not contain any programs") } crashDesc, _, crashStart, _ := report.Parse(crashLog, cfg.ParsedIgnores) if crashDesc == "" { crashStart = len(crashLog) // assuming VM hanged crashDesc = "hang" } Logf(0, "reproducing crash '%v': %v programs, %v VMs", crashDesc, len(entries), len(vmIndexes)) ctx := &context{ cfg: cfg, crashDesc: crashDesc, instances: make(chan *instance, len(vmIndexes)), bootRequests: make(chan int, len(vmIndexes)), } var wg sync.WaitGroup wg.Add(len(vmIndexes)) for _, vmIndex := range vmIndexes { ctx.bootRequests <- vmIndex go func() { defer wg.Done() for vmIndex := range ctx.bootRequests { var inst *instance for try := 0; try < 3; try++ { vmCfg, err := config.CreateVMConfig(cfg, vmIndex) if err != nil { Logf(0, "reproducing crash '%v': failed to create VM config: %v", crashDesc, err) time.Sleep(10 * time.Second) continue } vmInst, err := vm.Create(cfg.Type, vmCfg) if err != nil { Logf(0, "reproducing crash '%v': failed to create VM: %v", crashDesc, err) time.Sleep(10 * time.Second) continue } execprogBin, err := vmInst.Copy(filepath.Join(cfg.Syzkaller, "bin/syz-execprog")) if err != nil { Logf(0, "reproducing crash '%v': failed to copy to VM: %v", crashDesc, err) vmInst.Close() time.Sleep(10 * time.Second) continue } executorBin, err := vmInst.Copy(filepath.Join(cfg.Syzkaller, "bin/syz-executor")) if err != nil { Logf(0, "reproducing crash '%v': failed to copy to VM: %v", crashDesc, err) vmInst.Close() time.Sleep(10 * time.Second) continue } inst = &instance{vmInst, vmIndex, execprogBin, executorBin} break } if inst == nil { break } ctx.instances <- inst } }() } go func() { wg.Wait() close(ctx.instances) }() res, err := ctx.repro(entries, crashStart) close(ctx.bootRequests) for inst := range ctx.instances { inst.Close() } return res, err }
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 { Fatalf("%v", err) } 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 len(flag.Args()) != 1 { Fatalf("usage: syz-repro -config=config.file execution.log") } data, err := ioutil.ReadFile(flag.Args()[0]) 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} } }() } go func() { c := make(chan os.Signal, 2) signal.Notify(c, syscall.SIGINT) <-c close(shutdown) Logf(-1, "shutting down...") <-c Fatalf("terminating") }() repro(cfg, entries, crashStart) exit() }