From 2d51d57a71659b063ddcb21cc50845d05d39708b Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Fri, 1 Apr 2022 13:16:03 +0000 Subject: all: run strace on each found reproducer If `strace_bin` is specified, syzkaller will invoke a reproducer with it and save the output. This should help in debugging. If syz-manager is attached to a dashboard, upload the strace-powered output and report. --- pkg/instance/execprog.go | 26 +++++++++++++++++++ pkg/mgrconfig/config.go | 5 ++++ pkg/mgrconfig/load.go | 6 +++++ pkg/repro/strace.go | 67 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+) create mode 100644 pkg/repro/strace.go (limited to 'pkg') diff --git a/pkg/instance/execprog.go b/pkg/instance/execprog.go index 1d775c22c..f6f9bfa01 100644 --- a/pkg/instance/execprog.go +++ b/pkg/instance/execprog.go @@ -13,6 +13,7 @@ import ( "github.com/google/syzkaller/pkg/osutil" "github.com/google/syzkaller/pkg/report" "github.com/google/syzkaller/prog" + "github.com/google/syzkaller/sys/targets" "github.com/google/syzkaller/vm" ) @@ -23,6 +24,7 @@ type OptionalConfig struct { Logf ExecutorLogger OldFlagsCompatMode bool BeforeContextLen int + StraceBin string } type ExecProgInstance struct { @@ -62,6 +64,14 @@ func SetupExecProg(vmInst *vm.Instance, mgrCfg *mgrconfig.Config, reporter *repo } if opt != nil { ret.OptionalConfig = *opt + if ret.StraceBin != "" { + var err error + ret.StraceBin, err = vmInst.Copy(ret.StraceBin) + if err != nil { + vmInst.Close() + return nil, &TestError{Title: fmt.Sprintf("failed to copy strace bin: %v", err)} + } + } } if ret.Logf == nil { ret.Logf = func(int, string, ...interface{}) {} @@ -87,6 +97,19 @@ func CreateExecProgInstance(vmPool *vm.Pool, vmIndex int, mgrCfg *mgrconfig.Conf } func (inst *ExecProgInstance) runCommand(command string, duration time.Duration) (*RunResult, error) { + var prefixOutput []byte + if inst.StraceBin != "" { + filterCalls := "" + switch inst.mgrCfg.SysTarget.OS { + case targets.Linux: + // wait4 and nanosleep generate a lot of noise, especially when running syz-executor. + // We cut them on the VM side in order to decrease load on the network and to use + // the limited buffer size wisely. + filterCalls = ` -e \!wait4,clock_nanosleep,nanosleep` + } + command = inst.StraceBin + filterCalls + ` -s 100 -x -f ` + command + prefixOutput = []byte(fmt.Sprintf("%s\n\n<...>\n", command)) + } outc, errc, err := inst.VMInstance.Run(duration, nil, command) if err != nil { return nil, fmt.Errorf("failed to run command in VM: %v", err) @@ -95,6 +118,9 @@ func (inst *ExecProgInstance) runCommand(command string, duration time.Duration) ExecutionResult: *inst.VMInstance.MonitorExecutionRaw(outc, errc, inst.reporter, inst.ExitCondition, inst.BeforeContextLen), } + if len(prefixOutput) > 0 { + result.RawOutput = append(prefixOutput, result.RawOutput...) + } if result.Report == nil { inst.Logf(2, "program did not crash") } else { diff --git a/pkg/mgrconfig/config.go b/pkg/mgrconfig/config.go index c13e6f452..20a0b7b57 100644 --- a/pkg/mgrconfig/config.go +++ b/pkg/mgrconfig/config.go @@ -168,6 +168,11 @@ type Config struct { // Regexps are matched against bug title, guilty file and maintainer emails. Interests []string `json:"interests,omitempty"` + // Path to the strace binary compiled for the target architecture. + // If set, for each reproducer syzkaller will run it once more under strace and save + // the output. + StraceBin string `json:"strace_bin"` + // Type of virtual machine to use, e.g. "qemu", "gce", "android", "isolated", etc. Type string `json:"type"` // VM-type-specific parameters. diff --git a/pkg/mgrconfig/load.go b/pkg/mgrconfig/load.go index c9528b38a..2b49d0cbd 100644 --- a/pkg/mgrconfig/load.go +++ b/pkg/mgrconfig/load.go @@ -256,6 +256,12 @@ func (cfg *Config) completeBinaries() error { if cfg.ExecutorBin != "" && !osutil.IsExist(cfg.ExecutorBin) { return fmt.Errorf("bad config syzkaller param: can't find %v", cfg.ExecutorBin) } + if cfg.StraceBin != "" { + if !osutil.IsExist(cfg.StraceBin) { + return fmt.Errorf("bad config param strace_bin: can't find %v", cfg.StraceBin) + } + cfg.StraceBin = osutil.Abs(cfg.StraceBin) + } return nil } diff --git a/pkg/repro/strace.go b/pkg/repro/strace.go new file mode 100644 index 000000000..07c3ea025 --- /dev/null +++ b/pkg/repro/strace.go @@ -0,0 +1,67 @@ +// 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 repro + +import ( + "fmt" + + "github.com/google/syzkaller/pkg/instance" + "github.com/google/syzkaller/pkg/log" + "github.com/google/syzkaller/pkg/mgrconfig" + "github.com/google/syzkaller/pkg/report" + "github.com/google/syzkaller/vm" +) + +type StraceResult struct { + Report *report.Report + Output []byte + Error error +} + +const ( + straceOutputLogSize = 2048 << 10 +) + +func RunStrace(result *Result, cfg *mgrconfig.Config, reporter *report.Reporter, + vmPool *vm.Pool, vmIndex int) *StraceResult { + if cfg.StraceBin == "" { + return straceFailed(fmt.Errorf("strace binary is not set in the config")) + } + inst, err := instance.CreateExecProgInstance(vmPool, vmIndex, cfg, reporter, + &instance.OptionalConfig{ + StraceBin: cfg.StraceBin, + BeforeContextLen: straceOutputLogSize, + }) + if err != nil { + return straceFailed(fmt.Errorf("failed to set up instance: %v", err)) + } + defer inst.VMInstance.Close() + + var runRes *instance.RunResult + if result.CRepro { + log.Logf(1, "running C repro under strace") + runRes, err = inst.RunCProg(result.Prog, result.Duration, result.Opts) + } else { + log.Logf(1, "running syz repro under strace") + runRes, err = inst.RunSyzProg(result.Prog.Serialize(), result.Duration, result.Opts) + } + if err != nil { + return straceFailed(fmt.Errorf("failed to generate strace log: %v", err)) + } + return &StraceResult{ + Report: runRes.Report, + Output: runRes.RawOutput, + } +} + +func straceFailed(err error) *StraceResult { + return &StraceResult{Error: err} +} + +func (strace *StraceResult) IsSameBug(repro *Result) bool { + if strace == nil || strace.Report == nil || repro.Report == nil { + return false + } + return strace.Report.Title == repro.Report.Title +} -- cgit mrf-deployment