From 637acba36dcf8a7d2a71ee856f5f6f36c612525c Mon Sep 17 00:00:00 2001 From: Alexander Potapenko Date: Thu, 5 Dec 2024 15:45:00 +0100 Subject: pkg/runtest: use qemu- binaries to run cross-compiled tests When running the executor tests, do not rely on qemu-user providing binfmt_misc handlers for alien arches (e.g. arm64 on x86), because binfmt_misc cannot be mounted inside the Docker container. Instead, explicitly run the cross-compiled executor under the corresponding QEMU binary. --- pkg/runtest/executor_test.go | 57 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 7 deletions(-) (limited to 'pkg/runtest') diff --git a/pkg/runtest/executor_test.go b/pkg/runtest/executor_test.go index bb830e885..5655f5ea4 100644 --- a/pkg/runtest/executor_test.go +++ b/pkg/runtest/executor_test.go @@ -8,6 +8,8 @@ import ( "fmt" "math/rand" "os" + "os/exec" + "path/filepath" "runtime" "strings" "testing" @@ -24,6 +26,42 @@ import ( "github.com/google/syzkaller/sys/targets" ) +// Find the corresponding QEMU binary for the target arch, if needed. +func qemuBinary(arch string) (string, error) { + qarch, ok := map[string]string{ + "386": "i386", + "amd64": "x86_64", + "arm64": "aarch64", + "arm": "arm", + "mips64le": "mips64el", + "ppc64le": "ppc64le", + "riscv64": "riscv64", + "s390x": "s390x", + }[arch] + if !ok { + return "", fmt.Errorf("unsupported architecture: %s", arch) + } + + qemuBinary := "qemu-" + qarch + path, err := exec.LookPath(qemuBinary) + if err != nil { + return "", fmt.Errorf("qemu binary not found in PATH: %s", qemuBinary) + } + + return filepath.Base(path), nil +} + +// If the tests are running on CI, or if there is an assertion failure in the executor, +// report a fatal error. Otherwise, assume cross-arch execution does not work, and skip +// the test. +func handleCrossArchError(t *testing.T, err error) { + if os.Getenv("CI") != "" || strings.Contains(err.Error(), "SYZFAIL:") { + t.Fatal(err) + } else { + t.Skipf("skipping, cross-arch execution failed: %v", err) + } +} + // TestExecutor runs all internal executor unit tests. // We do it here because we already build executor binary here. func TestExecutor(t *testing.T) { @@ -41,15 +79,20 @@ func TestExecutor(t *testing.T) { t.Fatal(err) } bin := csource.BuildExecutor(t, target, "../..") - // qemu-user may allow us to run some cross-arch binaries. - if _, err := osutil.RunCmd(time.Minute, dir, bin, "test"); err != nil { - if sysTarget.Arch == runtime.GOARCH || sysTarget.VMArch == runtime.GOARCH { + if sysTarget.Arch == runtime.GOARCH || sysTarget.VMArch == runtime.GOARCH { + // Execute the tests natively. + if _, err := osutil.RunCmd(time.Minute, dir, bin, "test"); err != nil { t.Fatal(err) } - if os.Getenv("CI") != "" || strings.Contains(err.Error(), "SYZFAIL:") { - t.Fatal(err) - } else { - t.Skipf("skipping, cross-arch binary failed: %v", err) + } else { + // Get QEMU binary for the target arch. This code might run inside Docker, so it cannot + // rely on binfmt_misc handlers provided by qemu-user. + qemu, err := qemuBinary(sysTarget.Arch) + if err != nil { + handleCrossArchError(t, err) + } + if _, err := osutil.RunCmd(time.Minute, dir, qemu, bin, "test"); err != nil { + handleCrossArchError(t, err) } } }) -- cgit mrf-deployment