github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/syz-verifier/main.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 starts the syz-verifier tool. High-level documentation can be 9 // found in docs/syz_verifier.md. 10 package main 11 12 import ( 13 "flag" 14 "io" 15 "os" 16 "path/filepath" 17 "strconv" 18 19 "github.com/google/syzkaller/pkg/log" 20 "github.com/google/syzkaller/pkg/mgrconfig" 21 "github.com/google/syzkaller/pkg/osutil" 22 "github.com/google/syzkaller/pkg/report" 23 "github.com/google/syzkaller/pkg/tool" 24 "github.com/google/syzkaller/prog" 25 "github.com/google/syzkaller/vm" 26 ) 27 28 const ( 29 maxResultReports = 100 30 ) 31 32 // poolInfo contains kernel-specific information for spawning virtual machines 33 // and reporting crashes. It also keeps track of the Runners executing on 34 // spawned VMs, what programs have been sent to each Runner and what programs 35 // have yet to be sent on any of the Runners. 36 type poolInfo struct { 37 cfg *mgrconfig.Config 38 pool *vm.Pool 39 Reporter *report.Reporter 40 // checked is set to true when the set of system calls not supported on the 41 // kernel is known. 42 checked bool 43 } 44 45 func main() { 46 var cfgs tool.CfgsFlag 47 flag.Var(&cfgs, "configs", "[MANDATORY] list of at least two kernel-specific comma-sepatated configuration files") 48 flagDebug := flag.Bool("debug", false, "dump all VM output to console") 49 flagStats := flag.String("stats", "", "where stats will be written when"+ 50 "execution of syz-verifier finishes, defaults to stdout") 51 flagEnv := flag.Bool("new-env", true, "create a new environment for each program") 52 flagAddress := flag.String("address", "127.0.0.1:8080", "http address for monitoring") 53 flagReruns := flag.Int("rerun", 3, "number of time program is rerun when a mismatch is found") 54 flag.Parse() 55 56 pools := make(map[int]*poolInfo) 57 for idx, cfg := range cfgs { 58 var err error 59 pi := &poolInfo{} 60 pi.cfg, err = mgrconfig.LoadFile(cfg) 61 if err != nil { 62 log.Fatalf("%v", err) 63 } 64 // TODO: call pi.pool.Close() on exit. 65 pi.pool, err = vm.Create(pi.cfg, *flagDebug) 66 if err != nil { 67 log.Fatalf("%v", err) 68 } 69 pools[idx] = pi 70 } 71 72 if len(pools) < 2 { 73 flag.Usage() 74 os.Exit(1) 75 } 76 77 cfg := pools[0].cfg 78 workdir, target, sysTarget, addr := cfg.Workdir, cfg.Target, cfg.SysTarget, cfg.RPC 79 for idx := 1; idx < len(pools); idx++ { 80 cfg := pools[idx].cfg 81 82 // TODO: pass the configurations that should be the same for all 83 // kernels in a default config file in order to avoid this checks and 84 // add testing 85 if workdir != cfg.Workdir { 86 log.Fatalf("working directory mismatch") 87 } 88 if target != cfg.Target { 89 log.Fatalf("target mismatch") 90 } 91 if sysTarget != cfg.SysTarget { 92 log.Fatalf("system target mismatch") 93 } 94 if addr != pools[idx].cfg.RPC { 95 log.Fatalf("tcp address mismatch") 96 } 97 } 98 99 exe := sysTarget.ExeExtension 100 runnerBin := filepath.Join(cfg.Syzkaller, "bin", target.OS+"_"+target.Arch, "syz-runner"+exe) 101 if !osutil.IsExist(runnerBin) { 102 log.Fatalf("bad syzkaller config: can't find %v", runnerBin) 103 } 104 execBin := cfg.ExecutorBin 105 if !osutil.IsExist(execBin) { 106 log.Fatalf("bad syzkaller config: can't find %v", execBin) 107 } 108 109 crashdir := filepath.Join(workdir, "crashes") 110 osutil.MkdirAll(crashdir) 111 for idx := range pools { 112 OS, Arch := target.OS, target.Arch 113 targetPath := OS + "-" + Arch + "-" + strconv.Itoa(idx) 114 osutil.MkdirAll(filepath.Join(workdir, targetPath)) 115 osutil.MkdirAll(filepath.Join(crashdir, targetPath)) 116 } 117 118 resultsdir := filepath.Join(workdir, "results") 119 osutil.MkdirAll(resultsdir) 120 121 var sw io.Writer 122 var err error 123 if *flagStats == "" { 124 sw = os.Stdout 125 } else { 126 statsFile := filepath.Join(workdir, *flagStats) 127 sw, err = os.Create(statsFile) 128 if err != nil { 129 log.Fatalf("failed to create stats output file: %v", err) 130 } 131 } 132 133 for idx, pi := range pools { 134 var err error 135 pi.Reporter, err = report.NewReporter(pi.cfg) 136 if err != nil { 137 log.Fatalf("failed to create reporter for instance-%d: %v", idx, err) 138 } 139 } 140 141 calls := make(map[*prog.Syscall]bool) 142 143 for _, id := range cfg.Syscalls { 144 c := target.Syscalls[id] 145 calls[c] = true 146 } 147 148 vrf := &Verifier{ 149 workdir: workdir, 150 crashdir: crashdir, 151 resultsdir: resultsdir, 152 pools: pools, 153 target: target, 154 calls: calls, 155 reasons: make(map[*prog.Syscall]string), 156 runnerBin: runnerBin, 157 executorBin: execBin, 158 addr: addr, 159 reportReasons: len(cfg.EnabledSyscalls) != 0 || len(cfg.DisabledSyscalls) != 0, 160 stats: MakeStats(), 161 statsWrite: sw, 162 newEnv: *flagEnv, 163 reruns: *flagReruns, 164 } 165 166 vrf.Init() 167 168 vrf.StartProgramsAnalysis() 169 vrf.startInstances() 170 171 monitor := MakeMonitor() 172 monitor.SetStatsTracking(vrf.stats) 173 174 log.Logf(0, "run the Monitor at http://%s", *flagAddress) 175 go monitor.ListenAndServe(*flagAddress) 176 177 select {} 178 }