Beispiel #1
0
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)
		}
	}
}
Beispiel #2
0
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)
}
Beispiel #3
0
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)
	}
}
Beispiel #4
0
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)
}
Beispiel #5
0
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)
}
Beispiel #6
0
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
}
Beispiel #7
0
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)
		}
	}
}