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 { for i := 0; i < maxReproAttempts; i++ { name := filepath.Join(dir, fmt.Sprintf("repro%v", i)) if _, err := os.Stat(name); err != nil { ioutil.WriteFile(name, nil, 0660) break } } return } opts := fmt.Sprintf("# %+v\n", res.Opts) prog := res.Prog.Serialize() ioutil.WriteFile(filepath.Join(dir, "repro.prog"), append([]byte(opts), prog...), 0660) if len(mgr.cfg.Tag) > 0 { ioutil.WriteFile(filepath.Join(dir, "repro.tag"), []byte(mgr.cfg.Tag), 0660) } if len(crash.text) > 0 { ioutil.WriteFile(filepath.Join(dir, "repro.report"), []byte(crash.text), 0660) } if res.CRepro { cprog, err := csource.Write(res.Prog, res.Opts) if err == nil { formatted, err := csource.Format(cprog) if err == nil { cprog = formatted } ioutil.WriteFile(filepath.Join(dir, "repro.cprog"), cprog, 0660) } else { Logf(0, "failed to write C source: %v", err) } } }
func main() { flag.Parse() if len(flag.Args()) != 1 { fmt.Fprintf(os.Stderr, "usage: prog2c [-threaded [-collide]] prog_file\n") os.Exit(1) } data, err := ioutil.ReadFile(flag.Args()[0]) if err != nil { fmt.Fprintf(os.Stderr, "failed to read prog file: %v\n", err) os.Exit(1) } p, err := prog.Deserialize(data) if err != nil { fmt.Fprintf(os.Stderr, "failed to deserialize the program: %v\n", err) os.Exit(1) } opts := csource.Options{ Threaded: *flagThreaded, Collide: *flagCollide, } src := csource.Write(p, opts) if formatted, err := csource.Format(src); err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) } else { src = formatted } os.Stdout.Write(src) }
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 { Fatalf("%v", err) } if *flagCount > 0 { cfg.Count = *flagCount } if cfg.Count > 4 { cfg.Count = 4 } 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) } 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(vm.Shutdown) Logf(-1, "shutting down...") <-c Fatalf("terminating") }() res, err := repro.Run(data, cfg, vmIndexes) if err != nil { Logf(0, "reproduction failed: %v", err) } if res == nil { return } 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) } }
func main() { flag.Parse() if *flagProg == "" { flag.PrintDefaults() os.Exit(1) } data, err := ioutil.ReadFile(*flagProg) if err != nil { fmt.Fprintf(os.Stderr, "failed to read prog file: %v\n", err) os.Exit(1) } p, err := prog.Deserialize(data) if err != nil { fmt.Fprintf(os.Stderr, "failed to deserialize the program: %v\n", err) os.Exit(1) } opts := csource.Options{ Threaded: *flagThreaded, Collide: *flagCollide, Repeat: *flagRepeat, Procs: *flagProcs, Sandbox: *flagSandbox, Repro: false, } src, err := csource.Write(p, opts) if err != nil { fmt.Fprintf(os.Stderr, "failed to generate C spurce: %v\n", err) os.Exit(1) } if formatted, err := csource.Format(src); err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) } else { src = formatted } os.Stdout.Write(src) }
func repro(cfg *config.Config, entries []*prog.LogEntry, crashLoc []int) { // Cut programs that were executed after crash. for i, ent := range entries { if ent.Start > crashLoc[0] { 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. log.Printf("the suspected programs are:") for _, ent := range suspected { log.Printf("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 } } } if p == nil { log.Printf("no program crashed") return } log.Printf("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 } } src := csource.Write(p, opts) log.Printf("C source:\n%s\n", src) srcf, err := fileutil.WriteTempFile(src) if err != nil { log.Fatalf("%v", err) } bin, err := csource.Build(srcf) if err != nil { log.Fatalf("%v", err) } defer os.Remove(bin) testBin(cfg, bin) }
func (ctx *context) repro(entries []*prog.LogEntry, crashStart int) (*Result, error) { // 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]]) } Logf(2, "reproducing crash '%v': suspecting %v programs", ctx.crashDesc, len(suspected)) opts := csource.Options{ Threaded: true, Collide: true, Repeat: true, Procs: ctx.cfg.Procs, Sandbox: ctx.cfg.Sandbox, Repro: true, } // Execute the suspected programs. // We first try to execute each program for 10 seconds, that should detect simple crashes // (i.e. no races and no hangs). Then we execute each program for 5 minutes // to catch races and hangs. Note that the max duration must be larger than // hang/no output detection duration in vm.MonitorExecution, which is currently set to 3 mins. var res *Result var duration time.Duration for _, dur := range []time.Duration{10 * time.Second, 5 * time.Minute} { for _, ent := range suspected { crashed, err := ctx.testProg(ent.P, dur, opts, true) if err != nil { return nil, err } if crashed { res = &Result{ Prog: ent.P, Opts: opts, } duration = dur * 3 / 2 break } } if res != nil { break } } if res == nil { Logf(0, "reproducing crash '%v': no program crashed", ctx.crashDesc) return nil, nil } defer func() { res.Opts.Repro = false }() Logf(2, "reproducing crash '%v': minimizing guilty program", ctx.crashDesc) res.Prog, _ = prog.Minimize(res.Prog, -1, func(p1 *prog.Prog, callIndex int) bool { crashed, err := ctx.testProg(p1, duration, res.Opts, false) if err != nil { Logf(1, "reproducing crash '%v': minimization failed with %v", ctx.crashDesc, err) return false } return crashed }, true) // Try to "minimize" threaded/collide/sandbox/etc to find simpler reproducer. opts = res.Opts opts.Collide = false crashed, err := ctx.testProg(res.Prog, duration, opts, false) if err != nil { return res, err } if crashed { res.Opts = opts opts.Threaded = false crashed, err := ctx.testProg(res.Prog, duration, opts, false) if err != nil { return res, err } if crashed { res.Opts = opts } } if res.Opts.Sandbox == "namespace" { opts = res.Opts opts.Sandbox = "none" crashed, err := ctx.testProg(res.Prog, duration, opts, false) if err != nil { return res, err } if crashed { res.Opts = opts } } if res.Opts.Procs > 1 { opts = res.Opts opts.Procs = 1 crashed, err := ctx.testProg(res.Prog, duration, opts, false) if err != nil { return res, err } if crashed { res.Opts = opts } } if res.Opts.Repeat { opts = res.Opts opts.Repeat = false crashed, err := ctx.testProg(res.Prog, duration, opts, false) if err != nil { return res, err } if crashed { res.Opts = opts } } src, err := csource.Write(res.Prog, res.Opts) if err != nil { return res, err } srcf, err := fileutil.WriteTempFile(src) if err != nil { return res, err } bin, err := csource.Build(srcf) if err != nil { return res, err } defer os.Remove(bin) crashed, err = ctx.testBin(bin, duration, false) if err != nil { return res, err } res.CRepro = crashed return res, nil }
func TestCompare(t *testing.T) { t.Skip("flaky") bin := buildExecutor(t) defer os.Remove(bin) // Sequence of syscalls that statically linked libc produces on startup. rawTracePrefix := []string{"execve", "uname", "brk", "brk", "arch_prctl", "readlink", "brk", "brk", "access"} executorTracePrefix := []string{"execve", "uname", "brk", "brk", "arch_prctl", "set_tid_address", "set_robust_list", "futex", "rt_sigaction", "rt_sigaction", "rt_sigprocmask", "getrlimit", "readlink", "brk", "brk", "access", "mmap", "mmap"} // These calls produce non-deterministic results, ignore them. nondet := []string{"getrusage", "msgget", "msgrcv", "msgsnd", "shmget", "semat", "io_setup", "getpgrp", "getpid", "getpgid", "getppid", "setsid", "ppoll", "keyctl", "ioprio_get", "move_pages", "kcmp"} env1, err := MakeEnv(bin, timeout, FlagStrace) if err != nil { t.Fatalf("failed to create env: %v", err) } defer env1.Close() rs, iters := initTest(t) for i := 0; i < iters; i++ { p := prog.Generate(rs, 10, nil) _, strace1, _, _, _, _, err := env1.Exec(p) if err != nil { t.Fatalf("failed to run executor: %v", err) } src := csource.Write(p, csource.Options{}) cprog := buildSource(t, src) defer os.Remove(cprog) env2, err := MakeEnv(cprog, timeout, FlagStrace) if err != nil { t.Fatalf("failed to create env: %v", err) } defer env2.Close() // yes, that's defer in a loop _, strace2, _, _, _, _, err := env2.Exec(nil) if err != nil { t.Fatalf("failed to run c binary: %v", err) } stripPrefix := func(data []byte, prefix []string) string { prefix0 := prefix buf := new(bytes.Buffer) s := bufio.NewScanner(bytes.NewReader(data)) for s.Scan() { if strings.HasPrefix(s.Text(), "--- SIG") { // Signal parameters can contain pid and pc. continue } if len(prefix) == 0 { skip := false for _, c := range nondet { if strings.HasPrefix(s.Text(), c) { skip = true break } } if skip { continue } buf.WriteString(s.Text()) buf.Write([]byte{'\n'}) continue } if !strings.HasPrefix(s.Text(), prefix[0]) { t.Fatalf("strace output does not start with expected prefix\ngot:\n%s\nexpect prefix: %+v\ncurrent call: %v", data, prefix0, prefix[0]) } prefix = prefix[1:] } if err := s.Err(); err != nil { t.Fatalf("failed to scan strace output: %v", err) } return buf.String() } s1 := stripPrefix(strace1, executorTracePrefix) s2 := stripPrefix(strace2, rawTracePrefix) if s1 == "" || s1 != s2 { t.Logf("program:\n%s\n", p.Serialize()) t.Fatalf("strace output differs:\n%s\n\n\n%s\n", s1, s2) } } }