github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/instance/execprog.go (about)

     1  // Copyright 2022 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 instance
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"time"
    10  
    11  	"github.com/google/syzkaller/pkg/csource"
    12  	"github.com/google/syzkaller/pkg/mgrconfig"
    13  	"github.com/google/syzkaller/pkg/osutil"
    14  	"github.com/google/syzkaller/pkg/report"
    15  	"github.com/google/syzkaller/prog"
    16  	"github.com/google/syzkaller/sys/targets"
    17  	"github.com/google/syzkaller/vm"
    18  )
    19  
    20  type ExecutorLogger func(int, string, ...interface{})
    21  
    22  type OptionalConfig struct {
    23  	ExitCondition      vm.ExitCondition
    24  	Logf               ExecutorLogger
    25  	OldFlagsCompatMode bool
    26  	BeforeContextLen   int
    27  	StraceBin          string
    28  }
    29  
    30  type ExecProgInstance struct {
    31  	execprogBin string
    32  	executorBin string
    33  	reporter    *report.Reporter
    34  	mgrCfg      *mgrconfig.Config
    35  	VMInstance  *vm.Instance
    36  	OptionalConfig
    37  }
    38  
    39  type RunResult struct {
    40  	Output []byte
    41  	Report *report.Report
    42  }
    43  
    44  func SetupExecProg(vmInst *vm.Instance, mgrCfg *mgrconfig.Config, reporter *report.Reporter,
    45  	opt *OptionalConfig) (*ExecProgInstance, error) {
    46  	execprogBin, err := vmInst.Copy(mgrCfg.ExecprogBin)
    47  	if err != nil {
    48  		return nil, &TestError{Title: fmt.Sprintf("failed to copy syz-execprog to VM: %v", err)}
    49  	}
    50  	executorBin := mgrCfg.SysTarget.ExecutorBin
    51  	if executorBin == "" {
    52  		executorBin, err = vmInst.Copy(mgrCfg.ExecutorBin)
    53  		if err != nil {
    54  			return nil, &TestError{Title: fmt.Sprintf("failed to copy syz-executor to VM: %v", err)}
    55  		}
    56  	}
    57  	ret := &ExecProgInstance{
    58  		execprogBin: execprogBin,
    59  		executorBin: executorBin,
    60  		reporter:    reporter,
    61  		mgrCfg:      mgrCfg,
    62  		VMInstance:  vmInst,
    63  	}
    64  	if opt != nil {
    65  		ret.OptionalConfig = *opt
    66  		if ret.StraceBin != "" {
    67  			var err error
    68  			ret.StraceBin, err = vmInst.Copy(ret.StraceBin)
    69  			if err != nil {
    70  				return nil, &TestError{Title: fmt.Sprintf("failed to copy strace bin: %v", err)}
    71  			}
    72  		}
    73  	}
    74  	if ret.Logf == nil {
    75  		ret.Logf = func(int, string, ...interface{}) {}
    76  	}
    77  	if ret.ExitCondition == 0 {
    78  		ret.ExitCondition = vm.ExitTimeout | vm.ExitNormal | vm.ExitError
    79  	}
    80  	return ret, nil
    81  }
    82  
    83  func CreateExecProgInstance(vmPool *vm.Pool, vmIndex int, mgrCfg *mgrconfig.Config,
    84  	reporter *report.Reporter, opt *OptionalConfig) (*ExecProgInstance, error) {
    85  	vmInst, err := vmPool.Create(vmIndex)
    86  	if err != nil {
    87  		return nil, fmt.Errorf("failed to create VM: %w", err)
    88  	}
    89  	ret, err := SetupExecProg(vmInst, mgrCfg, reporter, opt)
    90  	if err != nil {
    91  		vmInst.Close()
    92  		return nil, err
    93  	}
    94  	return ret, nil
    95  }
    96  
    97  func (inst *ExecProgInstance) runCommand(command string, duration time.Duration) (*RunResult, error) {
    98  	var prefixOutput []byte
    99  	if inst.StraceBin != "" {
   100  		filterCalls := ""
   101  		switch inst.mgrCfg.SysTarget.OS {
   102  		case targets.Linux:
   103  			// wait4 and nanosleep generate a lot of noise, especially when running syz-executor.
   104  			// We cut them on the VM side in order to decrease load on the network and to use
   105  			// the limited buffer size wisely.
   106  			filterCalls = ` -e \!wait4,clock_nanosleep,nanosleep`
   107  		}
   108  		command = inst.StraceBin + filterCalls + ` -s 100 -x -f ` + command
   109  		prefixOutput = []byte(fmt.Sprintf("%s\n\n<...>\n", command))
   110  	}
   111  	opts := []any{inst.ExitCondition}
   112  	if inst.BeforeContextLen != 0 {
   113  		opts = append(opts, vm.OutputSize(inst.BeforeContextLen))
   114  	}
   115  	output, rep, err := inst.VMInstance.Run(duration, inst.reporter, command, opts...)
   116  	if err != nil {
   117  		return nil, fmt.Errorf("failed to run command in VM: %w", err)
   118  	}
   119  	if rep == nil {
   120  		inst.Logf(2, "program did not crash")
   121  	} else {
   122  		if err := inst.reporter.Symbolize(rep); err != nil {
   123  			inst.Logf(0, "failed to symbolize report: %v", err)
   124  		}
   125  		inst.Logf(2, "program crashed: %v", rep.Title)
   126  	}
   127  	result := &RunResult{append(prefixOutput, output...), rep}
   128  	return result, nil
   129  }
   130  
   131  func (inst *ExecProgInstance) runBinary(bin string, duration time.Duration) (*RunResult, error) {
   132  	bin, err := inst.VMInstance.Copy(bin)
   133  	if err != nil {
   134  		return nil, &TestError{Title: fmt.Sprintf("failed to copy binary to VM: %v", err)}
   135  	}
   136  	return inst.runCommand(bin, duration)
   137  }
   138  
   139  func (inst *ExecProgInstance) RunCProg(p *prog.Prog, duration time.Duration,
   140  	opts csource.Options) (*RunResult, error) {
   141  	src, err := csource.Write(p, opts)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  	inst.Logf(2, "testing compiled C program (duration=%v, %+v): %s", duration, opts, p)
   146  	return inst.RunCProgRaw(src, p.Target, duration)
   147  }
   148  
   149  func (inst *ExecProgInstance) RunCProgRaw(src []byte, target *prog.Target,
   150  	duration time.Duration) (*RunResult, error) {
   151  	bin, err := csource.BuildNoWarn(target, src)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  	defer os.Remove(bin)
   156  	return inst.runBinary(bin, duration)
   157  }
   158  
   159  func (inst *ExecProgInstance) RunSyzProgFile(progFile string, duration time.Duration,
   160  	opts csource.Options) (*RunResult, error) {
   161  	vmProgFile, err := inst.VMInstance.Copy(progFile)
   162  	if err != nil {
   163  		return nil, &TestError{Title: fmt.Sprintf("failed to copy prog to VM: %v", err)}
   164  	}
   165  	target := inst.mgrCfg.SysTarget
   166  	faultCall := -1
   167  	if opts.Fault {
   168  		faultCall = opts.FaultCall
   169  	}
   170  	command := ExecprogCmd(inst.execprogBin, inst.executorBin, target.OS, target.Arch, opts.Sandbox,
   171  		opts.SandboxArg, opts.Repeat, opts.Threaded, opts.Collide, opts.Procs, faultCall, opts.FaultNth,
   172  		!inst.OldFlagsCompatMode, inst.mgrCfg.Timeouts.Slowdown, vmProgFile)
   173  	return inst.runCommand(command, duration)
   174  }
   175  
   176  func (inst *ExecProgInstance) RunSyzProg(syzProg []byte, duration time.Duration,
   177  	opts csource.Options) (*RunResult, error) {
   178  	progFile, err := osutil.WriteTempFile(syzProg)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	defer os.Remove(progFile)
   183  	return inst.RunSyzProgFile(progFile, duration, opts)
   184  }
   185  
   186  func (inst *ExecProgInstance) Close() {
   187  	inst.VMInstance.Close()
   188  }