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