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 main() { flag.Parse() if len(flag.Args()) == 0 { fmt.Fprintf(os.Stderr, "usage: execprog [flags] file-with-programs+\n") flag.PrintDefaults() os.Exit(1) } var progs []*prog.Prog for _, fn := range flag.Args() { data, err := ioutil.ReadFile(fn) if err != nil { log.Fatalf("failed to read log file: %v", err) } entries := prog.ParseLog(data) for _, ent := range entries { progs = append(progs, ent.P) } } log.Printf("parsed %v programs", len(progs)) if len(progs) == 0 { return } flags, timeout := ipc.DefaultFlags() if *flagCoverFile != "" { flags |= ipc.FlagCover flags &= ^ipc.FlagDedupCover } var wg sync.WaitGroup wg.Add(*flagProcs) var posMu sync.Mutex var pos int var lastPrint time.Time for p := 0; p < *flagProcs; p++ { go func() { env, err := ipc.MakeEnv(*flagExecutor, timeout, flags) if err != nil { log.Fatalf("failed to create ipc env: %v", err) } for { posMu.Lock() idx := pos pos++ if idx%len(progs) == 0 && time.Since(lastPrint) > 5*time.Second { log.Printf("executed %v programs\n", idx) lastPrint = time.Now() } posMu.Unlock() if *flagRepeat > 0 && idx >= len(progs)**flagRepeat { env.Close() wg.Done() return } p := progs[idx%len(progs)] output, cov, _, failed, hanged, err := env.Exec(p) if failed { fmt.Printf("BUG: executor-detected bug:\n%s", output) } if flags&ipc.FlagDebug != 0 || err != nil { fmt.Printf("result: failed=%v hanged=%v err=%v\n\n%s", failed, hanged, err, output) } if *flagCoverFile != "" { // Coverage is dumped in sanitizer format. // github.com/google/sanitizers/tools/sancov command can be used to dump PCs, // then they can be piped via addr2line to symbolize. for i, c := range cov { fmt.Printf("call #%v: coverage %v\n", i, len(c)) if len(c) == 0 { continue } buf := new(bytes.Buffer) binary.Write(buf, binary.LittleEndian, uint64(0xC0BFFFFFFFFFFF64)) for _, pc := range c { binary.Write(buf, binary.LittleEndian, cover.RestorePC(pc)) } err := ioutil.WriteFile(fmt.Sprintf("%v.%v", *flagCoverFile, i), buf.Bytes(), 0660) if err != nil { log.Fatalf("failed to write coverage file: %v", err) } } } } }() } wg.Wait() }
func main() { flag.Parse() if len(flag.Args()) == 0 { fmt.Fprintf(os.Stderr, "usage: execprog [flags] file-with-programs+\n") flag.PrintDefaults() os.Exit(1) } var progs []*prog.Prog for _, fn := range flag.Args() { data, err := ioutil.ReadFile(fn) if err != nil { Fatalf("failed to read log file: %v", err) } entries := prog.ParseLog(data) for _, ent := range entries { progs = append(progs, ent.P) } } Logf(0, "parsed %v programs", len(progs)) if len(progs) == 0 { return } flags, timeout, err := ipc.DefaultFlags() if err != nil { Fatalf("%v", err) } if *flagCoverFile != "" { flags |= ipc.FlagCover flags &= ^ipc.FlagDedupCover } var wg sync.WaitGroup wg.Add(*flagProcs) var posMu, logMu sync.Mutex gate := ipc.NewGate(2**flagProcs, nil) var pos int var lastPrint time.Time var shutdown uint32 for p := 0; p < *flagProcs; p++ { pid := p go func() { defer wg.Done() env, err := ipc.MakeEnv(*flagExecutor, timeout, flags, pid) if err != nil { Fatalf("failed to create ipc env: %v", err) } defer env.Close() for { if !func() bool { // Limit concurrency window. ticket := gate.Enter() defer gate.Leave(ticket) posMu.Lock() idx := pos pos++ if idx%len(progs) == 0 && time.Since(lastPrint) > 5*time.Second { Logf(0, "executed programs: %v", idx) lastPrint = time.Now() } posMu.Unlock() if *flagRepeat > 0 && idx >= len(progs)**flagRepeat { return false } p := progs[idx%len(progs)] switch *flagOutput { case "stdout": data := p.Serialize() logMu.Lock() Logf(0, "executing program %v:\n%s", pid, data) logMu.Unlock() } output, cov, _, failed, hanged, err := env.Exec(p) if atomic.LoadUint32(&shutdown) != 0 { return false } if failed { fmt.Printf("BUG: executor-detected bug:\n%s", output) } if flags&ipc.FlagDebug != 0 || err != nil { fmt.Printf("result: failed=%v hanged=%v err=%v\n\n%s", failed, hanged, err, output) } if *flagCoverFile != "" { // Coverage is dumped in sanitizer format. // github.com/google/sanitizers/tools/sancov command can be used to dump PCs, // then they can be piped via addr2line to symbolize. for i, c := range cov { fmt.Printf("call #%v: coverage %v\n", i, len(c)) if len(c) == 0 { continue } buf := new(bytes.Buffer) binary.Write(buf, binary.LittleEndian, uint64(0xC0BFFFFFFFFFFF64)) for _, pc := range c { binary.Write(buf, binary.LittleEndian, cover.RestorePC(pc, 0xffffffff)) } err := ioutil.WriteFile(fmt.Sprintf("%v.%v", *flagCoverFile, i), buf.Bytes(), 0660) if err != nil { Fatalf("failed to write coverage file: %v", err) } } } return true }() { return } } }() } go func() { c := make(chan os.Signal, 2) signal.Notify(c, syscall.SIGINT) <-c Logf(0, "shutting down...") atomic.StoreUint32(&shutdown, 1) <-c Fatalf("terminating") }() wg.Wait() }
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() }