aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2016-02-16 15:55:44 +0100
committerDmitry Vyukov <dvyukov@google.com>2016-02-16 15:55:44 +0100
commit68c7a49a96b970897db5f61dd618382b5fe18ec0 (patch)
tree80cf2cb6c59f360ab0e9a7141c9d15aae2b35bcc
parent9724efa3358566378773cef6e72026f89c007930 (diff)
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.
-rw-r--r--config/config.go10
-rw-r--r--syz-fuzzer/fuzzer.go19
-rw-r--r--syz-manager/manager.go8
-rw-r--r--vm/local/local.go65
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
}