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