1// Copyright 2018 syzkaller project authors. All rights reserved. 2// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4// Runtest runs syzkaller test programs in sys/*/test/*. Start as: 5// $ syz-runtest -config manager.config 6// Also see pkg/runtest docs. 7package main 8 9import ( 10 "errors" 11 "flag" 12 "fmt" 13 "io/ioutil" 14 "log" 15 "net" 16 "os" 17 "path/filepath" 18 "sync" 19 "time" 20 21 "github.com/google/syzkaller/pkg/instance" 22 "github.com/google/syzkaller/pkg/mgrconfig" 23 "github.com/google/syzkaller/pkg/osutil" 24 "github.com/google/syzkaller/pkg/report" 25 "github.com/google/syzkaller/pkg/rpctype" 26 "github.com/google/syzkaller/pkg/runtest" 27 "github.com/google/syzkaller/prog" 28 "github.com/google/syzkaller/sys" 29 "github.com/google/syzkaller/vm" 30) 31 32var ( 33 flagConfig = flag.String("config", "", "manager config") 34 flagDebug = flag.Bool("debug", false, "debug mode") 35) 36 37func main() { 38 flag.Parse() 39 cfg, err := mgrconfig.LoadFile(*flagConfig) 40 if err != nil { 41 log.Fatal(err) 42 } 43 target, err := prog.GetTarget(cfg.TargetOS, cfg.TargetArch) 44 if err != nil { 45 log.Fatal(err) 46 } 47 vmPool, err := vm.Create(cfg, *flagDebug) 48 if err != nil { 49 log.Fatal(err) 50 } 51 reporter, err := report.NewReporter(cfg) 52 if err != nil { 53 log.Fatal(err) 54 } 55 osutil.MkdirAll(cfg.Workdir) 56 mgr := &Manager{ 57 cfg: cfg, 58 target: target, 59 vmPool: vmPool, 60 reporter: reporter, 61 debug: *flagDebug, 62 requests: make(chan *runtest.RunRequest, 2*vmPool.Count()), 63 checkResultC: make(chan *rpctype.CheckArgs, 1), 64 checkResultReady: make(chan bool), 65 vmStop: make(chan bool), 66 reqMap: make(map[int]*runtest.RunRequest), 67 lastReq: make(map[string]int), 68 } 69 s, err := rpctype.NewRPCServer(cfg.RPC, mgr) 70 if err != nil { 71 log.Fatalf("failed to create rpc server: %v", err) 72 } 73 mgr.port = s.Addr().(*net.TCPAddr).Port 74 go s.Serve() 75 var wg sync.WaitGroup 76 wg.Add(vmPool.Count()) 77 fmt.Printf("booting VMs...\n") 78 for i := 0; i < vmPool.Count(); i++ { 79 i := i 80 go func() { 81 defer wg.Done() 82 name := fmt.Sprintf("vm-%v", i) 83 for { 84 rep, err := mgr.boot(name, i) 85 if err != nil { 86 log.Fatal(err) 87 } 88 if rep == nil { 89 return 90 } 91 if err := mgr.finishRequest(name, rep); err != nil { 92 log.Fatal(err) 93 } 94 } 95 }() 96 } 97 mgr.checkResult = <-mgr.checkResultC 98 close(mgr.checkResultReady) 99 enabledCalls := make(map[string]map[*prog.Syscall]bool) 100 for sandbox, ids := range mgr.checkResult.EnabledCalls { 101 calls := make(map[*prog.Syscall]bool) 102 for _, id := range ids { 103 calls[target.Syscalls[id]] = true 104 } 105 enabledCalls[sandbox] = calls 106 } 107 for _, feat := range mgr.checkResult.Features { 108 fmt.Printf("%-24v: %v\n", feat.Name, feat.Reason) 109 } 110 for sandbox, calls := range enabledCalls { 111 fmt.Printf("%-24v: %v calls enabled\n", sandbox+" sandbox", len(calls)) 112 } 113 ctx := &runtest.Context{ 114 Dir: filepath.Join(cfg.Syzkaller, "sys", target.OS, "test"), 115 Target: target, 116 Features: mgr.checkResult.Features, 117 EnabledCalls: enabledCalls, 118 Requests: mgr.requests, 119 LogFunc: func(text string) { fmt.Println(text) }, 120 } 121 err = ctx.Run() 122 close(vm.Shutdown) 123 wg.Wait() 124 if err != nil { 125 fmt.Println(err) 126 os.Exit(1) 127 } 128} 129 130type Manager struct { 131 cfg *mgrconfig.Config 132 target *prog.Target 133 vmPool *vm.Pool 134 reporter report.Reporter 135 requests chan *runtest.RunRequest 136 checkResult *rpctype.CheckArgs 137 checkResultReady chan bool 138 checkResultC chan *rpctype.CheckArgs 139 vmStop chan bool 140 port int 141 debug bool 142 143 reqMu sync.Mutex 144 reqSeq int 145 reqMap map[int]*runtest.RunRequest 146 lastReq map[string]int 147} 148 149func (mgr *Manager) boot(name string, index int) (*report.Report, error) { 150 inst, err := mgr.vmPool.Create(index) 151 if err != nil { 152 return nil, fmt.Errorf("failed to create instance: %v", err) 153 } 154 defer inst.Close() 155 156 fwdAddr, err := inst.Forward(mgr.port) 157 if err != nil { 158 return nil, fmt.Errorf("failed to setup port forwarding: %v", err) 159 } 160 fuzzerBin, err := inst.Copy(mgr.cfg.SyzFuzzerBin) 161 if err != nil { 162 return nil, fmt.Errorf("failed to copy binary: %v", err) 163 } 164 executorBin, err := inst.Copy(mgr.cfg.SyzExecutorBin) 165 if err != nil { 166 return nil, fmt.Errorf("failed to copy binary: %v", err) 167 } 168 cmd := instance.FuzzerCmd(fuzzerBin, executorBin, name, 169 mgr.cfg.TargetOS, mgr.cfg.TargetArch, fwdAddr, mgr.cfg.Sandbox, mgr.cfg.Procs, 0, 170 mgr.cfg.Cover, mgr.debug, false, true) 171 outc, errc, err := inst.Run(time.Hour, mgr.vmStop, cmd) 172 if err != nil { 173 return nil, fmt.Errorf("failed to run fuzzer: %v", err) 174 } 175 rep := inst.MonitorExecution(outc, errc, mgr.reporter, true) 176 return rep, nil 177} 178 179func (mgr *Manager) finishRequest(name string, rep *report.Report) error { 180 mgr.reqMu.Lock() 181 defer mgr.reqMu.Unlock() 182 lastReq := mgr.lastReq[name] 183 req := mgr.reqMap[lastReq] 184 if lastReq == 0 || req == nil { 185 return fmt.Errorf("vm crash: %v\n%s\n%s", rep.Title, rep.Report, rep.Output) 186 } 187 delete(mgr.reqMap, lastReq) 188 delete(mgr.lastReq, name) 189 req.Err = fmt.Errorf("%v", rep.Title) 190 req.Output = rep.Report 191 if len(req.Output) == 0 { 192 req.Output = rep.Output 193 } 194 close(req.Done) 195 return nil 196} 197 198func (mgr *Manager) Connect(a *rpctype.ConnectArgs, r *rpctype.ConnectRes) error { 199 r.GitRevision = sys.GitRevision 200 r.TargetRevision = mgr.target.Revision 201 r.AllSandboxes = true 202 select { 203 case <-mgr.checkResultReady: 204 r.CheckResult = mgr.checkResult 205 default: 206 } 207 return nil 208} 209 210func (mgr *Manager) Check(a *rpctype.CheckArgs, r *int) error { 211 if a.Error != "" { 212 log.Fatalf("machine check: %v", a.Error) 213 } 214 select { 215 case mgr.checkResultC <- a: 216 default: 217 } 218 return nil 219} 220 221func (mgr *Manager) Poll(a *rpctype.RunTestPollReq, r *rpctype.RunTestPollRes) error { 222 req := <-mgr.requests 223 if req == nil { 224 return nil 225 } 226 mgr.reqMu.Lock() 227 if mgr.lastReq[a.Name] != 0 { 228 log.Fatalf("double poll req from %v", a.Name) 229 } 230 mgr.reqSeq++ 231 r.ID = mgr.reqSeq 232 mgr.reqMap[mgr.reqSeq] = req 233 mgr.lastReq[a.Name] = mgr.reqSeq 234 mgr.reqMu.Unlock() 235 if req.Bin != "" { 236 data, err := ioutil.ReadFile(req.Bin) 237 if err != nil { 238 log.Fatalf("failed to read bin file: %v", err) 239 } 240 r.Bin = data 241 return nil 242 } 243 r.Prog = req.P.Serialize() 244 r.Cfg = req.Cfg 245 r.Opts = req.Opts 246 r.Repeat = req.Repeat 247 return nil 248} 249 250func (mgr *Manager) Done(a *rpctype.RunTestDoneArgs, r *int) error { 251 mgr.reqMu.Lock() 252 lastReq := mgr.lastReq[a.Name] 253 if lastReq != a.ID { 254 log.Fatalf("wrong done id %v from %v", a.ID, a.Name) 255 } 256 req := mgr.reqMap[a.ID] 257 delete(mgr.reqMap, a.ID) 258 delete(mgr.lastReq, a.Name) 259 mgr.reqMu.Unlock() 260 if req == nil { 261 log.Fatalf("got done request for unknown id %v", a.ID) 262 } 263 req.Output = a.Output 264 req.Info = a.Info 265 if a.Error != "" { 266 req.Err = errors.New(a.Error) 267 } 268 close(req.Done) 269 return nil 270} 271