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