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  }