aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2023-05-24 17:40:41 +0200
committerAleksandr Nogikh <wp32pw@gmail.com>2023-05-25 11:45:51 +0200
commit453f56a20332a18969781dc145c8ae8da75468ea (patch)
tree200eabe3b9e37f0e46561b5a22f9a68454cca1a1
parentb36c307e4d7a49e7b738ab63576854eb43855e12 (diff)
pkg/repro: add a smoke test
This is a sanity test for the overall pkg/repro machinery. It does not focus on minor corner cases.
-rw-r--r--pkg/repro/repro.go12
-rw-r--r--pkg/repro/repro_test.go111
2 files changed, 117 insertions, 6 deletions
diff --git a/pkg/repro/repro.go b/pkg/repro/repro.go
index 6df634960..b030d38bd 100644
--- a/pkg/repro/repro.go
+++ b/pkg/repro/repro.go
@@ -73,7 +73,7 @@ var ErrNoPrograms = errors.New("crash log does not contain any programs")
func Run(crashLog []byte, cfg *mgrconfig.Config, features *host.Features, reporter *report.Reporter,
vmPool *vm.Pool, vmIndexes []int) (*Result, *Stats, error) {
- ctx, err := prepareCtx(crashLog, cfg, features, reporter, vmIndexes)
+ ctx, err := prepareCtx(crashLog, cfg, features, reporter, len(vmIndexes))
if err != nil {
return nil, nil, err
}
@@ -82,8 +82,8 @@ func Run(crashLog []byte, cfg *mgrconfig.Config, features *host.Features, report
}
func prepareCtx(crashLog []byte, cfg *mgrconfig.Config, features *host.Features, reporter *report.Reporter,
- vmIndexes []int) (*context, error) {
- if len(vmIndexes) == 0 {
+ VMs int) (*context, error) {
+ if VMs == 0 {
return nil, fmt.Errorf("no VMs provided")
}
entries := cfg.Target.ParseLog(crashLog)
@@ -123,14 +123,14 @@ func prepareCtx(crashLog []byte, cfg *mgrconfig.Config, features *host.Features,
crashType: crashType,
crashStart: crashStart,
entries: entries,
- instances: make(chan *reproInstance, len(vmIndexes)),
- bootRequests: make(chan int, len(vmIndexes)),
+ instances: make(chan *reproInstance, VMs),
+ bootRequests: make(chan int, VMs),
testTimeouts: testTimeouts,
startOpts: createStartOptions(cfg, features, crashType),
stats: new(Stats),
timeouts: cfg.Timeouts,
}
- ctx.reproLogf(0, "%v programs, %v VMs, timeouts %v", len(entries), len(vmIndexes), testTimeouts)
+ ctx.reproLogf(0, "%v programs, %v VMs, timeouts %v", len(entries), VMs, testTimeouts)
return ctx, nil
}
diff --git a/pkg/repro/repro_test.go b/pkg/repro/repro_test.go
index 486dc963b..51c99eb75 100644
--- a/pkg/repro/repro_test.go
+++ b/pkg/repro/repro_test.go
@@ -5,9 +5,16 @@ package repro
import (
"math/rand"
+ "regexp"
+ "sync"
"testing"
+ "time"
+ "github.com/google/go-cmp/cmp"
"github.com/google/syzkaller/pkg/csource"
+ "github.com/google/syzkaller/pkg/instance"
+ "github.com/google/syzkaller/pkg/mgrconfig"
+ "github.com/google/syzkaller/pkg/report"
"github.com/google/syzkaller/pkg/testutil"
"github.com/google/syzkaller/prog"
"github.com/google/syzkaller/sys/targets"
@@ -98,3 +105,107 @@ func TestSimplifies(t *testing.T) {
}
check(opts, 0)
}
+
+func generateTestInstances(ctx *context, count int, execInterface execInterface) {
+ for i := 0; i < count; i++ {
+ ctx.bootRequests <- i
+ }
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ for vmIndex := range ctx.bootRequests {
+ ctx.instances <- &reproInstance{execProg: execInterface, index: vmIndex}
+ }
+ }()
+ wg.Wait()
+ close(ctx.instances)
+}
+
+type testExecInterface struct {
+ t *testing.T
+ // For now only do the simplest imitation.
+ run func([]byte) (*instance.RunResult, error)
+}
+
+func (tei *testExecInterface) Close() {}
+
+func (tei *testExecInterface) RunCProg(p *prog.Prog, duration time.Duration,
+ opts csource.Options) (*instance.RunResult, error) {
+ return tei.RunSyzProg(p.Serialize(), duration, opts)
+}
+
+func (tei *testExecInterface) RunSyzProg(syzProg []byte, duration time.Duration,
+ opts csource.Options) (*instance.RunResult, error) {
+ return tei.run(syzProg)
+}
+
+func prepareTestCtx(t *testing.T, log string) *context {
+ mgrConfig := &mgrconfig.Config{
+ Derived: mgrconfig.Derived{
+ TargetOS: targets.Linux,
+ TargetVMArch: targets.AMD64,
+ },
+ Sandbox: "namespace",
+ }
+ var err error
+ mgrConfig.Target, err = prog.GetTarget(targets.Linux, targets.AMD64)
+ if err != nil {
+ t.Fatal(err)
+ }
+ reporter, err := report.NewReporter(mgrConfig)
+ if err != nil {
+ t.Fatal(err)
+ }
+ ctx, err := prepareCtx([]byte(log), mgrConfig, nil, reporter, 3)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return ctx
+}
+
+const testReproLog = `
+2015/12/21 12:18:05 executing program 1:
+getpid()
+pause()
+2015/12/21 12:18:10 executing program 2:
+getpid()
+getuid()
+2015/12/21 12:18:15 executing program 1:
+alarm(0x5)
+pause()
+2015/12/21 12:18:20 executing program 3:
+alarm(0xa)
+getpid()
+`
+
+// Just a pkg/repro smoke test: check that we can extract a two-call reproducer.
+// No focus on error handling and minor corner cases.
+func TestPlainRepro(t *testing.T) {
+ ctx := prepareTestCtx(t, testReproLog)
+ // Only crash if `pause()` is followed by `alarm(0xa)`.
+ var match = regexp.MustCompile(`(?s)pause\(\).*alarm\(0xa\)`)
+ go generateTestInstances(ctx, 3, &testExecInterface{
+ t: t,
+ run: func(log []byte) (*instance.RunResult, error) {
+ crash := match.Match(log)
+ if crash {
+ ret := &instance.RunResult{}
+ ret.Report = &report.Report{
+ Title: `some crash`,
+ }
+ return ret, nil
+ }
+ return &instance.RunResult{}, nil
+ },
+ })
+ result, _, err := ctx.run()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if diff := cmp.Diff(`pause()
+alarm(0xa)
+`, string(result.Prog.Serialize())); diff != "" {
+ t.Fatal(diff)
+ }
+}