github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/runtest/executor_test.go (about) 1 // Copyright 2015 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 "context" 8 "fmt" 9 "math/rand" 10 "os" 11 "os/exec" 12 "path/filepath" 13 "runtime" 14 "strings" 15 "testing" 16 "time" 17 18 "github.com/google/syzkaller/pkg/csource" 19 "github.com/google/syzkaller/pkg/flatrpc" 20 "github.com/google/syzkaller/pkg/fuzzer/queue" 21 "github.com/google/syzkaller/pkg/image" 22 "github.com/google/syzkaller/pkg/osutil" 23 "github.com/google/syzkaller/pkg/testutil" 24 "github.com/google/syzkaller/prog" 25 _ "github.com/google/syzkaller/sys" 26 "github.com/google/syzkaller/sys/targets" 27 ) 28 29 // Find the corresponding QEMU binary for the target arch, if needed. 30 func qemuBinary(arch string) (string, error) { 31 qarch, ok := map[string]string{ 32 "386": "i386", 33 "amd64": "x86_64", 34 "arm64": "aarch64", 35 "arm": "arm", 36 "mips64le": "mips64el", 37 "ppc64le": "ppc64le", 38 "riscv64": "riscv64", 39 "s390x": "s390x", 40 }[arch] 41 if !ok { 42 return "", fmt.Errorf("unsupported architecture: %s", arch) 43 } 44 45 qemuBinary := "qemu-" + qarch 46 path, err := exec.LookPath(qemuBinary) 47 if err != nil { 48 return "", fmt.Errorf("qemu binary not found in PATH: %s", qemuBinary) 49 } 50 51 return filepath.Base(path), nil 52 } 53 54 // If the tests are running on CI, or if there is an assertion failure in the executor, 55 // report a fatal error. Otherwise, assume cross-arch execution does not work, and skip 56 // the test. 57 func handleCrossArchError(t *testing.T, err error) { 58 if os.Getenv("CI") != "" || strings.Contains(err.Error(), "SYZFAIL:") { 59 t.Fatal(err) 60 } else { 61 t.Skipf("skipping, cross-arch execution failed: %v", err) 62 } 63 } 64 65 // TestExecutor runs all internal executor unit tests. 66 // We do it here because we already build executor binary here. 67 func TestExecutor(t *testing.T) { 68 t.Parallel() 69 for _, sysTarget := range targets.List[runtime.GOOS] { 70 sysTarget := targets.Get(runtime.GOOS, sysTarget.Arch) 71 t.Run(sysTarget.Arch, func(t *testing.T) { 72 if sysTarget.BrokenCompiler != "" { 73 t.Skipf("skipping, broken cross-compiler: %v", sysTarget.BrokenCompiler) 74 } 75 t.Parallel() 76 dir := t.TempDir() 77 target, err := prog.GetTarget(runtime.GOOS, sysTarget.Arch) 78 if err != nil { 79 t.Fatal(err) 80 } 81 bin := csource.BuildExecutor(t, target, "../..") 82 if sysTarget.Arch == runtime.GOARCH || sysTarget.VMArch == runtime.GOARCH { 83 // Execute the tests natively. 84 if _, err := osutil.RunCmd(time.Minute, dir, bin, "test"); err != nil { 85 t.Fatal(err) 86 } 87 } else { 88 // Get QEMU binary for the target arch. This code might run inside Docker, so it cannot 89 // rely on binfmt_misc handlers provided by qemu-user. 90 qemu, err := qemuBinary(sysTarget.Arch) 91 if err != nil { 92 handleCrossArchError(t, err) 93 } 94 if _, err := osutil.RunCmd(time.Minute, dir, qemu, bin, "test"); err != nil { 95 handleCrossArchError(t, err) 96 } 97 } 98 }) 99 } 100 } 101 102 func TestZlib(t *testing.T) { 103 t.Parallel() 104 target, err := prog.GetTarget(targets.TestOS, targets.TestArch64) 105 if err != nil { 106 t.Fatal(err) 107 } 108 sysTarget := targets.Get(target.OS, target.Arch) 109 if sysTarget.BrokenCompiler != "" { 110 t.Skipf("skipping, broken cross-compiler: %v", sysTarget.BrokenCompiler) 111 } 112 executor := csource.BuildExecutor(t, target, "../..") 113 source := queue.Plain() 114 startRPCServer(t, target, executor, source, rpcParams{manyProcs: true}) 115 r := rand.New(testutil.RandSource(t)) 116 for i := 0; i < 10; i++ { 117 data := testutil.RandMountImage(r) 118 compressed := image.Compress(data) 119 text := fmt.Sprintf(`syz_compare_zlib(&(0x7f0000000000)="$%s", AUTO, &(0x7f0000800000)="$%s", AUTO)`, 120 image.EncodeB64(data), image.EncodeB64(compressed)) 121 p, err := target.Deserialize([]byte(text), prog.Strict) 122 if err != nil { 123 t.Fatalf("failed to deserialize empty program: %v", err) 124 } 125 req := &queue.Request{ 126 Prog: p, 127 ReturnError: true, 128 ReturnOutput: true, 129 ExecOpts: flatrpc.ExecOpts{ 130 EnvFlags: flatrpc.ExecEnvSandboxNone, 131 }, 132 } 133 source.Submit(req) 134 res := req.Wait(context.Background()) 135 if res.Err != nil { 136 t.Fatalf("program execution failed: %v\n%s", res.Err, res.Output) 137 } 138 if res.Info.Calls[0].Error != 0 { 139 t.Fatalf("data comparison failed: %v\n%s", res.Info.Calls[0].Error, res.Output) 140 } 141 } 142 } 143 144 func TestExecutorCommonExt(t *testing.T) { 145 t.Parallel() 146 target, err := prog.GetTarget("test", "64_fork") 147 if err != nil { 148 t.Fatal(err) 149 } 150 sysTarget := targets.Get(target.OS, target.Arch) 151 if sysTarget.BrokenCompiler != "" { 152 t.Skipf("skipping, broken cross-compiler: %v", sysTarget.BrokenCompiler) 153 } 154 executor := csource.BuildExecutor(t, target, "../..", "-DSYZ_TEST_COMMON_EXT_EXAMPLE=1") 155 // The example setup_ext_test does: 156 // *(uint64*)(SYZ_DATA_OFFSET + 0x1234) = 0xbadc0ffee; 157 // The following program tests that that value is present at 0x1234. 158 test := `syz_compare(&(0x7f0000001234)="", 0x8, &(0x7f0000000000)=@blob="eeffc0ad0b000000", AUTO)` 159 p, err := target.Deserialize([]byte(test), prog.Strict) 160 if err != nil { 161 t.Fatal(err) 162 } 163 source := queue.Plain() 164 startRPCServer(t, target, executor, source, rpcParams{}) 165 req := &queue.Request{ 166 Prog: p, 167 ReturnError: true, 168 ReturnOutput: true, 169 ExecOpts: flatrpc.ExecOpts{ 170 EnvFlags: flatrpc.ExecEnvSandboxNone, 171 }, 172 } 173 source.Submit(req) 174 res := req.Wait(context.Background()) 175 if res.Err != nil { 176 t.Fatalf("program execution failed: %v\n%s", res.Err, res.Output) 177 } 178 if call := res.Info.Calls[0]; call.Flags&flatrpc.CallFlagFinished == 0 || call.Error != 0 { 179 t.Fatalf("bad call result: flags=%x errno=%v", call.Flags, call.Error) 180 } 181 }