github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/tools/syz-runtest/runtest.go (about) 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. 7 package main 8 9 import ( 10 "errors" 11 "flag" 12 "fmt" 13 "log" 14 "net" 15 "os" 16 "path/filepath" 17 "slices" 18 "sync" 19 "sync/atomic" 20 "time" 21 22 "github.com/google/syzkaller/pkg/flatrpc" 23 "github.com/google/syzkaller/pkg/fuzzer/queue" 24 "github.com/google/syzkaller/pkg/instance" 25 "github.com/google/syzkaller/pkg/mgrconfig" 26 "github.com/google/syzkaller/pkg/osutil" 27 "github.com/google/syzkaller/pkg/report" 28 "github.com/google/syzkaller/pkg/rpctype" 29 "github.com/google/syzkaller/pkg/runtest" 30 "github.com/google/syzkaller/pkg/vminfo" 31 "github.com/google/syzkaller/prog" 32 _ "github.com/google/syzkaller/sys" 33 "github.com/google/syzkaller/vm" 34 ) 35 36 var ( 37 flagConfig = flag.String("config", "", "manager config") 38 flagDebug = flag.Bool("debug", false, "debug mode") 39 flagTests = flag.String("tests", "", "prefix to match test file names") 40 ) 41 42 func main() { 43 flag.Parse() 44 cfg, err := mgrconfig.LoadFile(*flagConfig) 45 if err != nil { 46 log.Fatal(err) 47 } 48 vmPool, err := vm.Create(cfg, *flagDebug) 49 if err != nil { 50 log.Fatal(err) 51 } 52 reporter, err := report.NewReporter(cfg) 53 if err != nil { 54 log.Fatal(err) 55 } 56 osutil.MkdirAll(cfg.Workdir) 57 mgr := &Manager{ 58 cfg: cfg, 59 vmPool: vmPool, 60 checker: vminfo.New(cfg), 61 reporter: reporter, 62 debug: *flagDebug, 63 checkResultC: make(chan *rpctype.CheckArgs, 1), 64 vmStop: make(chan bool), 65 reqMap: make(map[int64]*queue.Request), 66 pending: make(map[string]map[int64]bool), 67 } 68 mgr.checkFiles = mgr.checker.RequiredFiles() 69 mgr.source = queue.DynamicSource(mgr.checker) 70 s, err := rpctype.NewRPCServer(cfg.RPC, "Manager", mgr) 71 if err != nil { 72 log.Fatalf("failed to create rpc server: %v", err) 73 } 74 mgr.port = s.Addr().(*net.TCPAddr).Port 75 go s.Serve() 76 var wg sync.WaitGroup 77 wg.Add(vmPool.Count()) 78 fmt.Printf("booting VMs...\n") 79 var nameSeq atomic.Uint64 80 for i := 0; i < vmPool.Count(); i++ { 81 i := i 82 go func() { 83 defer wg.Done() 84 for { 85 name := fmt.Sprintf("vm-%v", nameSeq.Add(1)) 86 rep, err := mgr.boot(name, i) 87 if err != nil { 88 log.Fatal(err) 89 } 90 if rep == nil { 91 return 92 } 93 if err := mgr.finishRequests(name, rep); err != nil { 94 log.Fatal(err) 95 } 96 } 97 }() 98 } 99 checkResult := <-mgr.checkResultC 100 calls, _, features, err := mgr.checker.Run(checkResult.Files, checkResult.Features) 101 if err != nil { 102 log.Fatalf("failed to detect enabled syscalls: %v", err) 103 } 104 calls, _ = cfg.Target.TransitivelyEnabledCalls(calls) 105 enabledCalls := make(map[string]map[*prog.Syscall]bool) 106 // TODO: restore checking/testing of all other sandboxes (we used to test them). 107 // Note: syz_emit_ethernet/syz_extract_tcp_res were manually disabled for "" ("no") sandbox, 108 // b/c tun is not setup without sandbox. 109 enabledCalls[mgr.cfg.Sandbox] = calls 110 for feat, info := range features { 111 fmt.Printf("%-24v: %v\n", flatrpc.EnumNamesFeature[feat], info.Reason) 112 } 113 for sandbox, calls := range enabledCalls { 114 if sandbox == "" { 115 sandbox = "no" 116 } 117 fmt.Printf("%-24v: %v calls enabled\n", sandbox+" sandbox", len(calls)) 118 } 119 ctx := &runtest.Context{ 120 Dir: filepath.Join(cfg.Syzkaller, "sys", cfg.Target.OS, "test"), 121 Target: cfg.Target, 122 Features: features.Enabled(), 123 EnabledCalls: enabledCalls, 124 LogFunc: func(text string) { fmt.Println(text) }, 125 Verbose: true, 126 Debug: *flagDebug, 127 Tests: *flagTests, 128 } 129 mgr.source.Store(ctx) 130 err = ctx.Run() 131 close(vm.Shutdown) 132 wg.Wait() 133 if err != nil { 134 fmt.Println(err) 135 os.Exit(1) 136 } 137 } 138 139 type Manager struct { 140 cfg *mgrconfig.Config 141 vmPool *vm.Pool 142 checker *vminfo.Checker 143 checkFiles []string 144 reporter *report.Reporter 145 checkResultC chan *rpctype.CheckArgs 146 vmStop chan bool 147 port int 148 debug bool 149 source *queue.DynamicSourceCtl 150 151 reqMu sync.Mutex 152 reqSeq int64 153 reqMap map[int64]*queue.Request 154 pending map[string]map[int64]bool 155 } 156 157 func (mgr *Manager) boot(name string, index int) (*report.Report, error) { 158 inst, err := mgr.vmPool.Create(index) 159 if err != nil { 160 return nil, fmt.Errorf("failed to create instance: %w", err) 161 } 162 defer inst.Close() 163 164 fwdAddr, err := inst.Forward(mgr.port) 165 if err != nil { 166 return nil, fmt.Errorf("failed to setup port forwarding: %w", err) 167 } 168 169 fuzzerBin, err := inst.Copy(mgr.cfg.FuzzerBin) 170 if err != nil { 171 return nil, fmt.Errorf("failed to copy binary: %w", err) 172 } 173 174 // If SyzExecutorCmd is provided, it means that syz-executor is already in 175 // the image, so no need to copy it. 176 executorBin := mgr.cfg.SysTarget.ExecutorBin 177 if executorBin == "" { 178 executorBin, err = inst.Copy(mgr.cfg.ExecutorBin) 179 if err != nil { 180 return nil, fmt.Errorf("failed to copy binary: %w", err) 181 } 182 } 183 args := &instance.FuzzerCmdArgs{ 184 Fuzzer: fuzzerBin, 185 Executor: executorBin, 186 Name: name, 187 OS: mgr.cfg.TargetOS, 188 Arch: mgr.cfg.TargetArch, 189 FwdAddr: fwdAddr, 190 Sandbox: mgr.cfg.Sandbox, 191 Procs: 1, 192 Verbosity: 0, 193 Cover: mgr.cfg.Cover, 194 Debug: mgr.debug, 195 Test: false, 196 Optional: &instance.OptionalFuzzerArgs{ 197 Slowdown: mgr.cfg.Timeouts.Slowdown, 198 SandboxArg: mgr.cfg.SandboxArg, 199 }, 200 } 201 cmd := instance.FuzzerCmd(args) 202 _, rep, err := inst.Run(time.Hour, mgr.reporter, cmd, vm.StopChan(mgr.vmStop)) 203 if err != nil { 204 return nil, fmt.Errorf("failed to run fuzzer: %w", err) 205 } 206 return rep, nil 207 } 208 209 func (mgr *Manager) finishRequests(name string, rep *report.Report) error { 210 mgr.reqMu.Lock() 211 defer mgr.reqMu.Unlock() 212 for id := range mgr.pending[name] { 213 req := mgr.reqMap[id] 214 if req == nil { 215 return fmt.Errorf("vm crash: %v\n%s\n%s", rep.Title, rep.Report, rep.Output) 216 } 217 delete(mgr.reqMap, id) 218 output := rep.Report 219 if len(output) == 0 { 220 output = rep.Output 221 } 222 req.Done(&queue.Result{ 223 Status: queue.Crashed, 224 Err: fmt.Errorf("%v", rep.Title), 225 Output: slices.Clone(output), 226 }) 227 } 228 delete(mgr.pending, name) 229 return nil 230 } 231 232 func (mgr *Manager) Connect(a *rpctype.ConnectArgs, r *rpctype.ConnectRes) error { 233 r.ReadFiles = append(mgr.checker.RequiredFiles(), mgr.checkFiles...) 234 r.ReadGlobs = mgr.cfg.Target.RequiredGlobs() 235 for feat := range flatrpc.EnumNamesFeature { 236 r.Features |= feat 237 } 238 return nil 239 } 240 241 func (mgr *Manager) Check(a *rpctype.CheckArgs, r *rpctype.CheckRes) error { 242 if a.Error != "" { 243 log.Fatalf("machine check: %v", a.Error) 244 } 245 select { 246 case mgr.checkResultC <- a: 247 default: 248 } 249 return nil 250 } 251 252 func (mgr *Manager) ExchangeInfo(a *rpctype.ExchangeInfoRequest, r *rpctype.ExchangeInfoReply) error { 253 mgr.reqMu.Lock() 254 defer mgr.reqMu.Unlock() 255 if mgr.pending[a.Name] == nil { 256 mgr.pending[a.Name] = make(map[int64]bool) 257 } 258 for i := range a.Results { 259 res := a.Results[i] 260 if !mgr.pending[a.Name][res.ID] { 261 log.Fatalf("runner %v wasn't executing request %v", a.Name, res.ID) 262 } 263 delete(mgr.pending[a.Name], res.ID) 264 req := mgr.reqMap[res.ID] 265 if req == nil { 266 log.Fatalf("request %v does not exist", res.ID) 267 } 268 delete(mgr.reqMap, res.ID) 269 if req == nil { 270 log.Fatalf("got done request for unknown id %v", res.ID) 271 } 272 result := &queue.Result{ 273 Status: queue.Success, 274 Info: &res.Info, 275 Output: res.Output, 276 } 277 if res.Error != "" { 278 result.Status = queue.ExecFailure 279 result.Err = errors.New(res.Error) 280 } 281 req.Done(result) 282 } 283 for i := 0; i < a.NeedProgs; i++ { 284 req := mgr.source.Next() 285 if req == nil { 286 break 287 } 288 mgr.reqSeq++ 289 mgr.reqMap[mgr.reqSeq] = req 290 mgr.pending[a.Name][mgr.reqSeq] = true 291 var progData []byte 292 var err error 293 if req.BinaryFile != "" { 294 progData, err = os.ReadFile(req.BinaryFile) 295 } else { 296 progData, err = req.Prog.SerializeForExec() 297 } 298 if err != nil { 299 log.Fatal(err) 300 } 301 r.Requests = append(r.Requests, rpctype.ExecutionRequest{ 302 ID: mgr.reqSeq, 303 ProgData: progData, 304 ExecOpts: req.ExecOpts, 305 IsBinary: req.BinaryFile != "", 306 ResetState: req.BinaryFile == "", 307 ReturnOutput: true, 308 ReturnError: true, 309 Repeat: req.Repeat, 310 }) 311 } 312 return nil 313 } 314 315 func (mgr *Manager) StartExecuting(a *rpctype.ExecutingRequest, r *int) error { 316 return nil 317 }