// Copyright 2016 syzkaller project authors. All rights reserved. // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. // syz-crush replays crash log on multiple VMs. Usage: // syz-crush -config=config.file execution.log // Intended for reproduction of particularly elusive crashes. package main import ( "flag" "io/ioutil" "sync" "sync/atomic" "time" "github.com/google/syzkaller/pkg/instance" "github.com/google/syzkaller/pkg/log" "github.com/google/syzkaller/pkg/mgrconfig" "github.com/google/syzkaller/pkg/osutil" "github.com/google/syzkaller/pkg/report" "github.com/google/syzkaller/prog" "github.com/google/syzkaller/vm" ) var ( flagConfig = flag.String("config", "", "configuration file") ) func main() { flag.Parse() cfg, err := mgrconfig.LoadFile(*flagConfig) if err != nil { log.Fatalf("%v", err) } if len(flag.Args()) != 1 { log.Fatalf("usage: syz-crush -config=config.file execution.log") } if _, err := prog.GetTarget(cfg.TargetOS, cfg.TargetArch); err != nil { log.Fatalf("%v", err) } vmPool, err := vm.Create(cfg, false) if err != nil { log.Fatalf("%v", err) } reporter, err := report.NewReporter(cfg) if err != nil { log.Fatalf("%v", err) } log.Logf(0, "booting test machines...") var shutdown uint32 var wg sync.WaitGroup wg.Add(vmPool.Count() + 1) for i := 0; i < vmPool.Count(); i++ { i := i go func() { defer wg.Done() for { runInstance(cfg, reporter, vmPool, i) if atomic.LoadUint32(&shutdown) != 0 { break } } }() } shutdownC := make(chan struct{}) osutil.HandleInterrupts(shutdownC) go func() { <-shutdownC wg.Done() atomic.StoreUint32(&shutdown, 1) }() wg.Wait() } func runInstance(cfg *mgrconfig.Config, reporter report.Reporter, vmPool *vm.Pool, index int) { inst, err := vmPool.Create(index) if err != nil { log.Logf(0, "failed to create instance: %v", err) return } defer inst.Close() execprogBin, err := inst.Copy(cfg.SyzExecprogBin) if err != nil { log.Logf(0, "failed to copy execprog: %v", err) return } executorBin, err := inst.Copy(cfg.SyzExecutorBin) if err != nil { log.Logf(0, "failed to copy executor: %v", err) return } logFile, err := inst.Copy(flag.Args()[0]) if err != nil { log.Logf(0, "failed to copy log: %v", err) return } cmd := instance.ExecprogCmd(execprogBin, executorBin, cfg.TargetOS, cfg.TargetArch, cfg.Sandbox, true, true, true, cfg.Procs, -1, -1, logFile) outc, errc, err := inst.Run(time.Hour, nil, cmd) if err != nil { log.Logf(0, "failed to run execprog: %v", err) return } log.Logf(0, "vm-%v: crushing...", index) rep := inst.MonitorExecution(outc, errc, reporter, false) if rep == nil { // This is the only "OK" outcome. log.Logf(0, "vm-%v: running long enough, restarting", index) } else { f, err := ioutil.TempFile(".", "syz-crush") if err != nil { log.Logf(0, "failed to create temp file: %v", err) return } defer f.Close() log.Logf(0, "vm-%v: crashed: %v, saving to %v", index, rep.Title, f.Name()) f.Write(rep.Output) } }