github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/syz-verifier/execresult.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
     9  
    10  import (
    11  	"fmt"
    12  	"syscall"
    13  
    14  	"github.com/google/syzkaller/pkg/ipc"
    15  	"github.com/google/syzkaller/prog"
    16  )
    17  
    18  // ExecResult stores the results of executing a program.
    19  type ExecResult struct {
    20  	// Pool is the index of the pool.
    21  	Pool int
    22  	// Hanged is set to true when a program was killed due to hanging.
    23  	Hanged bool
    24  	// Info contains information about the execution of each system call
    25  	// in the generated programs.
    26  	Info ipc.ProgInfo
    27  	// Crashed is set to true if a crash occurred while executing the program.
    28  	// TODO: is not used properly. Crashes are just an errors now.
    29  	Crashed bool
    30  	// Source task ID is used to route result back to the caller.
    31  	ExecTaskID int64
    32  	// To signal the processing errors.
    33  	Error error
    34  }
    35  
    36  func (l *ExecResult) IsEqual(r *ExecResult) bool {
    37  	if l.Crashed || r.Crashed {
    38  		return false
    39  	}
    40  
    41  	lCalls := l.Info.Calls
    42  	rCalls := r.Info.Calls
    43  
    44  	if len(lCalls) != len(rCalls) {
    45  		return false
    46  	}
    47  
    48  	for i := 0; i < len(lCalls); i++ {
    49  		if lCalls[i].Errno != rCalls[i].Errno ||
    50  			lCalls[i].Flags != rCalls[i].Flags {
    51  			return false
    52  		}
    53  	}
    54  
    55  	return true
    56  }
    57  
    58  type ResultReport struct {
    59  	// Prog is the serialized program.
    60  	Prog string
    61  	// Reports contains information about each system call.
    62  	Reports []*CallReport
    63  	// Mismatch says whether the Reports differ.
    64  	Mismatch bool
    65  }
    66  
    67  type CallReport struct {
    68  	// Call is the name of the system call.
    69  	Call string
    70  	// States is a map between pools and their return state when executing the system call.
    71  	States map[int]ReturnState
    72  	// Mismatch is set to true if the returned error codes were not the same.
    73  	Mismatch bool
    74  }
    75  
    76  // ReturnState stores the results of executing a system call.
    77  type ReturnState struct {
    78  	// Errno is returned by executing the system call.
    79  	Errno int
    80  	// Flags stores the call flags (see pkg/ipc/ipc.go).
    81  	Flags ipc.CallFlags
    82  	// Crashed is set to true if the kernel crashed while executing the program
    83  	// that contains the system call.
    84  	Crashed bool
    85  }
    86  
    87  func (s ReturnState) String() string {
    88  	state := ""
    89  
    90  	if s.Crashed {
    91  		return "Crashed"
    92  	}
    93  
    94  	state += fmt.Sprintf("Flags: %d, ", s.Flags)
    95  	errDesc := "success"
    96  	if s.Errno != 0 {
    97  		errDesc = syscall.Errno(s.Errno).Error()
    98  	}
    99  	state += fmt.Sprintf("Errno: %d (%s)", s.Errno, errDesc)
   100  	return state
   101  }
   102  
   103  // CompareResults checks whether the ExecResult of the same program,
   104  // executed on different kernels, are the same.
   105  // It returns s ResultReport, highlighting the differences.
   106  func CompareResults(res []*ExecResult, prog *prog.Prog) *ResultReport {
   107  	rr := &ResultReport{
   108  		Prog: string(prog.Serialize()),
   109  	}
   110  
   111  	// Build the CallReport for each system call in the program.
   112  	for idx, call := range prog.Calls {
   113  		cn := call.Meta.Name
   114  
   115  		cr := &CallReport{
   116  			Call:   cn,
   117  			States: map[int]ReturnState{},
   118  		}
   119  
   120  		for _, r := range res {
   121  			if r.Crashed {
   122  				cr.States[r.Pool] = ReturnState{Crashed: true}
   123  				continue
   124  			}
   125  
   126  			ci := r.Info.Calls[idx]
   127  			cr.States[r.Pool] = ReturnState{Errno: ci.Errno, Flags: ci.Flags}
   128  		}
   129  		rr.Reports = append(rr.Reports, cr)
   130  	}
   131  
   132  	pool0 := res[0].Pool
   133  	for _, cr := range rr.Reports {
   134  		for _, state := range cr.States {
   135  			// For each CallReport, verify whether the ReturnStates from all
   136  			// the pools that executed the program are the same
   137  			if state0 := cr.States[pool0]; state0 != state {
   138  				cr.Mismatch = true
   139  				rr.Mismatch = true
   140  			}
   141  		}
   142  	}
   143  
   144  	return rr
   145  }