diff options
| author | Aleksandr Nogikh <nogikh@google.com> | 2022-03-31 18:26:42 +0000 |
|---|---|---|
| committer | Aleksandr Nogikh <wp32pw@gmail.com> | 2022-04-29 17:16:33 +0200 |
| commit | a04ae3093de7eebc147770fe38a5bda96b5c0634 (patch) | |
| tree | 5d7062a73b354b5f1906847835e1c00804a572a5 /pkg/instance | |
| parent | 7f2625f8905f7d981475fa314c3d06c56c7bd4b0 (diff) | |
all: use the same prog execution code throughout the project
Previously it was copypasted in pkg/instance, pkg/repro,
tools/syz-crash. Use the single implementation instead.
Also, this commit fixes a bug - the previous code always set collide to
true while reproducing a bug, which led to an immediate syz-exexprog's
exit. As a result, newer bugs with .syz repro only were never actually
reproduced on #syz test requests.
Diffstat (limited to 'pkg/instance')
| -rw-r--r-- | pkg/instance/execprog.go | 159 | ||||
| -rw-r--r-- | pkg/instance/instance.go | 86 |
2 files changed, 183 insertions, 62 deletions
diff --git a/pkg/instance/execprog.go b/pkg/instance/execprog.go new file mode 100644 index 000000000..7610298a6 --- /dev/null +++ b/pkg/instance/execprog.go @@ -0,0 +1,159 @@ +// Copyright 2022 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +package instance + +import ( + "fmt" + "os" + "time" + + "github.com/google/syzkaller/pkg/csource" + "github.com/google/syzkaller/pkg/mgrconfig" + "github.com/google/syzkaller/pkg/osutil" + "github.com/google/syzkaller/pkg/report" + "github.com/google/syzkaller/prog" + "github.com/google/syzkaller/vm" +) + +type ExecutorLogger func(int, string, ...interface{}) + +type OptionalConfig struct { + ExitCondition vm.ExitCondition + Logf ExecutorLogger + OldFlagsCompatMode bool +} + +type ExecProgInstance struct { + execprogBin string + executorBin string + reporter *report.Reporter + mgrCfg *mgrconfig.Config + VMInstance *vm.Instance + OptionalConfig +} + +type RunResult struct { + Report *report.Report +} + +func SetupExecProg(vmInst *vm.Instance, mgrCfg *mgrconfig.Config, reporter *report.Reporter, + opt *OptionalConfig) (*ExecProgInstance, error) { + execprogBin, err := vmInst.Copy(mgrCfg.ExecprogBin) + if err != nil { + vmInst.Close() + return nil, &TestError{Title: fmt.Sprintf("failed to copy syz-execprog to VM: %v", err)} + } + executorBin := mgrCfg.SysTarget.ExecutorBin + if executorBin == "" { + executorBin, err = vmInst.Copy(mgrCfg.ExecutorBin) + if err != nil { + vmInst.Close() + return nil, &TestError{Title: fmt.Sprintf("failed to copy syz-executor to VM: %v", err)} + } + } + ret := &ExecProgInstance{ + execprogBin: execprogBin, + executorBin: executorBin, + reporter: reporter, + mgrCfg: mgrCfg, + VMInstance: vmInst, + } + if opt != nil { + ret.OptionalConfig = *opt + } + if ret.Logf == nil { + ret.Logf = func(int, string, ...interface{}) {} + } + if ret.ExitCondition == 0 { + ret.ExitCondition = vm.ExitTimeout | vm.ExitNormal | vm.ExitError + } + return ret, nil +} + +func CreateExecProgInstance(vmPool *vm.Pool, vmIndex int, mgrCfg *mgrconfig.Config, + reporter *report.Reporter, opt *OptionalConfig) (*ExecProgInstance, error) { + vmInst, err := vmPool.Create(vmIndex) + if err != nil { + return nil, fmt.Errorf("failed to create VM: %v", err) + } + ret, err := SetupExecProg(vmInst, mgrCfg, reporter, opt) + if err != nil { + vmInst.Close() + return nil, err + } + return ret, nil +} + +func (inst *ExecProgInstance) runCommand(command string, duration time.Duration) (*RunResult, error) { + outc, errc, err := inst.VMInstance.Run(duration, nil, command) + if err != nil { + return nil, fmt.Errorf("failed to run command in VM: %v", err) + } + result := &RunResult{} + result.Report = inst.VMInstance.MonitorExecution(outc, errc, inst.reporter, inst.ExitCondition) + if result.Report == nil { + inst.Logf(2, "program did not crash") + } else { + if err := inst.reporter.Symbolize(result.Report); err != nil { + return nil, fmt.Errorf("failed to symbolize report: %v", err) + } + inst.Logf(2, "program crashed: %v", result.Report.Title) + } + return result, nil +} + +func (inst *ExecProgInstance) runBinary(bin string, duration time.Duration) (*RunResult, error) { + bin, err := inst.VMInstance.Copy(bin) + if err != nil { + return nil, &TestError{Title: fmt.Sprintf("failed to copy binary to VM: %v", err)} + } + return inst.runCommand(bin, duration) +} + +func (inst *ExecProgInstance) RunCProg(p *prog.Prog, duration time.Duration, + opts csource.Options) (*RunResult, error) { + src, err := csource.Write(p, opts) + if err != nil { + return nil, err + } + inst.Logf(2, "testing compiled C program (duration=%v, %+v): %s", duration, opts, p) + return inst.RunCProgRaw(src, p.Target, duration) +} + +func (inst *ExecProgInstance) RunCProgRaw(src []byte, target *prog.Target, + duration time.Duration) (*RunResult, error) { + bin, err := csource.BuildNoWarn(target, src) + if err != nil { + return nil, err + } + defer os.Remove(bin) + return inst.runBinary(bin, duration) +} + +func (inst *ExecProgInstance) RunSyzProgFile(progFile string, duration time.Duration, + opts csource.Options) (*RunResult, error) { + vmProgFile, err := inst.VMInstance.Copy(progFile) + if err != nil { + return nil, &TestError{Title: fmt.Sprintf("failed to copy prog to VM: %v", err)} + } + target := inst.mgrCfg.SysTarget + faultCall := -1 + if opts.Fault { + faultCall = opts.FaultCall + } + command := ExecprogCmd(inst.execprogBin, inst.executorBin, target.OS, target.Arch, opts.Sandbox, + opts.Repeat, opts.Threaded, opts.Collide, opts.Procs, faultCall, opts.FaultNth, + !inst.OldFlagsCompatMode, inst.mgrCfg.Timeouts.Slowdown, vmProgFile) + return inst.runCommand(command, duration) +} + +func (inst *ExecProgInstance) RunSyzProg(syzProg []byte, duration time.Duration, + opts csource.Options) (*RunResult, error) { + progFile, err := osutil.WriteTempFile(syzProg) + if err != nil { + return nil, err + } + defer os.Remove(progFile) + return inst.RunSyzProgFile(progFile, duration, opts) +} diff --git a/pkg/instance/instance.go b/pkg/instance/instance.go index a3a87d338..218408191 100644 --- a/pkg/instance/instance.go +++ b/pkg/instance/instance.go @@ -360,30 +360,22 @@ func (inst *inst) testInstance() error { } func (inst *inst) testRepro() error { - cfg := inst.cfg - if len(inst.reproSyz) > 0 { - execprogBin, err := inst.vm.Copy(cfg.ExecprogBin) - if err != nil { - return &TestError{Title: fmt.Sprintf("failed to copy test binary to VM: %v", err)} - } - // If ExecutorBin is provided, it means that syz-executor is already in the image, - // so no need to copy it. - executorBin := cfg.SysTarget.ExecutorBin - if executorBin == "" { - executorBin, err = inst.vm.Copy(inst.cfg.ExecutorBin) - if err != nil { - return &TestError{Title: fmt.Sprintf("failed to copy test binary to VM: %v", err)} - } - } - progFile := filepath.Join(cfg.Workdir, "repro.prog") - if err := osutil.WriteFile(progFile, inst.reproSyz); err != nil { - return fmt.Errorf("failed to write temp file: %v", err) - } - vmProgFile, err := inst.vm.Copy(progFile) - if err != nil { - return &TestError{Title: fmt.Sprintf("failed to copy test binary to VM: %v", err)} + var err error + execProg, err := SetupExecProg(inst.vm, inst.cfg, inst.reporter, &OptionalConfig{ + OldFlagsCompatMode: !inst.optionalFlags, + }) + if err != nil { + return err + } + transformError := func(res *RunResult, err error) error { + if err != nil && res.Report != nil { + return &CrashError{Report: res.Report} } - opts, err := csource.DeserializeOptions(inst.reproOpts) + return err + } + if len(inst.reproSyz) > 0 { + var opts csource.Options + opts, err = csource.DeserializeOptions(inst.reproOpts) if err != nil { return err } @@ -394,47 +386,17 @@ func (inst *inst) testRepro() error { if opts.Sandbox == "" { opts.Sandbox = "none" } - if !opts.Fault { - opts.FaultCall = -1 - } - cmdSyz := ExecprogCmd(execprogBin, executorBin, cfg.TargetOS, cfg.TargetArch, opts.Sandbox, - true, true, opts.Collide, cfg.Procs, opts.FaultCall, opts.FaultNth, inst.optionalFlags, - cfg.Timeouts.Slowdown, vmProgFile) - if err := inst.testProgram(cmdSyz, cfg.Timeouts.NoOutputRunningTime); err != nil { - return err - } - } - if len(inst.reproC) == 0 { - return nil - } - bin, err := csource.BuildNoWarn(cfg.Target, inst.reproC) - if err != nil { - return err - } - defer os.Remove(bin) - vmBin, err := inst.vm.Copy(bin) - if err != nil { - return &TestError{Title: fmt.Sprintf("failed to copy test binary to VM: %v", err)} - } - // We should test for more than full "no output" timeout, but the problem is that C reproducers - // don't print anything, so we will get a false "no output" crash. - return inst.testProgram(vmBin, cfg.Timeouts.NoOutput/2) -} - -func (inst *inst) testProgram(command string, testTime time.Duration) error { - outc, errc, err := inst.vm.Run(testTime, nil, command) - if err != nil { - return fmt.Errorf("failed to run binary in VM: %v", err) - } - rep := inst.vm.MonitorExecution(outc, errc, inst.reporter, - vm.ExitTimeout|vm.ExitNormal|vm.ExitError) - if rep == nil { - return nil + opts.Repeat, opts.Threaded = true, true + err = transformError(execProg.RunSyzProg(inst.reproSyz, + inst.cfg.Timeouts.NoOutputRunningTime, opts)) } - if err := inst.reporter.Symbolize(rep); err != nil { - log.Logf(0, "failed to symbolize report: %v", err) + if err == nil && len(inst.reproC) > 0 { + // We should test for more than full "no output" timeout, but the problem is that C reproducers + // don't print anything, so we will get a false "no output" crash. + err = transformError(execProg.RunCProgRaw(inst.reproC, inst.cfg.Target, + inst.cfg.Timeouts.NoOutput/2)) } - return &CrashError{Report: rep} + return err } type OptionalFuzzerArgs struct { |
