github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/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 "context" 8 "fmt" 9 "os" 10 "time" 11 12 "github.com/google/syzkaller/pkg/csource" 13 "github.com/google/syzkaller/pkg/mgrconfig" 14 "github.com/google/syzkaller/pkg/osutil" 15 "github.com/google/syzkaller/pkg/report" 16 "github.com/google/syzkaller/prog" 17 "github.com/google/syzkaller/sys/targets" 18 "github.com/google/syzkaller/vm" 19 ) 20 21 type ExecutorLogger func(int, string, ...interface{}) 22 23 type OptionalConfig struct { 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 Duration time.Duration 43 } 44 45 const ( 46 // It's reasonable to expect that tools/syz-execprog should not normally 47 // return a non-zero exit code. 48 SyzExitConditions = vm.ExitTimeout | vm.ExitNormal 49 binExitConditions = vm.ExitTimeout | vm.ExitNormal | vm.ExitError 50 ) 51 52 func SetupExecProg(vmInst *vm.Instance, mgrCfg *mgrconfig.Config, reporter *report.Reporter, 53 opt *OptionalConfig) (*ExecProgInstance, error) { 54 var err error 55 execprogBin := mgrCfg.SysTarget.ExecprogBin 56 if execprogBin == "" { 57 execprogBin, err = vmInst.Copy(mgrCfg.ExecprogBin) 58 if err != nil { 59 return nil, &TestError{Title: fmt.Sprintf("failed to copy syz-execprog to VM: %v", err)} 60 } 61 } 62 executorBin := mgrCfg.SysTarget.ExecutorBin 63 if executorBin == "" { 64 executorBin, err = vmInst.Copy(mgrCfg.ExecutorBin) 65 if err != nil { 66 return nil, &TestError{Title: fmt.Sprintf("failed to copy syz-executor to VM: %v", err)} 67 } 68 } 69 ret := &ExecProgInstance{ 70 execprogBin: execprogBin, 71 executorBin: executorBin, 72 reporter: reporter, 73 mgrCfg: mgrCfg, 74 VMInstance: vmInst, 75 } 76 if opt != nil { 77 ret.OptionalConfig = *opt 78 if !mgrCfg.StraceBinOnTarget && ret.StraceBin != "" { 79 var err error 80 ret.StraceBin, err = vmInst.Copy(ret.StraceBin) 81 if err != nil { 82 return nil, &TestError{Title: fmt.Sprintf("failed to copy strace bin: %v", err)} 83 } 84 } 85 } 86 if ret.Logf == nil { 87 ret.Logf = func(int, string, ...interface{}) {} 88 } 89 return ret, nil 90 } 91 92 func CreateExecProgInstance(vmPool *vm.Pool, vmIndex int, mgrCfg *mgrconfig.Config, 93 reporter *report.Reporter, opt *OptionalConfig) (*ExecProgInstance, error) { 94 vmInst, err := vmPool.Create(context.Background(), vmIndex) 95 if err != nil { 96 return nil, fmt.Errorf("failed to create VM: %w", err) 97 } 98 ret, err := SetupExecProg(vmInst, mgrCfg, reporter, opt) 99 if err != nil { 100 vmInst.Close() 101 return nil, err 102 } 103 return ret, nil 104 } 105 106 func (inst *ExecProgInstance) runCommand(command string, duration time.Duration, 107 exitCondition vm.ExitCondition) (*RunResult, error) { 108 start := time.Now() 109 110 var prefixOutput []byte 111 if inst.StraceBin != "" { 112 filterCalls := "" 113 switch inst.mgrCfg.SysTarget.OS { 114 case targets.Linux: 115 // wait4 and nanosleep generate a lot of noise, especially when running syz-executor. 116 // We cut them on the VM side in order to decrease load on the network and to use 117 // the limited buffer size wisely. 118 filterCalls = ` -e \!wait4,clock_nanosleep,nanosleep` 119 } 120 command = inst.StraceBin + filterCalls + ` -s 100 -x -f ` + command 121 prefixOutput = []byte(fmt.Sprintf("%s\n\n<...>\n", command)) 122 } 123 optionalBeforeContext := func(*vm.RunOptions) {} 124 if inst.BeforeContextLen != 0 { 125 optionalBeforeContext = vm.WithBeforeContext(inst.BeforeContextLen) 126 } 127 ctxTimeout, cancel := context.WithTimeout(context.Background(), duration) 128 defer cancel() 129 output, reps, err := inst.VMInstance.Run(ctxTimeout, inst.reporter, command, 130 vm.WithExitCondition(exitCondition), 131 optionalBeforeContext, 132 ) 133 var rep *report.Report 134 if len(reps) > 0 { 135 rep = reps[0] 136 } 137 if err != nil { 138 return nil, fmt.Errorf("failed to run command in VM: %w", err) 139 } 140 if rep == nil { 141 inst.Logf(2, "program did not crash") 142 } else { 143 if err := inst.reporter.Symbolize(rep); err != nil { 144 inst.Logf(0, "failed to symbolize report: %v", err) 145 } 146 inst.Logf(2, "program crashed: %v", rep.Title) 147 } 148 return &RunResult{ 149 Output: append(prefixOutput, output...), 150 Report: rep, 151 Duration: time.Since(start), 152 }, nil 153 } 154 155 func (inst *ExecProgInstance) runBinary(bin string, duration time.Duration) (*RunResult, error) { 156 bin, err := inst.VMInstance.Copy(bin) 157 if err != nil { 158 return nil, &TestError{Title: fmt.Sprintf("failed to copy binary to VM: %v", err)} 159 } 160 return inst.runCommand(bin, duration, binExitConditions) 161 } 162 163 type ExecParams struct { 164 // Only one of these will be used, depending on the function. 165 CProg *prog.Prog 166 SyzProg []byte 167 168 Opts csource.Options 169 Duration time.Duration 170 // If ExitConditions is empty, RunSyzProg() will assume instance.SyzExitConditions. 171 // RunCProg() always runs with binExitConditions. 172 ExitConditions vm.ExitCondition 173 } 174 175 func (inst *ExecProgInstance) RunCProg(params ExecParams) (*RunResult, error) { 176 src, err := csource.Write(params.CProg, params.Opts) 177 if err != nil { 178 return nil, err 179 } 180 inst.Logf(2, "testing compiled C program (duration=%v, %+v): %s", 181 params.Duration, params.Opts, params.CProg) 182 return inst.RunCProgRaw(src, params.CProg.Target, params.Duration) 183 } 184 185 func (inst *ExecProgInstance) RunCProgRaw(src []byte, target *prog.Target, 186 duration time.Duration) (*RunResult, error) { 187 bin, err := csource.BuildNoWarn(target, src) 188 if err != nil { 189 return nil, err 190 } 191 defer os.Remove(bin) 192 return inst.runBinary(bin, duration) 193 } 194 195 func (inst *ExecProgInstance) RunSyzProgFile(progFile string, duration time.Duration, 196 opts csource.Options, exitCondition vm.ExitCondition) (*RunResult, error) { 197 vmProgFile, err := inst.VMInstance.Copy(progFile) 198 if err != nil { 199 return nil, &TestError{Title: fmt.Sprintf("failed to copy prog to VM: %v", err)} 200 } 201 target := inst.mgrCfg.SysTarget 202 command := ExecprogCmd(inst.execprogBin, inst.executorBin, target.OS, target.Arch, inst.mgrCfg.Type, opts, 203 !inst.OldFlagsCompatMode, inst.mgrCfg.Timeouts.Slowdown, vmProgFile) 204 return inst.runCommand(command, duration, exitCondition) 205 } 206 207 func (inst *ExecProgInstance) RunSyzProg(params ExecParams) (*RunResult, error) { 208 progFile, err := osutil.WriteTempFile(params.SyzProg) 209 if err != nil { 210 return nil, err 211 } 212 defer os.Remove(progFile) 213 214 if params.ExitConditions == 0 { 215 params.ExitConditions = SyzExitConditions 216 } 217 return inst.RunSyzProgFile(progFile, params.Duration, params.Opts, params.ExitConditions) 218 }