github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/syz-verifier/verifier.go (about) 1 // Copyright 2021 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 package main 5 6 import ( 7 "errors" 8 "fmt" 9 "io" 10 "math/rand" 11 "os" 12 "os/signal" 13 "path/filepath" 14 "strings" 15 "sync" 16 "time" 17 18 "github.com/google/syzkaller/pkg/instance" 19 "github.com/google/syzkaller/pkg/log" 20 "github.com/google/syzkaller/pkg/osutil" 21 "github.com/google/syzkaller/pkg/rpctype" 22 "github.com/google/syzkaller/prog" 23 "github.com/google/syzkaller/vm" 24 ) 25 26 // Verifier TODO. 27 type Verifier struct { 28 pools map[int]*poolInfo 29 vmStop chan bool 30 // Location of a working directory for all VMs for the syz-verifier process. 31 // Outputs here include: 32 // - <workdir>/crashes/<OS-Arch>/*: crash output files grouped by OS/Arch 33 // - <workdir>/corpus.db: corpus with interesting programs 34 // - <workdir>/<OS-Arch>/instance-x: per VM instance temporary files 35 // grouped by OS/Arch 36 workdir string 37 crashdir string 38 resultsdir string 39 target *prog.Target 40 runnerBin string 41 executorBin string 42 progGeneratorInit sync.WaitGroup 43 choiceTable *prog.ChoiceTable 44 progIdx int 45 addr string 46 srv *RPCServer 47 calls map[*prog.Syscall]bool 48 reasons map[*prog.Syscall]string 49 reportReasons bool 50 stats *Stats 51 statsWrite io.Writer 52 newEnv bool 53 reruns int 54 55 // We use single queue for every kernel environment. 56 tasksMutex sync.Mutex 57 onTaskAdded *sync.Cond 58 kernelEnvTasks [][]*ExecTaskQueue 59 taskFactory *ExecTaskFactory 60 } 61 62 func (vrf *Verifier) Init() { 63 vrf.progGeneratorInit.Add(1) 64 65 vrf.onTaskAdded = sync.NewCond(&vrf.tasksMutex) 66 67 vrf.kernelEnvTasks = make([][]*ExecTaskQueue, len(vrf.pools)) 68 for i := range vrf.kernelEnvTasks { 69 vrf.kernelEnvTasks[i] = make([]*ExecTaskQueue, EnvironmentsCount) 70 for j := range vrf.kernelEnvTasks[i] { 71 vrf.kernelEnvTasks[i][j] = MakeExecTaskQueue() 72 } 73 } 74 75 srv, err := startRPCServer(vrf) 76 if err != nil { 77 log.Fatalf("failed to initialise RPC server: %v", err) 78 } 79 vrf.srv = srv 80 81 vrf.taskFactory = MakeExecTaskFactory() 82 } 83 84 func (vrf *Verifier) StartProgramsAnalysis() { 85 go func() { 86 vrf.progGeneratorInit.Wait() 87 88 type AnalysisResult struct { 89 Diff []*ExecResult 90 Prog *prog.Prog 91 } 92 93 results := make(chan *AnalysisResult) 94 go func() { 95 for result := range results { 96 if result.Diff != nil { 97 vrf.SaveDiffResults(result.Diff, result.Prog) 98 } 99 } 100 }() 101 102 for i := 0; i < 100; i++ { 103 go func() { 104 for { 105 prog := vrf.generate() 106 results <- &AnalysisResult{ 107 vrf.TestProgram(prog), 108 prog, 109 } 110 } 111 }() 112 } 113 }() 114 } 115 116 func (vrf *Verifier) GetRunnerTask(kernel int, existing EnvDescr) *rpctype.ExecTask { 117 vrf.tasksMutex.Lock() 118 defer vrf.tasksMutex.Unlock() 119 120 for { 121 for env := existing; env >= AnyEnvironment; env-- { 122 if task, ok := vrf.kernelEnvTasks[kernel][env].PopTask(); ok { 123 return task.ToRPC() 124 } 125 } 126 127 vrf.onTaskAdded.Wait() 128 } 129 } 130 131 func (vrf *Verifier) PutExecResult(result *ExecResult) { 132 c := vrf.taskFactory.GetExecResultChan(result.ExecTaskID) 133 c <- result 134 } 135 136 // TestProgram return the results slice if some exec diff was found. 137 func (vrf *Verifier) TestProgram(prog *prog.Prog) (result []*ExecResult) { 138 steps := []EnvDescr{ 139 NewEnvironment, 140 NewEnvironment, 141 } 142 143 defer vrf.stats.TotalProgs.Inc() 144 145 for i, env := range steps { 146 stepRes, err := vrf.Run(prog, env) 147 if err != nil { 148 vrf.stats.ExecErrorProgs.Inc() 149 return 150 } 151 vrf.AddCallsExecutionStat(stepRes, prog) 152 if stepRes[0].IsEqual(stepRes[1]) { 153 if i != 0 { 154 vrf.stats.FlakyProgs.Inc() 155 } 156 return 157 } 158 if i == len(steps)-1 { 159 vrf.stats.MismatchingProgs.Inc() 160 return stepRes 161 } 162 } 163 return 164 } 165 166 // Run sends the program for verification to execution queues and return 167 // result once it's ready. 168 // In case of time-out, return (nil, error). 169 func (vrf *Verifier) Run(prog *prog.Prog, env EnvDescr) (result []*ExecResult, err error) { 170 totalKernels := len(vrf.kernelEnvTasks) 171 result = make([]*ExecResult, totalKernels) 172 173 wg := sync.WaitGroup{} 174 wg.Add(totalKernels) 175 for i := 0; i < totalKernels; i++ { 176 i := i 177 q := vrf.kernelEnvTasks[i][env] 178 179 go func() { 180 defer wg.Done() 181 task := vrf.taskFactory.MakeExecTask(prog) 182 defer vrf.taskFactory.DeleteExecTask(task) 183 184 vrf.tasksMutex.Lock() 185 q.PushTask(task) 186 vrf.onTaskAdded.Signal() 187 vrf.tasksMutex.Unlock() 188 189 result[i] = <-task.ExecResultChan 190 }() 191 } 192 wg.Wait() 193 194 for _, item := range result { 195 if item == nil { 196 err = errors.New("something went wrong and we exit w/o results") 197 return nil, err 198 } 199 if item.Error != nil { 200 err = item.Error 201 return nil, err 202 } 203 } 204 205 return result, nil 206 } 207 208 // SetPrintStatAtSIGINT asks Stats object to report verification 209 // statistics when an os.Interrupt occurs and Exit(). 210 func (vrf *Verifier) SetPrintStatAtSIGINT() error { 211 if vrf.stats == nil { 212 return errors.New("verifier.stats is nil") 213 } 214 215 osSignalChannel := make(chan os.Signal, 1) 216 signal.Notify(osSignalChannel, os.Interrupt) 217 218 go func() { 219 <-osSignalChannel 220 defer os.Exit(0) 221 222 totalExecutionTime := time.Since(vrf.stats.StartTime.Get()).Minutes() 223 if !vrf.stats.MismatchesFound() { 224 fmt.Fprint(vrf.statsWrite, "No mismatches occurred until syz-verifier was stopped.") 225 } else { 226 fmt.Fprintf(vrf.statsWrite, "%s", vrf.stats.GetTextDescription(totalExecutionTime)) 227 } 228 }() 229 230 return nil 231 } 232 233 func (vrf *Verifier) startInstances() { 234 for poolID, pi := range vrf.pools { 235 totalInstances := pi.pool.Count() 236 for vmID := 0; vmID < totalInstances; vmID++ { 237 go func(pi *poolInfo, poolID, vmID int) { 238 for { 239 vrf.createAndManageInstance(pi, poolID, vmID) 240 } 241 }(pi, poolID, vmID) 242 } 243 } 244 } 245 246 func (vrf *Verifier) createAndManageInstance(pi *poolInfo, poolID, vmID int) { 247 inst, err := pi.pool.Create(vmID) 248 if err != nil { 249 log.Fatalf("failed to create instance: %v", err) 250 } 251 defer inst.Close() 252 defer vrf.srv.cleanup(poolID, vmID) 253 254 fwdAddr, err := inst.Forward(vrf.srv.port) 255 if err != nil { 256 log.Fatalf("failed to set up port forwarding: %v", err) 257 } 258 259 runnerBin, err := inst.Copy(vrf.runnerBin) 260 if err != nil { 261 log.Fatalf(" failed to copy runner binary: %v", err) 262 } 263 _, err = inst.Copy(vrf.executorBin) 264 if err != nil { 265 log.Fatalf("failed to copy executor binary: %v", err) 266 } 267 268 cmd := instance.RunnerCmd(runnerBin, fwdAddr, vrf.target.OS, vrf.target.Arch, poolID, 0, false, vrf.newEnv) 269 _, _, err = inst.Run(pi.cfg.Timeouts.VMRunningTime, pi.Reporter, cmd, vm.ExitTimeout, vm.StopChan(vrf.vmStop)) 270 if err != nil { 271 log.Fatalf("failed to start runner: %v", err) 272 } 273 log.Logf(0, "reboot the VM in pool %d", poolID) 274 } 275 276 // finalizeCallSet removes the system calls that are not supported from the set 277 // of enabled system calls and reports the reason to the io.Writer (either 278 // because the call is not supported by one of the kernels or because the call 279 // is missing some transitive dependencies). The resulting set of system calls 280 // will be used to build the prog.ChoiceTable. 281 func (vrf *Verifier) finalizeCallSet(w io.Writer) { 282 for c := range vrf.reasons { 283 delete(vrf.calls, c) 284 } 285 286 // Find and report to the user all the system calls that need to be 287 // disabled due to missing dependencies. 288 _, disabled := vrf.target.TransitivelyEnabledCalls(vrf.calls) 289 for c, reason := range disabled { 290 vrf.reasons[c] = reason 291 delete(vrf.calls, c) 292 } 293 294 if len(vrf.calls) == 0 { 295 log.Logf(0, "All enabled system calls are missing dependencies or not"+ 296 " supported by some kernels, exiting syz-verifier.") 297 } 298 299 if !vrf.reportReasons { 300 return 301 } 302 303 fmt.Fprintln(w, "The following calls have been disabled:") 304 for c, reason := range vrf.reasons { 305 fmt.Fprintf(w, "\t%v: %v\n", c.Name, reason) 306 } 307 } 308 309 // AddCallsExecutionStat ignore all the calls after the first mismatch. 310 func (vrf *Verifier) AddCallsExecutionStat(results []*ExecResult, program *prog.Prog) { 311 rr := CompareResults(results, program) 312 for _, cr := range rr.Reports { 313 vrf.stats.Calls.IncCallOccurrenceCount(cr.Call) 314 } 315 316 for _, cr := range rr.Reports { 317 if !cr.Mismatch { 318 continue 319 } 320 vrf.stats.IncCallMismatches(cr.Call) 321 for _, state := range cr.States { 322 if state0 := cr.States[0]; state0 != state { 323 vrf.stats.Calls.AddState(cr.Call, state) 324 vrf.stats.Calls.AddState(cr.Call, state0) 325 } 326 } 327 break 328 } 329 } 330 331 // SaveDiffResults extract diff and save result on the persistent storage. 332 func (vrf *Verifier) SaveDiffResults(results []*ExecResult, program *prog.Prog) bool { 333 rr := CompareResults(results, program) 334 335 oldest := 0 336 var oldestTime time.Time 337 for i := 0; i < maxResultReports; i++ { 338 info, err := os.Stat(filepath.Join(vrf.resultsdir, fmt.Sprintf("result-%d", i))) 339 if err != nil { 340 // There are only i-1 report files so the i-th one 341 // can be created. 342 oldest = i 343 break 344 } 345 346 // Otherwise, search for the oldest report file to 347 // overwrite as newer result reports are more useful. 348 if oldestTime.IsZero() || info.ModTime().Before(oldestTime) { 349 oldest = i 350 oldestTime = info.ModTime() 351 } 352 } 353 354 err := osutil.WriteFile(filepath.Join(vrf.resultsdir, 355 fmt.Sprintf("result-%d", oldest)), createReport(rr, len(vrf.pools))) 356 if err != nil { 357 log.Logf(0, "failed to write result-%d file, err %v", oldest, err) 358 } 359 360 log.Logf(0, "result-%d written successfully", oldest) 361 return true 362 } 363 364 // generate returns a newly generated program or error. 365 func (vrf *Verifier) generate() *prog.Prog { 366 vrf.progGeneratorInit.Wait() 367 368 rnd := rand.New(rand.NewSource(time.Now().UnixNano() + 1e12)) 369 return vrf.target.Generate(rnd, prog.RecommendedCalls, vrf.choiceTable) 370 } 371 372 func createReport(rr *ResultReport, pools int) []byte { 373 calls := strings.Split(rr.Prog, "\n") 374 calls = calls[:len(calls)-1] 375 376 data := "ERRNO mismatches found for program:\n\n" 377 for idx, cr := range rr.Reports { 378 tick := "[=]" 379 if cr.Mismatch { 380 tick = "[!]" 381 } 382 data += fmt.Sprintf("%s %s\n", tick, calls[idx]) 383 384 // Ensure results are ordered by pool index. 385 for i := 0; i < pools; i++ { 386 state := cr.States[i] 387 data += fmt.Sprintf("\t↳ Pool: %d, %s\n", i, state) 388 } 389 390 data += "\n" 391 } 392 393 return []byte(data) 394 }