Esempio n. 1
0
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
		}
	}
}
Esempio n. 2
0
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
		}
	}
}
Esempio n. 3
0
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
			}
		}
	}
}
Esempio n. 4
0
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
		}
	}
}