aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/runtest/run_test.go
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2024-06-04 12:55:41 +0200
committerDmitry Vyukov <dvyukov@google.com>2024-06-24 09:57:34 +0000
commite16e2c9a4cb6937323e861b646792a6c4c978a3c (patch)
tree6c513e98e5f465b44a98546d8984485d2c128582 /pkg/runtest/run_test.go
parent90d67044dab68568e8f35bc14b68055dbd166eff (diff)
executor: add runner mode
Move all syz-fuzzer logic into syz-executor and remove syz-fuzzer. Also restore syz-runtest functionality in the manager. Update #4917 (sets most signal handlers to SIG_IGN)
Diffstat (limited to 'pkg/runtest/run_test.go')
-rw-r--r--pkg/runtest/run_test.go175
1 files changed, 66 insertions, 109 deletions
diff --git a/pkg/runtest/run_test.go b/pkg/runtest/run_test.go
index f04ad4b0f..92b6c2d77 100644
--- a/pkg/runtest/run_test.go
+++ b/pkg/runtest/run_test.go
@@ -8,41 +8,41 @@ import (
"context"
"encoding/binary"
"encoding/hex"
- "errors"
"flag"
"fmt"
- "os"
"path/filepath"
"runtime"
"testing"
- "time"
"github.com/google/syzkaller/pkg/csource"
"github.com/google/syzkaller/pkg/flatrpc"
"github.com/google/syzkaller/pkg/fuzzer/queue"
- "github.com/google/syzkaller/pkg/ipc"
"github.com/google/syzkaller/pkg/osutil"
+ "github.com/google/syzkaller/pkg/rpcserver"
"github.com/google/syzkaller/pkg/testutil"
+ "github.com/google/syzkaller/pkg/vminfo"
"github.com/google/syzkaller/prog"
"github.com/google/syzkaller/sys/targets"
_ "github.com/google/syzkaller/sys/test/gen" // pull in the test target
"github.com/stretchr/testify/assert"
)
-// Can be used as:
-// go test -v -run=Test/64_fork ./pkg/runtest -filter=nonfailing
-// to select a subset of tests to run.
-var flagFilter = flag.String("filter", "", "prefix to match test file names")
-
-var flagDebug = flag.Bool("debug", false, "include debug output from the executor")
+var (
+ // Can be used as:
+ // go test -v -run=Test/64_fork ./pkg/runtest -filter=nonfailing
+ // to select a subset of tests to run.
+ flagFilter = flag.String("filter", "", "prefix to match test file names")
+ flagDebug = flag.Bool("debug", false, "include debug output from the executor")
+ flagGDB = flag.Bool("gdb", false, "run executor under gdb")
+)
-func Test(t *testing.T) {
+func TestUnit(t *testing.T) {
switch runtime.GOOS {
case targets.OpenBSD:
t.Skipf("broken on %v", runtime.GOOS)
}
// Test only one target in short mode (each takes 5+ seconds to run).
- shortTarget := targets.Get(targets.TestOS, targets.TestArch64)
+ shortTarget := targets.Get(targets.TestOS, targets.TestArch64Fork)
for _, sysTarget := range targets.List[targets.TestOS] {
if testing.Short() && sysTarget != shortTarget {
continue
@@ -83,27 +83,7 @@ func test(t *testing.T, sysTarget *targets.Target) {
Verbose: true,
Debug: *flagDebug,
}
-
- executorCtx, cancel := context.WithCancel(context.Background())
- t.Cleanup(cancel)
- go func() {
- for {
- select {
- case <-time.After(time.Millisecond):
- case <-executorCtx.Done():
- return
- }
- req := ctx.Next()
- if req == nil {
- continue
- }
- if req.BinaryFile != "" {
- req.Done(runTestC(req))
- } else {
- req.Done(runTest(req, executor))
- }
- }
- }()
+ startRpcserver(t, target, executor, ctx)
if err := ctx.Run(); err != nil {
t.Fatal(err)
}
@@ -114,7 +94,7 @@ func TestCover(t *testing.T) {
// We inject given blobs into KCOV buffer using syz_inject_cover,
// and then test what we get back.
t.Parallel()
- for _, arch := range []string{targets.TestArch32, targets.TestArch64} {
+ for _, arch := range []string{targets.TestArch32, targets.TestArch64, targets.TestArch64Fork} {
sysTarget := targets.Get(targets.TestOS, arch)
t.Run(arch, func(t *testing.T) {
if sysTarget.BrokenCompiler != "" {
@@ -202,15 +182,15 @@ func testCover(t *testing.T, target *prog.Target) {
Input: makeCover64(0xc0dec0dec0000033, 0xc0dec0dec0000022, 0xc0dec0dec0000011,
0xc0dec0dec0000011, 0xc0dec0dec0000022, 0xc0dec0dec0000033, 0xc0dec0dec0000011),
Flags: flatrpc.ExecFlagCollectCover,
- Cover: []uint64{0xc0dec0dec0000033, 0xc0dec0dec0000022, 0xc0dec0dec0000011,
- 0xc0dec0dec0000011, 0xc0dec0dec0000022, 0xc0dec0dec0000033, 0xc0dec0dec0000011},
+ Cover: []uint64{0xc0dec0dec0000011, 0xc0dec0dec0000033, 0xc0dec0dec0000022,
+ 0xc0dec0dec0000011, 0xc0dec0dec0000011, 0xc0dec0dec0000022, 0xc0dec0dec0000033},
},
{
Is64Bit: 1,
Input: makeCover64(0xc0dec0dec0000033, 0xc0dec0dec0000022, 0xc0dec0dec0000011,
0xc0dec0dec0000011, 0xc0dec0dec0000022, 0xc0dec0dec0000033, 0xc0dec0dec0000011),
Flags: flatrpc.ExecFlagCollectCover | flatrpc.ExecFlagDedupCover,
- Cover: []uint64{0xc0dec0dec0000011, 0xc0dec0dec0000022, 0xc0dec0dec0000033},
+ Cover: []uint64{0xc0dec0dec0000033, 0xc0dec0dec0000022, 0xc0dec0dec0000011},
},
// Signal hashing.
{
@@ -218,8 +198,8 @@ func testCover(t *testing.T, target *prog.Target) {
Input: makeCover64(0xc0dec0dec0011001, 0xc0dec0dec0022002, 0xc0dec0dec00330f0,
0xc0dec0dec0044b00, 0xc0dec0dec0011001, 0xc0dec0dec0022002),
Flags: flatrpc.ExecFlagCollectSignal,
- Signal: []uint64{0xc0dec0dec0011001, 0xc0dec0dec0022003, 0xc0dec0dec00330f2,
- 0xc0dec0dec0044bf0, 0xc0dec0dec0011b01},
+ Signal: []uint64{0xc0dec0dec0011b01, 0xc0dec0dec0044bf0, 0xc0dec0dec00330f2,
+ 0xc0dec0dec0022003, 0xc0dec0dec0011001},
},
// Invalid non-kernel PCs must fail test execution.
{
@@ -296,38 +276,49 @@ func testCover(t *testing.T, target *prog.Target) {
// TODO: test max signal filtering and cover filter when syz-executor handles them.
}
executor := csource.BuildExecutor(t, target, "../../")
+ source := queue.Plain()
+ startRpcserver(t, target, executor, source)
for i, test := range tests {
test := test
t.Run(fmt.Sprint(i), func(t *testing.T) {
t.Parallel()
- testCover1(t, target, executor, test)
+ testCover1(t, target, test, source)
})
}
}
-func testCover1(t *testing.T, target *prog.Target, executor string, test CoverTest) {
+func testCover1(t *testing.T, target *prog.Target, test CoverTest, source *queue.PlainQueue) {
text := fmt.Sprintf(`syz_inject_cover(0x%v, &AUTO="%s", AUTO)`, test.Is64Bit, hex.EncodeToString(test.Input))
p, err := target.Deserialize([]byte(text), prog.Strict)
if err != nil {
t.Fatal(err)
}
req := &queue.Request{
- Prog: p,
- Repeat: 1,
+ Prog: p,
ExecOpts: flatrpc.ExecOpts{
- EnvFlags: flatrpc.ExecEnvSignal,
+ EnvFlags: flatrpc.ExecEnvSignal | flatrpc.ExecEnvSandboxNone,
ExecFlags: test.Flags,
},
}
- res := runTest(req, executor)
+ if test.Flags&flatrpc.ExecFlagCollectSignal != 0 {
+ req.ReturnAllSignal = []int{0}
+ }
+ source.Submit(req)
+ res := req.Wait(context.Background())
if res.Err != nil || res.Info == nil || len(res.Info.Calls) != 1 || res.Info.Calls[0] == nil {
- t.Fatalf("program execution failed: %v\n%s", res.Err, res.Output)
+ t.Fatalf("program execution failed: status=%v err=%v\n%s", res.Status, res.Err, res.Output)
}
call := res.Info.Calls[0]
var comps [][2]uint64
for _, cmp := range call.Comps {
comps = append(comps, [2]uint64{cmp.Op1, cmp.Op2})
}
+ if test.Cover == nil {
+ test.Cover = []uint64{}
+ }
+ if test.Signal == nil {
+ test.Signal = []uint64{}
+ }
assert.Equal(t, test.Cover, call.Cover)
assert.Equal(t, test.Signal, call.Signal)
// Comparisons are reordered and order does not matter, so compare without order.
@@ -361,72 +352,38 @@ func makeComps(comps ...Comparison) []byte {
return w.Bytes()
}
-func runTest(req *queue.Request, executor string) *queue.Result {
- cfg := new(ipc.Config)
- sysTarget := targets.Get(req.Prog.Target.OS, req.Prog.Target.Arch)
- cfg.UseForkServer = sysTarget.ExecutorUsesForkServer
- cfg.Timeouts = sysTarget.Timeouts(1)
- cfg.Executor = executor
- env, err := ipc.MakeEnv(cfg, 0)
- if err != nil {
- return &queue.Result{
- Status: queue.ExecFailure,
- Err: fmt.Errorf("failed to create ipc env: %w", err),
- }
+func startRpcserver(t *testing.T, target *prog.Target, executor string, source queue.Source) {
+ ctx, done := context.WithCancel(context.Background())
+ cfg := &rpcserver.LocalConfig{
+ Config: rpcserver.Config{
+ Config: vminfo.Config{
+ Target: target,
+ Debug: *flagDebug,
+ Features: flatrpc.FeatureSandboxNone,
+ Sandbox: flatrpc.ExecEnvSandboxNone,
+ },
+ Procs: runtime.GOMAXPROCS(0),
+ Slowdown: 10, // to deflake slower tests
+ },
+ Executor: executor,
+ Dir: t.TempDir(),
+ Context: ctx,
+ GDB: *flagGDB,
}
- defer env.Close()
- ret := &queue.Result{Status: queue.Success}
- for run := 0; run < req.Repeat; run++ {
- if run%2 == 0 {
- // Recreate Env every few iterations, this allows to cover more paths.
- env.ForceRestart()
- }
- output, info, hanged, err := env.Exec(&req.ExecOpts, req.Prog)
- ret.Output = append(ret.Output, output...)
- if err != nil {
- return &queue.Result{
- Status: queue.ExecFailure,
- Err: fmt.Errorf("run %v: failed to run: %w", run, err),
- }
- }
- if hanged {
- return &queue.Result{
- Status: queue.ExecFailure,
- Err: fmt.Errorf("run %v: hanged", run),
- }
- }
- if run == 0 {
- ret.Info = info
- } else {
- ret.Info.Calls = append(ret.Info.Calls, info.Calls...)
- }
+ cfg.MachineChecked = func(features flatrpc.Feature, syscalls map[*prog.Syscall]bool) queue.Source {
+ cfg.Cover = true
+ return source
}
- return ret
-}
-
-func runTestC(req *queue.Request) *queue.Result {
- tmpDir, err := os.MkdirTemp("", "syz-runtest")
- if err != nil {
- return &queue.Result{
- Status: queue.ExecFailure,
- Err: fmt.Errorf("failed to create temp dir: %w", err),
+ errc := make(chan error)
+ go func() {
+ errc <- rpcserver.RunLocal(cfg)
+ }()
+ t.Cleanup(func() {
+ done()
+ if err := <-errc; err != nil {
+ t.Fatal(err)
}
- }
- defer os.RemoveAll(tmpDir)
- cmd := osutil.Command(req.BinaryFile)
- cmd.Dir = tmpDir
- // Tell ASAN to not mess with our NONFAILING.
- cmd.Env = append(append([]string{}, os.Environ()...), "ASAN_OPTIONS=handle_segv=0 allow_user_segv_handler=1")
- res := &queue.Result{}
- res.Output, res.Err = osutil.Run(20*time.Second, cmd)
- var verr *osutil.VerboseError
- if errors.As(res.Err, &verr) {
- // The process can legitimately do something like exit_group(1).
- // So we ignore the error and rely on the rest of the checks (e.g. syscall return values).
- res.Err = nil
- res.Output = verr.Output
- }
- return res
+ })
}
func TestParsing(t *testing.T) {