From 62351e3ea53211036203a3cb5d4049a05f2f2eff Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Thu, 31 Dec 2015 13:47:20 +0100 Subject: [PATCH] fuzzer: allow to write programs to dmesg Mostly useful for local VM. --- config/config.go | 9 +++++++++ syz-fuzzer/fuzzer.go | 34 ++++++++++++++++++++++++---------- syz-manager/manager.go | 4 ++-- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/config/config.go b/config/config.go index 74b2a475..6ae3baa3 100644 --- a/config/config.go +++ b/config/config.go @@ -30,6 +30,7 @@ type Config struct { Port int // VM ssh port to use Bin string // qemu/lkvm binary name Debug bool // dump all VM output to console + Output string // one of stdout/dmesg/file (useful only for local VM) Syzkaller string // path to syzkaller checkout (syz-manager will look for binaries in bin subdir) Type string // VM type (qemu, kvm, local) @@ -84,6 +85,14 @@ func Parse(filename string) (*Config, map[int]bool, []*regexp.Regexp, error) { if cfg.Procs <= 0 { cfg.Procs = 1 } + if cfg.Output == "" { + cfg.Output = "stdout" + } + switch cfg.Output { + case "stdout", "dmesg", "file": + default: + return nil, nil, nil, fmt.Errorf("config param output must contain one of stdout/dmesg/file") + } syscalls, err := parseSyscalls(cfg) if err != nil { diff --git a/syz-fuzzer/fuzzer.go b/syz-fuzzer/fuzzer.go index faa8dde1..edc6a3b2 100644 --- a/syz-fuzzer/fuzzer.go +++ b/syz-fuzzer/fuzzer.go @@ -8,6 +8,7 @@ package main // i.e. aim at cracking new branches and triggering bugs in that new piece of code. import ( + "bytes" "crypto/sha1" "flag" "fmt" @@ -36,13 +37,13 @@ var ( flagExecutor = flag.String("executor", "", "path to executor binary") flagManager = flag.String("manager", "", "manager rpc address") flagStrace = flag.Bool("strace", false, "run executor under strace") - flagSaveProg = flag.Bool("saveprog", false, "save programs into local file before executing") flagSyscalls = flag.String("calls", "", "comma-delimited list of enabled syscall IDs (empty string for all syscalls)") flagNoCover = flag.Bool("nocover", false, "disable coverage collection/handling") flagDropPrivs = flag.Bool("dropprivs", true, "impersonate into nobody") 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") ) const ( @@ -92,6 +93,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") + os.Exit(1) + } logf(0, "started") corpusCover = make([]cover.Cover, sys.CallCount) @@ -193,7 +198,7 @@ func main() { var lastPoll time.Time var lastPrint time.Time for range time.NewTicker(3 * time.Second).C { - if *flagSaveProg && time.Since(lastPrint) > 10*time.Second { + if *flagOutput != "stdout" && time.Since(lastPrint) > 10*time.Second { // Keep-alive for manager. logf(0, "alive") lastPrint = time.Now() @@ -456,19 +461,28 @@ func execute1(pid int, env *ipc.Env, p *prog.Prog, stat *uint64) []cover.Cover { } }) - if *flagSaveProg { + // The following output helps to understand what program crashed kernel. + // It must not be intermixed. + switch *flagOutput { + case "stdout": + data := p.Serialize() + logMu.Lock() + log.Printf("executing program %v:\n%s", pid, data) + logMu.Unlock() + case "dmesg": + fd, err := syscall.Open("/dev/kmsg", syscall.O_WRONLY, 0) + if err == nil { + buf := new(bytes.Buffer) + fmt.Fprintf(buf, "syzkaller: executing program %v:\n%s", pid, p.Serialize()) + syscall.Write(fd, buf.Bytes()) + syscall.Close(fd) + } + case "file": f, err := os.Create(fmt.Sprintf("%v-%v.prog", *flagName, pid)) if err == nil { f.Write(p.Serialize()) f.Close() } - } else { - // The following output helps to understand what program crashed kernel. - // It must not be intermixed. - data := p.Serialize() - logMu.Lock() - log.Printf("executing program %v:\n%s", pid, data) - logMu.Unlock() } try := 0 diff --git a/syz-manager/manager.go b/syz-manager/manager.go index f422c132..50e8d172 100644 --- a/syz-manager/manager.go +++ b/syz-manager/manager.go @@ -181,8 +181,8 @@ func (mgr *Manager) runInstance(vmCfg *vm.Config, first bool) bool { // 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("/syz-fuzzer -name %v -executor /syz-executor -manager %v:%v -procs %v -leak=%v %v %v %v", - vmCfg.Name, inst.HostAddr(), mgr.port, mgr.cfg.Procs, leak, cover, dropprivs, calls)) + outputC, errorC, err := inst.Run(time.Hour, fmt.Sprintf("/syz-fuzzer -name %v -executor /syz-executor -manager %v:%v -output=%v -procs %v -leak=%v %v %v %v", + vmCfg.Name, inst.HostAddr(), mgr.port, mgr.cfg.Output, mgr.cfg.Procs, leak, cover, dropprivs, calls)) if err != nil { logf(0, "failed to run fuzzer: %v", err) return false