From 68c7a49a96b970897db5f61dd618382b5fe18ec0 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Tue, 16 Feb 2016 15:55:44 +0100 Subject: fuzzer: warn about absent kcov/kmemleak files Currently the failure mode is too obscure, so warn explicitly about these common cases. Also make local mode more usable. --- config/config.go | 10 +++++--- syz-fuzzer/fuzzer.go | 19 +++++++++++---- syz-manager/manager.go | 8 ++++--- vm/local/local.go | 65 +++++++++++++++++++++++++++++++++++++------------- 4 files changed, 75 insertions(+), 27 deletions(-) diff --git a/config/config.go b/config/config.go index f648795c1..073d34b41 100644 --- a/config/config.go +++ b/config/config.go @@ -87,12 +87,16 @@ func Parse(filename string) (*Config, map[int]bool, []*regexp.Regexp, error) { cfg.Procs = 1 } if cfg.Output == "" { - cfg.Output = "stdout" + if cfg.Type == "local" { + cfg.Output = "none" + } else { + cfg.Output = "stdout" + } } switch cfg.Output { - case "stdout", "dmesg", "file": + case "none", "stdout", "dmesg", "file": default: - return nil, nil, nil, fmt.Errorf("config param output must contain one of stdout/dmesg/file") + return nil, nil, nil, fmt.Errorf("config param output must contain one of none/stdout/dmesg/file") } syscalls, err := parseSyscalls(cfg) diff --git a/syz-fuzzer/fuzzer.go b/syz-fuzzer/fuzzer.go index c4f941d49..7942a6cab 100644 --- a/syz-fuzzer/fuzzer.go +++ b/syz-fuzzer/fuzzer.go @@ -40,7 +40,7 @@ var ( flagProcs = flag.Int("procs", 1, "number of parallel test processes") flagLeak = flag.Bool("leak", false, "detect memory leaks") flagV = flag.Int("v", 0, "verbosity") - flagOutput = flag.String("output", "stdout", "write programs to stdout/dmesg/file") + flagOutput = flag.String("output", "stdout", "write programs to none/stdout/dmesg/file") ) const ( @@ -91,8 +91,10 @@ var ( func main() { debug.SetGCPercent(50) flag.Parse() - if *flagOutput != "stdout" && *flagOutput != "dmesg" && *flagOutput != "file" { - fmt.Fprintf(os.Stderr, "-output flag must be one of stdout/dmesg/file\n") + switch *flagOutput { + case "none", "stdout", "dmesg", "file": + default: + fmt.Fprintf(os.Stderr, "-output flag must be one of none/stdout/dmesg/file\n") os.Exit(1) } logf(0, "started") @@ -119,6 +121,13 @@ func main() { flags, timeout := ipc.DefaultFlags() noCover = flags&ipc.FlagCover == 0 + if !noCover { + fd, err := syscall.Open("/sys/kernel/debug/kcov", syscall.O_RDWR, 0) + if err != nil { + log.Fatalf("BUG: /sys/kernel/debug/kcov is missing (%v). Enable CONFIG_KCOV and mount debugfs.", err) + } + syscall.Close(fd) + } gate = ipc.NewGate(2 * *flagProcs) envs := make([]*ipc.Env, *flagProcs) for pid := 0; pid < *flagProcs; pid++ { @@ -448,6 +457,8 @@ func execute1(pid int, env *ipc.Env, p *prog.Prog, stat *uint64) []cover.Cover { // The following output helps to understand what program crashed kernel. // It must not be intermixed. switch *flagOutput { + case "none": + // This case intentionally left blank. case "stdout": data := p.Serialize() logMu.Lock() @@ -507,7 +518,7 @@ func kmemleakInit() { fd, err := syscall.Open("/sys/kernel/debug/kmemleak", syscall.O_RDWR, 0) if err != nil { if *flagLeak { - panic(err) + log.Fatalf("BUG: /sys/kernel/debug/kmemleak is missing (%v). Enable CONFIG_KMEMLEAK and mount debugfs.", err) } else { return } diff --git a/syz-manager/manager.go b/syz-manager/manager.go index f9aa08b00..ffb184936 100644 --- a/syz-manager/manager.go +++ b/syz-manager/manager.go @@ -302,13 +302,15 @@ func (mgr *Manager) runInstance(vmCfg *vm.Config, first bool) bool { } // In some cases kernel constantly prints something to console, // but fuzzer is not actually executing programs. - if time.Since(lastExecuteTime) > 3*time.Minute { + if mgr.cfg.Type != "local" && time.Since(lastExecuteTime) > 3*time.Minute { saveCrasher("not executing programs", output) return true } case <-ticker.C: - saveCrasher("no output", output) - return true + if mgr.cfg.Type != "local" { + saveCrasher("no output", output) + return true + } } } } diff --git a/vm/local/local.go b/vm/local/local.go index 08a72a39f..e40b378ae 100644 --- a/vm/local/local.go +++ b/vm/local/local.go @@ -64,48 +64,79 @@ func (inst *instance) Copy(hostSrc string) (string, error) { } func (inst *instance) Run(timeout time.Duration, command string) (<-chan []byte, <-chan error, error) { + rpipe, wpipe, err := os.Pipe() + if err != nil { + return nil, nil, fmt.Errorf("failed to create pipe: %v", err) + } + for sz := 128 << 10; sz <= 2<<20; sz *= 2 { + syscall.Syscall(syscall.SYS_FCNTL, wpipe.Fd(), syscall.F_SETPIPE_SZ, uintptr(sz)) + } for strings.Index(command, " ") != -1 { command = strings.Replace(command, " ", " ", -1) } args := strings.Split(command, " ") cmd := exec.Command(args[0], args[1:]...) - if inst.cfg.Debug { - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stdout - } + cmd.Stdout = wpipe + cmd.Stderr = wpipe if err := cmd.Start(); err != nil { + rpipe.Close() + wpipe.Close() return nil, nil, err } + wpipe.Close() outputC := make(chan []byte, 10) - errorC := make(chan error, 2) + errorC := make(chan error, 1) done := make(chan bool) + signal := func(err error) { + time.Sleep(3 * time.Second) // wait for any pending output + select { + case errorC <- err: + default: + } + } go func() { - errorC <- cmd.Wait() + var buf [64 << 10]byte + var output []byte + for { + n, err := rpipe.Read(buf[:]) + if n != 0 { + if inst.cfg.Debug { + os.Stdout.Write(buf[:n]) + os.Stdout.Write([]byte{'\n'}) + } + output = append(output, buf[:n]...) + select { + case outputC <- output: + output = nil + default: + } + time.Sleep(time.Millisecond) + } + if err != nil { + rpipe.Close() + return + } + } + }() + go func() { + err := cmd.Wait() + signal(err) close(done) }() go func() { - ticker := time.NewTicker(time.Second) timeout := time.NewTicker(timeout) for { select { - case <-ticker.C: - select { - case outputC <- []byte{'.'}: - default: - } case <-timeout.C: - errorC <- vm.TimeoutErr + signal(vm.TimeoutErr) cmd.Process.Kill() - ticker.Stop() return case <-done: - ticker.Stop() timeout.Stop() return case <-inst.closed: - errorC <- fmt.Errorf("closed") + signal(fmt.Errorf("closed")) cmd.Process.Kill() - ticker.Stop() timeout.Stop() return } -- cgit mrf-deployment