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  }