github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/runtest/run_test.go (about)

     1  // Copyright 2018 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 runtest
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"encoding/binary"
    10  	"encoding/hex"
    11  	"flag"
    12  	"fmt"
    13  	"os"
    14  	"path/filepath"
    15  	"runtime"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/google/syzkaller/pkg/csource"
    20  	"github.com/google/syzkaller/pkg/flatrpc"
    21  	"github.com/google/syzkaller/pkg/fuzzer/queue"
    22  	"github.com/google/syzkaller/pkg/osutil"
    23  	"github.com/google/syzkaller/pkg/rpcserver"
    24  	"github.com/google/syzkaller/pkg/testutil"
    25  	"github.com/google/syzkaller/pkg/vminfo"
    26  	"github.com/google/syzkaller/prog"
    27  	_ "github.com/google/syzkaller/sys"
    28  	"github.com/google/syzkaller/sys/targets"
    29  	"github.com/stretchr/testify/assert"
    30  )
    31  
    32  var (
    33  	// Can be used as:
    34  	// go test -v -run=Test/64_fork ./pkg/runtest -filter=nonfailing
    35  	// to select a subset of tests to run.
    36  	flagFilter = flag.String("filter", "", "prefix to match test file names")
    37  	flagDebug  = flag.Bool("debug", false, "include debug output from the executor")
    38  	flagGDB    = flag.Bool("gdb", false, "run executor under gdb")
    39  )
    40  
    41  func TestUnit(t *testing.T) {
    42  	switch runtime.GOOS {
    43  	case targets.OpenBSD:
    44  		t.Skipf("broken on %v", runtime.GOOS)
    45  	}
    46  	// Test only one target in short mode (each takes 5+ seconds to run).
    47  	shortTarget := targets.Get(targets.TestOS, targets.TestArch64Fork)
    48  	for _, sysTarget := range targets.List[targets.TestOS] {
    49  		if testing.Short() && sysTarget != shortTarget {
    50  			continue
    51  		}
    52  		sysTarget1 := targets.Get(sysTarget.OS, sysTarget.Arch)
    53  		t.Run(sysTarget1.Arch, func(t *testing.T) {
    54  			t.Parallel()
    55  			test(t, sysTarget1)
    56  		})
    57  	}
    58  }
    59  
    60  func test(t *testing.T, sysTarget *targets.Target) {
    61  	target, err := prog.GetTarget(sysTarget.OS, sysTarget.Arch)
    62  	if err != nil {
    63  		t.Fatal(err)
    64  	}
    65  	executor := csource.BuildExecutor(t, target, "../../", "-fsanitize-coverage=trace-pc")
    66  	calls := make(map[*prog.Syscall]bool)
    67  	for _, call := range target.Syscalls {
    68  		calls[call] = true
    69  	}
    70  	enabledCalls := map[string]map[*prog.Syscall]bool{
    71  		"":     calls,
    72  		"none": calls,
    73  	}
    74  	ctx := &Context{
    75  		Dir:          filepath.Join("..", "..", "sys", target.OS, targets.TestOS),
    76  		Target:       target,
    77  		Tests:        *flagFilter,
    78  		Features:     0,
    79  		EnabledCalls: enabledCalls,
    80  		LogFunc: func(text string) {
    81  			t.Helper()
    82  			t.Log(text)
    83  		},
    84  		Retries: 7, // empirical number that seem to reduce flakes to zero
    85  		Verbose: true,
    86  		Debug:   *flagDebug,
    87  	}
    88  	ctx.Init()
    89  	waitCtx := startRPCServer(t, target, executor, ctx, rpcParams{
    90  		manyProcs: true,
    91  		machineChecked: func(features flatrpc.Feature) {
    92  			// Features we expect to be enabled on the test OS.
    93  			// All sandboxes except for none are not implemented, coverage is not returned,
    94  			// and setup for few features is failing specifically to test feature detection.
    95  			want := flatrpc.FeatureCoverage |
    96  				flatrpc.FeatureExtraCoverage |
    97  				flatrpc.FeatureDelayKcovMmap |
    98  				flatrpc.FeatureKcovResetIoctl |
    99  				flatrpc.FeatureSandboxNone |
   100  				flatrpc.FeatureFault |
   101  				flatrpc.FeatureNetDevices |
   102  				flatrpc.FeatureKCSAN |
   103  				flatrpc.FeatureNicVF |
   104  				flatrpc.FeatureUSBEmulation |
   105  				flatrpc.FeatureVhciInjection |
   106  				flatrpc.FeatureWifiEmulation |
   107  				flatrpc.FeatureLRWPANEmulation |
   108  				flatrpc.FeatureBinFmtMisc |
   109  				flatrpc.FeatureSwap
   110  			for feat, name := range flatrpc.EnumNamesFeature {
   111  				if features&feat != want&feat {
   112  					t.Errorf("expect feature %v to be %v, but it is %v",
   113  						name, want&feat != 0, features&feat != 0)
   114  				}
   115  			}
   116  		},
   117  	})
   118  	if t.Failed() {
   119  		return
   120  	}
   121  	if err := ctx.Run(waitCtx); err != nil {
   122  		t.Fatal(err)
   123  	}
   124  }
   125  
   126  func TestCover(t *testing.T) {
   127  	// End-to-end test for coverage/signal/comparisons collection.
   128  	// We inject given blobs into KCOV buffer using syz_inject_cover,
   129  	// and then test what we get back.
   130  	t.Parallel()
   131  	for _, arch := range []string{targets.TestArch32, targets.TestArch64, targets.TestArch64Fork} {
   132  		sysTarget := targets.Get(targets.TestOS, arch)
   133  		t.Run(arch, func(t *testing.T) {
   134  			if sysTarget.BrokenCompiler != "" {
   135  				t.Skipf("skipping due to broken compiler:\n%v", sysTarget.BrokenCompiler)
   136  			}
   137  			target, err := prog.GetTarget(targets.TestOS, arch)
   138  			if err != nil {
   139  				t.Fatal(err)
   140  			}
   141  			t.Parallel()
   142  			testCover(t, target)
   143  		})
   144  	}
   145  }
   146  
   147  type CoverTest struct {
   148  	Is64Bit         bool
   149  	ExtraCoverage   bool
   150  	Input           []byte
   151  	MaxSignal       []uint64
   152  	CoverFilter     []uint64
   153  	ReturnAllSignal bool
   154  	Flags           flatrpc.ExecFlag
   155  	Cover           []uint64
   156  	Signal          []uint64
   157  	Comps           []*flatrpc.Comparison
   158  }
   159  
   160  type Comparison struct {
   161  	Type uint64
   162  	Arg1 uint64
   163  	Arg2 uint64
   164  	PC   uint64
   165  }
   166  
   167  const (
   168  	CmpConst = 1
   169  	CmpSize1 = 0
   170  	CmpSize2 = 2
   171  	CmpSize4 = 4
   172  	CmpSize8 = 6
   173  )
   174  
   175  func testCover(t *testing.T, target *prog.Target) {
   176  	tests := []CoverTest{
   177  		// Empty coverage.
   178  		{
   179  			Is64Bit: true,
   180  			Input:   makeCover64(),
   181  			Flags:   flatrpc.ExecFlagCollectSignal | flatrpc.ExecFlagCollectCover,
   182  		},
   183  		{
   184  			Is64Bit: false,
   185  			Input:   makeCover32(),
   186  			Flags:   flatrpc.ExecFlagCollectSignal | flatrpc.ExecFlagCollectCover,
   187  		},
   188  		// Single 64-bit PC.
   189  		{
   190  			Is64Bit: true,
   191  			Input:   makeCover64(0xc0dec0dec0112233),
   192  			Flags:   flatrpc.ExecFlagCollectSignal | flatrpc.ExecFlagCollectCover,
   193  			Cover:   []uint64{0xc0dec0dec0112233},
   194  			Signal:  []uint64{0xc0dec0dec0112233},
   195  		},
   196  		// Single 32-bit PC.
   197  		{
   198  			Is64Bit: false,
   199  			Input:   makeCover32(0xc0112233),
   200  			Flags:   flatrpc.ExecFlagCollectSignal | flatrpc.ExecFlagCollectCover,
   201  			Cover:   []uint64{0xc0112233},
   202  			Signal:  []uint64{0xc0112233},
   203  		},
   204  		// Ensure we don't sent cover/signal when not requested.
   205  		{
   206  			Is64Bit: true,
   207  			Input:   makeCover64(0xc0dec0dec0112233),
   208  			Flags:   flatrpc.ExecFlagCollectCover,
   209  			Cover:   []uint64{0xc0dec0dec0112233},
   210  		},
   211  		{
   212  			Is64Bit: true,
   213  			Input:   makeCover64(0xc0dec0dec0112233),
   214  			Flags:   flatrpc.ExecFlagCollectSignal,
   215  			Signal:  []uint64{0xc0dec0dec0112233},
   216  		},
   217  		// Coverage deduplication.
   218  		{
   219  			Is64Bit: true,
   220  			Input: makeCover64(0xc0dec0dec0000033, 0xc0dec0dec0000022, 0xc0dec0dec0000011,
   221  				0xc0dec0dec0000011, 0xc0dec0dec0000022, 0xc0dec0dec0000033, 0xc0dec0dec0000011),
   222  			Flags: flatrpc.ExecFlagCollectCover,
   223  			Cover: []uint64{0xc0dec0dec0000033, 0xc0dec0dec0000022, 0xc0dec0dec0000011,
   224  				0xc0dec0dec0000011, 0xc0dec0dec0000022, 0xc0dec0dec0000033, 0xc0dec0dec0000011},
   225  		},
   226  		{
   227  			Is64Bit: true,
   228  			Input: makeCover64(0xc0dec0dec0000033, 0xc0dec0dec0000022, 0xc0dec0dec0000011,
   229  				0xc0dec0dec0000011, 0xc0dec0dec0000022, 0xc0dec0dec0000033, 0xc0dec0dec0000011),
   230  			Flags: flatrpc.ExecFlagCollectCover | flatrpc.ExecFlagDedupCover,
   231  			Cover: []uint64{0xc0dec0dec0000011, 0xc0dec0dec0000022, 0xc0dec0dec0000033},
   232  		},
   233  		// Signal hashing.
   234  		{
   235  			Is64Bit: true,
   236  			Input: makeCover64(0xc0dec0dec0011001, 0xc0dec0dec0022002, 0xc0dec0dec00330f0,
   237  				0xc0dec0dec0044b00, 0xc0dec0dec0011001, 0xc0dec0dec0022002),
   238  			Flags: flatrpc.ExecFlagCollectSignal,
   239  			Signal: []uint64{0xc0dec0dec0011b01, 0xc0dec0dec0044bf0, 0xc0dec0dec00330f2,
   240  				0xc0dec0dec0022003, 0xc0dec0dec0011001},
   241  		},
   242  		// Invalid non-kernel PCs must fail test execution.
   243  		{
   244  			Is64Bit: true,
   245  			Input:   makeCover64(0xc0dec0dec0000022, 0xc000000000000033),
   246  			Flags:   flatrpc.ExecFlagCollectSignal | flatrpc.ExecFlagCollectCover,
   247  		},
   248  		{
   249  			Is64Bit: false,
   250  			Input:   makeCover32(0x33),
   251  			Flags:   flatrpc.ExecFlagCollectSignal | flatrpc.ExecFlagCollectCover,
   252  		},
   253  		// 64-bit comparisons.
   254  		{
   255  			Is64Bit: true,
   256  			Input: makeComps(
   257  				// A normal 8-byte comparison must be returned in the output as is.
   258  				Comparison{CmpSize8 | CmpConst, 0x1111111111111111, 0x2222222222222222, 1},
   259  				// Duplicate must be removed.
   260  				Comparison{CmpSize8 | CmpConst, 0x1111111111111111, 0x2222222222222222, 1},
   261  				// Non-const comparisons must be duplicated both ways.
   262  				Comparison{CmpSize8, 0x30, 0x31, 1},
   263  				// Test sign-extension for smaller argument types.
   264  				Comparison{CmpSize1 | CmpConst, 0xa3, 0x77, 1},
   265  				Comparison{CmpSize1 | CmpConst, 0xff10, 0xffe1, 1},
   266  				Comparison{CmpSize2 | CmpConst, 0xabcd, 0x4321, 1},
   267  				Comparison{CmpSize4 | CmpConst, 0xabcd1234, 0x4321, 1},
   268  				// Comparison with const 0 must be removed.
   269  				Comparison{CmpSize8 | CmpConst, 0, 0x2222222222222222, 1},
   270  				Comparison{CmpSize8, 0, 0x3333, 1},
   271  				// Comparison of equal values must be removed.
   272  				Comparison{CmpSize8, 0, 0, 1},
   273  				Comparison{CmpSize8, 0x1111, 0x1111, 1},
   274  				// Comparisons of kernel addresses must be removed.
   275  				Comparison{CmpSize8 | CmpConst, 0xda1a0000, 0xda1a1000, 1},
   276  				Comparison{CmpSize8, 0xda1a0000, 0, 1},
   277  				Comparison{CmpSize8, 0, 0xda1a0010, 1},
   278  				Comparison{CmpSize8 | CmpConst, 0xc0dec0dec0de0000, 0xc0dec0dec0de1000, 1},
   279  				// But not with something that's not a kernel address.
   280  				Comparison{CmpSize8 | CmpConst, 0xda1a0010, 0xabcd, 1},
   281  			),
   282  			Flags: flatrpc.ExecFlagCollectComps,
   283  			Comps: []*flatrpc.Comparison{
   284  				{Pc: 1, Op1: 0x2222222222222222, Op2: 0x1111111111111111, IsConst: true},
   285  				{Pc: 1, Op1: 0x31, Op2: 0x30, IsConst: false},
   286  				{Pc: 1, Op1: 0x77, Op2: 0xffffffffffffffa3, IsConst: true},
   287  				{Pc: 1, Op1: 0xffffffffffffffe1, Op2: 0x10, IsConst: true},
   288  				{Pc: 1, Op1: 0x4321, Op2: 0xffffffffffffabcd, IsConst: true},
   289  				{Pc: 1, Op1: 0x4321, Op2: 0xffffffffabcd1234, IsConst: true},
   290  				{Pc: 1, Op1: 0x3333, Op2: 0, IsConst: false},
   291  				{Pc: 1, Op1: 0xabcd, Op2: 0xda1a0010, IsConst: true},
   292  			},
   293  		},
   294  		// 32-bit comparisons must be the same, so test only a subset.
   295  		{
   296  			Is64Bit: false,
   297  			Input: makeComps(
   298  				Comparison{CmpSize8 | CmpConst, 0x1111111111111111, 0x2222222222222222, 1},
   299  				Comparison{CmpSize2 | CmpConst, 0xabcd, 0x4321, 2},
   300  				Comparison{CmpSize4 | CmpConst, 0xda1a0000, 0xda1a1000, 1},
   301  				Comparison{CmpSize8 | CmpConst, 0xc0dec0dec0de0000, 0xc0dec0dec0de1000, 3},
   302  				Comparison{CmpSize4 | CmpConst, 0xc0de0000, 0xc0de1000, 1},
   303  				Comparison{CmpSize4 | CmpConst, 0xc0de0011, 0xc0de1022, 1},
   304  			),
   305  			Flags: flatrpc.ExecFlagCollectComps,
   306  			Comps: []*flatrpc.Comparison{
   307  				{Pc: 1, Op1: 0x2222222222222222, Op2: 0x1111111111111111, IsConst: true},
   308  				{Pc: 2, Op1: 0x4321, Op2: 0xffffffffffffabcd, IsConst: true},
   309  				{Pc: 3, Op1: 0xc0dec0dec0de1000, Op2: 0xc0dec0dec0de0000, IsConst: true},
   310  			},
   311  		},
   312  		// Test max signal.
   313  		{
   314  			Is64Bit: true,
   315  			Input: makeCover64(0xc0dec0dec0000001, 0xc0dec0dec0000010, 0xc0dec0dec0000002,
   316  				0xc0dec0dec0000100, 0xc0dec0dec0001000),
   317  			MaxSignal: []uint64{0xc0dec0dec0000001, 0xc0dec0dec0000013, 0xc0dec0dec0000abc},
   318  			Flags:     flatrpc.ExecFlagCollectSignal | flatrpc.ExecFlagCollectCover,
   319  			Cover: []uint64{0xc0dec0dec0000001, 0xc0dec0dec0000010, 0xc0dec0dec0000002,
   320  				0xc0dec0dec0000100, 0xc0dec0dec0001000},
   321  			Signal: []uint64{0xc0dec0dec0001100, 0xc0dec0dec0000102},
   322  		},
   323  		{
   324  			Is64Bit:   false,
   325  			Input:     makeCover32(0xc0000001, 0xc0000010, 0xc0000002, 0xc0000100, 0xc0001000),
   326  			MaxSignal: []uint64{0xc0000001, 0xc0000013, 0xc0000abc},
   327  			Flags:     flatrpc.ExecFlagCollectSignal | flatrpc.ExecFlagCollectCover,
   328  			Cover:     []uint64{0xc0000001, 0xc0000010, 0xc0000002, 0xc0000100, 0xc0001000},
   329  			Signal:    []uint64{0xc0001100, 0xc0000102},
   330  		},
   331  		{
   332  			Is64Bit: true,
   333  			Input: makeCover64(0xc0dec0dec0000001, 0xc0dec0dec0000010, 0xc0dec0dec0000002,
   334  				0xc0dec0dec0000100, 0xc0dec0dec0001000),
   335  			MaxSignal:       []uint64{0xc0dec0dec0000001, 0xc0dec0dec0000013, 0xc0dec0dec0000abc},
   336  			ReturnAllSignal: true,
   337  			Flags:           flatrpc.ExecFlagCollectSignal,
   338  			Signal: []uint64{0xc0dec0dec0001100, 0xc0dec0dec0000102, 0xc0dec0dec0000012,
   339  				0xc0dec0dec0000011, 0xc0dec0dec0000001},
   340  		},
   341  		// Test cover filter.
   342  		{
   343  			Is64Bit: true,
   344  			Input: makeCover64(0xc0dec0dec0000001, 0xc0dec0dec0000010, 0xc0dec0dec0000020,
   345  				0xc0dec0dec0000040, 0xc0dec0dec0000100, 0xc0dec0dec0001000, 0xc0dec0dec0002000),
   346  			CoverFilter: []uint64{0xc0dec0dec0000002, 0xc0dec0dec0000100},
   347  			Flags:       flatrpc.ExecFlagCollectSignal | flatrpc.ExecFlagCollectCover,
   348  			Cover: []uint64{0xc0dec0dec0000001, 0xc0dec0dec0000010, 0xc0dec0dec0000020, 0xc0dec0dec0000040,
   349  				0xc0dec0dec0000100, 0xc0dec0dec0001000, 0xc0dec0dec0002000},
   350  			Signal: []uint64{0xc0dec0dec0001100, 0xc0dec0dec0000140, 0xc0dec0dec0000011, 0xc0dec0dec0000001},
   351  		},
   352  		{
   353  			Is64Bit: false,
   354  			Input: makeCover32(0xc0000001, 0xc0000010, 0xc0000020, 0xc0000040,
   355  				0xc0000100, 0xc0001000, 0xc0002000),
   356  			CoverFilter: []uint64{0xc0000002, 0xc0000100},
   357  			Flags:       flatrpc.ExecFlagCollectSignal | flatrpc.ExecFlagCollectCover,
   358  			Cover: []uint64{0xc0000001, 0xc0000010, 0xc0000020, 0xc0000040,
   359  				0xc0000100, 0xc0001000, 0xc0002000},
   360  			Signal: []uint64{0xc0001100, 0xc0000140, 0xc0000011, 0xc0000001},
   361  		},
   362  		// Extra coverage.
   363  		{
   364  			Is64Bit:       true,
   365  			ExtraCoverage: true,
   366  			Input:         makeCover64(0xc0dec0dec0000001, 0xc0dec0dec0000010),
   367  			Flags:         flatrpc.ExecFlagCollectSignal | flatrpc.ExecFlagCollectCover,
   368  			Cover:         []uint64{0xc0dec0dec0000001, 0xc0dec0dec0000010},
   369  			Signal:        []uint64{0xc0dec0dec0000011, 0xc0dec0dec0000001},
   370  		},
   371  	}
   372  	executor := csource.BuildExecutor(t, target, "../../")
   373  	for i, test := range tests {
   374  		t.Run(fmt.Sprint(i), func(t *testing.T) {
   375  			t.Parallel()
   376  			source := queue.Plain()
   377  			vmArch := targets.TestArch32
   378  			if test.Is64Bit {
   379  				vmArch = targets.TestArch64
   380  			}
   381  			sysTarget := targets.Get(targets.TestOS, vmArch)
   382  			if sysTarget.BrokenCompiler != "" {
   383  				t.Skipf("skipping due to broken compiler:\n%v", sysTarget.BrokenCompiler)
   384  			}
   385  			ctx := startRPCServer(t, target, executor, source, rpcParams{
   386  				vmArch:      vmArch,
   387  				maxSignal:   test.MaxSignal,
   388  				coverFilter: test.CoverFilter,
   389  			})
   390  			testCover1(t, ctx, target, test, source)
   391  		})
   392  	}
   393  }
   394  
   395  func testCover1(t *testing.T, ctx context.Context, target *prog.Target, test CoverTest, source *queue.PlainQueue) {
   396  	callName := "syz_inject_cover"
   397  	if test.ExtraCoverage {
   398  		callName = "syz_inject_remote_cover"
   399  	}
   400  	text := fmt.Sprintf(`%s(&AUTO="%s", AUTO)`, callName, hex.EncodeToString(test.Input))
   401  	p, err := target.Deserialize([]byte(text), prog.Strict)
   402  	if err != nil {
   403  		t.Fatal(err)
   404  	}
   405  	req := &queue.Request{
   406  		Prog: p,
   407  		ExecOpts: flatrpc.ExecOpts{
   408  			EnvFlags:  flatrpc.ExecEnvSignal | flatrpc.ExecEnvExtraCover | flatrpc.ExecEnvSandboxNone,
   409  			ExecFlags: test.Flags,
   410  		},
   411  	}
   412  	if test.ReturnAllSignal {
   413  		req.ReturnAllSignal = []int{0}
   414  	}
   415  	source.Submit(req)
   416  	res := req.Wait(ctx)
   417  	if res.Err != nil || res.Info == nil || len(res.Info.Calls) != 1 || res.Info.Calls[0] == nil {
   418  		t.Fatalf("program execution failed: status=%v err=%v\n%s", res.Status, res.Err, res.Output)
   419  	}
   420  	call := res.Info.Calls[0]
   421  	if test.ExtraCoverage {
   422  		call = res.Info.Extra
   423  		if call == nil {
   424  			t.Fatalf("got no extra coverage info")
   425  		}
   426  	}
   427  	if test.Cover == nil {
   428  		test.Cover = []uint64{}
   429  	}
   430  	if test.Signal == nil {
   431  		test.Signal = []uint64{}
   432  	}
   433  	assert.Equal(t, test.Cover, call.Cover)
   434  	assert.Equal(t, test.Signal, call.Signal)
   435  	// Comparisons are reordered and order does not matter, so compare without order.
   436  	assert.ElementsMatch(t, test.Comps, call.Comps)
   437  }
   438  
   439  func makeCover64(pcs ...uint64) []byte {
   440  	w := new(bytes.Buffer)
   441  	binary.Write(w, binary.NativeEndian, uint64(len(pcs)))
   442  	for _, pc := range pcs {
   443  		binary.Write(w, binary.NativeEndian, pc)
   444  	}
   445  	return w.Bytes()
   446  }
   447  
   448  func makeCover32(pcs ...uint32) []byte {
   449  	w := new(bytes.Buffer)
   450  	binary.Write(w, binary.NativeEndian, uint32(len(pcs)))
   451  	for _, pc := range pcs {
   452  		binary.Write(w, binary.NativeEndian, pc)
   453  	}
   454  	return w.Bytes()
   455  }
   456  
   457  func makeComps(comps ...Comparison) []byte {
   458  	w := new(bytes.Buffer)
   459  	binary.Write(w, binary.NativeEndian, uint64(len(comps)))
   460  	for _, cmp := range comps {
   461  		binary.Write(w, binary.NativeEndian, cmp)
   462  	}
   463  	return w.Bytes()
   464  }
   465  
   466  type rpcParams struct {
   467  	manyProcs      bool
   468  	vmArch         string
   469  	vmType         string
   470  	maxSignal      []uint64
   471  	coverFilter    []uint64
   472  	machineChecked func(features flatrpc.Feature)
   473  }
   474  
   475  func startRPCServer(t *testing.T, target *prog.Target, executor string,
   476  	source queue.Source, extra rpcParams) context.Context {
   477  	dir, err := os.MkdirTemp("", "syz-runtest")
   478  	if err != nil {
   479  		t.Fatal(err)
   480  	}
   481  	ctx, done := context.WithCancel(context.Background())
   482  
   483  	procs := runtime.GOMAXPROCS(0)
   484  	if !extra.manyProcs {
   485  		// We don't need many procs for this test.
   486  		procs = min(procs, 4)
   487  	}
   488  	var output bytes.Buffer
   489  	cfg := &rpcserver.LocalConfig{
   490  		Config: rpcserver.Config{
   491  			Config: vminfo.Config{
   492  				Target:   target,
   493  				VMType:   extra.vmType,
   494  				Cover:    true,
   495  				Debug:    *flagDebug,
   496  				Features: flatrpc.AllFeatures,
   497  				Sandbox:  flatrpc.ExecEnvSandboxNone,
   498  			},
   499  			VMArch:        extra.vmArch,
   500  			Procs:         procs,
   501  			Slowdown:      10, // to deflake slower tests
   502  			DebugTimeouts: true,
   503  		},
   504  		Executor:    executor,
   505  		Dir:         dir,
   506  		GDB:         *flagGDB,
   507  		MaxSignal:   extra.maxSignal,
   508  		CoverFilter: extra.coverFilter,
   509  		// Note that when *flagGDB is set, the option is ignored.
   510  		OutputWriter: &output,
   511  	}
   512  	cfg.MachineChecked = func(features flatrpc.Feature, syscalls map[*prog.Syscall]bool) queue.Source {
   513  		if extra.machineChecked != nil {
   514  			extra.machineChecked(features)
   515  		}
   516  		return source
   517  	}
   518  	errc := make(chan error)
   519  	go func() {
   520  		err := rpcserver.RunLocal(ctx, cfg)
   521  		done()
   522  		errc <- err
   523  	}()
   524  	t.Cleanup(func() {
   525  		done()
   526  		if err := <-errc; err != nil {
   527  			t.Logf("executor output:\n%s", output.String())
   528  			t.Fatal(err)
   529  		}
   530  		// We need to retry b/c we don't wait for all executor subprocesses (only set PR_SET_PDEATHSIG),
   531  		// so t.TempDir() leads to episodic "directory not empty" failures.
   532  		for i := 0; ; i++ {
   533  			if err := os.RemoveAll(dir); err == nil {
   534  				break
   535  			}
   536  			if i < 100 {
   537  				time.Sleep(100 * time.Millisecond)
   538  				continue
   539  			}
   540  			t.Logf("executor output:\n%s", output.String())
   541  			t.Fatalf("failed to remove temp dir %v: %v", dir, err)
   542  		}
   543  	})
   544  	return ctx
   545  }
   546  
   547  func TestParsing(t *testing.T) {
   548  	t.Parallel()
   549  	// Test only one target in race mode (we have gazillion of auto-generated Linux test).
   550  	raceTarget := targets.Get(targets.TestOS, targets.TestArch64)
   551  	for OS, arches := range targets.List {
   552  		if testutil.RaceEnabled && OS != raceTarget.OS {
   553  			continue
   554  		}
   555  		dir := filepath.Join("..", "..", "sys", OS, "test")
   556  		if !osutil.IsExist(dir) {
   557  			continue
   558  		}
   559  		files, err := progFileList(dir, "")
   560  		if err != nil {
   561  			t.Fatal(err)
   562  		}
   563  		for arch := range arches {
   564  			if testutil.RaceEnabled && arch != raceTarget.Arch {
   565  				continue
   566  			}
   567  			target, err := prog.GetTarget(OS, arch)
   568  			if err != nil {
   569  				t.Fatal(err)
   570  			}
   571  			sysTarget := targets.Get(target.OS, target.Arch)
   572  			t.Run(fmt.Sprintf("%v/%v", target.OS, target.Arch), func(t *testing.T) {
   573  				t.Parallel()
   574  				for _, file := range files {
   575  					// syz_mount_image tests are very large and this test takes too long.
   576  					// syz-imagegen that generates does some of this testing (Deserialize/SerializeForExec).
   577  					requires := map[string]bool{"manual": false}
   578  					p, _, _, err := parseProg(target, dir, file, requires)
   579  					if err != nil {
   580  						t.Errorf("failed to parse %v: %v", file, err)
   581  					}
   582  					if p == nil {
   583  						continue
   584  					}
   585  					if runtime.GOOS != sysTarget.BuildOS {
   586  						continue // we need at least preprocessor binary to generate sources
   587  					}
   588  					if _, err = csource.Write(p, csource.ExecutorOpts); err != nil {
   589  						t.Errorf("failed to generate C source for %v: %v", file, err)
   590  					}
   591  				}
   592  			})
   593  		}
   594  	}
   595  }