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