aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/pseudo_syscalls.md13
-rw-r--r--executor/common_bsd.h4
-rw-r--r--executor/executor.cc44
-rw-r--r--executor/executor_bsd.h4
-rw-r--r--executor/executor_linux.h52
-rw-r--r--pkg/host/features.go227
-rw-r--r--pkg/host/features_linux.go355
-rw-r--r--pkg/host/features_linux_test.go23
-rw-r--r--pkg/host/host_darwin.go16
-rw-r--r--pkg/host/host_freebsd.go19
-rw-r--r--pkg/host/host_fuchsia.go14
-rw-r--r--pkg/host/host_netbsd.go42
-rw-r--r--pkg/host/host_openbsd.go38
-rw-r--r--pkg/host/host_test.go70
-rw-r--r--pkg/host/host_windows.go12
-rw-r--r--pkg/host/syscalls.go45
-rw-r--r--pkg/host/syscalls_linux.go568
-rw-r--r--pkg/host/syscalls_linux_test.go307
-rw-r--r--pkg/ipc/ipc.go31
-rw-r--r--pkg/ipc/ipc_test.go2
-rw-r--r--pkg/rpctype/rpctype.go7
-rw-r--r--pkg/runtest/run_test.go24
-rw-r--r--pkg/vminfo/features.go192
-rw-r--r--pkg/vminfo/linux_syscalls.go20
-rw-r--r--pkg/vminfo/linux_test.go12
-rw-r--r--pkg/vminfo/netbsd.go38
-rw-r--r--pkg/vminfo/openbsd.go36
-rw-r--r--pkg/vminfo/syscalls.go73
-rw-r--r--pkg/vminfo/vminfo.go14
-rw-r--r--pkg/vminfo/vminfo_test.go26
-rw-r--r--syz-fuzzer/fuzzer.go117
-rw-r--r--syz-fuzzer/proc.go5
-rw-r--r--syz-fuzzer/testing.go96
-rw-r--r--syz-manager/rpc.go42
-rw-r--r--syz-runner/runner.go16
-rw-r--r--tools/syz-execprog/execprog.go112
-rw-r--r--tools/syz-runtest/runtest.go76
37 files changed, 709 insertions, 2083 deletions
diff --git a/docs/pseudo_syscalls.md b/docs/pseudo_syscalls.md
index cb899e6fc..fc36f85f9 100644
--- a/docs/pseudo_syscalls.md
+++ b/docs/pseudo_syscalls.md
@@ -56,17 +56,10 @@ are violated (e.g. passing `NULL` to a `non-NULL` argument, or passing
that.
Now, to handle the pseudo-syscall properly we have to update the
-`isSupportedSyzkall` in
-[syscalls_linux.go](../pkg/host/syscalls_linux.go) and add a particular
+`linuxSyscallChecks` in
+[linux_syscalls.go](../pkg/vminfo/linux_syscalls.go) and add a particular
case for this syscall, enabling it when necessary. If we want to enable
-it unconditionally we can simply make `isSupportedSyzkall` return `true,
-""` for it:
-
- func isSupportedSyzkall(sandbox string, c *prog.Syscall) (bool, string) {
- switch c.CallName {
- ...
- case "syz_mycall":
- return true, ""
+it unconditionally we can simply use `alwaysSupported` for it.
Finally, run `make generate`. Now you can use it in a syscall
description file as if it was a regular system call:
diff --git a/executor/common_bsd.h b/executor/common_bsd.h
index 4ed45d0bd..e0beac33f 100644
--- a/executor/common_bsd.h
+++ b/executor/common_bsd.h
@@ -24,6 +24,7 @@ static void setup_usb(void)
if (dir == NULL)
fail("failed to open /dev");
+ bool have_vhci = false;
struct dirent* ent = NULL;
while ((ent = readdir(dir)) != NULL) {
if (ent->d_type != DT_CHR)
@@ -34,7 +35,10 @@ static void setup_usb(void)
snprintf(path, sizeof(path), "/dev/%s", ent->d_name);
if (chmod(path, 0666))
failmsg("failed to chmod vhci", "path=%s", path);
+ have_vhci = true;
}
+ if (!have_vhci)
+ fail("don't have any /dev/vhci devices");
closedir(dir);
}
diff --git a/executor/executor.cc b/executor/executor.cc
index 6ac777ad6..2e4e01da8 100644
--- a/executor/executor.cc
+++ b/executor/executor.cc
@@ -369,7 +369,7 @@ struct kcov_comparison_t {
typedef char kcov_comparison_size[sizeof(kcov_comparison_t) == 4 * sizeof(uint64) ? 1 : -1];
struct feature_t {
- const char* name;
+ rpc::Feature id;
void (*setup)();
};
@@ -1658,33 +1658,41 @@ bool kcov_comparison_t::operator<(const struct kcov_comparison_t& other) const
}
#endif // if SYZ_EXECUTOR_USES_SHMEM
+#if !SYZ_HAVE_FEATURES
+static feature_t features[] = {};
+#endif
+
void setup_features(char** enable, int n)
{
// This does any one-time setup for the requested features on the machine.
// Note: this can be called multiple times and must be idempotent.
flag_debug = true;
+ if (n != 1)
+ fail("setup: more than one feature");
+ char* endptr = nullptr;
+ auto feature = static_cast<rpc::Feature>(strtoull(enable[0], &endptr, 10));
+ if (endptr == enable[0] || (feature > rpc::Feature::ANY) ||
+ __builtin_popcountll(static_cast<uint64>(feature)) > 1)
+ failmsg("setup: failed to parse feature", "feature='%s'", enable[0]);
+ if (feature == rpc::Feature::NONE) {
#if SYZ_HAVE_FEATURES
- setup_sysctl();
- setup_cgroups();
+ setup_sysctl();
+ setup_cgroups();
#endif
#if SYZ_HAVE_SETUP_EXT
- // This can be defined in common_ext.h.
- setup_ext();
+ // This can be defined in common_ext.h.
+ setup_ext();
#endif
- for (int i = 0; i < n; i++) {
- bool found = false;
-#if SYZ_HAVE_FEATURES
- for (unsigned f = 0; f < sizeof(features) / sizeof(features[0]); f++) {
- if (strcmp(enable[i], features[f].name) == 0) {
- features[f].setup();
- found = true;
- break;
- }
+ return;
+ }
+ for (size_t i = 0; i < sizeof(features) / sizeof(features[0]); i++) {
+ if (features[i].id == feature) {
+ features[i].setup();
+ return;
}
-#endif
- if (!found)
- failmsg("setup features: unknown feature", "feature=%s", enable[i]);
}
+ // Note: pkg/host knows about this error message.
+ fail("feature setup is not needed");
}
void failmsg(const char* err, const char* msg, ...)
@@ -1728,7 +1736,7 @@ void exitf(const char* msg, ...)
vfprintf(stderr, msg, args);
va_end(args);
fprintf(stderr, " (errno %d)\n", e);
- doexit(0);
+ doexit(1);
}
void debug(const char* msg, ...)
diff --git a/executor/executor_bsd.h b/executor/executor_bsd.h
index d88922417..6c2174360 100644
--- a/executor/executor_bsd.h
+++ b/executor/executor_bsd.h
@@ -182,8 +182,8 @@ static bool use_cover_edges(uint64 pc)
#if GOOS_netbsd
#define SYZ_HAVE_FEATURES 1
static feature_t features[] = {
- {"usb", setup_usb},
- {"fault", setup_fault},
+ {rpc::Feature::USBEmulation, setup_usb},
+ {rpc::Feature::Fault, setup_fault},
};
static void setup_sysctl(void)
diff --git a/executor/executor_linux.h b/executor/executor_linux.h
index 578c65673..b8c98d3f7 100644
--- a/executor/executor_linux.h
+++ b/executor/executor_linux.h
@@ -256,13 +256,51 @@ NORETURN void doexit_thread(int status)
}
}
+static void setup_nicvf()
+{
+ // This feature has custom checking precedure rather than just rely on running
+ // a simple program with this feature enabled b/c find_vf_interface cannot be made
+ // failing. It searches for the nic in init namespace, but then the nic is moved
+ // to one of testing namespace, so if number of procs is more than the number of devices,
+ // then some of them won't fine a nic (the code is also racy, more than one proc
+ // can find the same device and then moving it will fail for all but one).
+ // So we have to make find_vf_interface non-failing in case of failures,
+ // which means we cannot use it for feature checking.
+ if (open("/sys/bus/pci/devices/0000:00:11.0/", O_RDONLY | O_NONBLOCK) == -1)
+ fail("PCI device 0000:00:11.0 is not available");
+}
+
+static void setup_devlink_pci()
+{
+ // See comment in setup_nicvf.
+ if (open("/sys/bus/pci/devices/0000:00:10.0/", O_RDONLY | O_NONBLOCK) == -1)
+ fail("PCI device 0000:00:10.0 is not available");
+}
+
+static void setup_delay_kcov()
+{
+ is_kernel_64_bit = detect_kernel_bitness();
+ cover_t cov = {};
+ cov.fd = kCoverFd;
+ cover_open(&cov, false);
+ cover_mmap(&cov);
+ cov.data = nullptr;
+ cover_mmap(&cov);
+ // If delayed kcov mmap is not supported by the kernel,
+ // accesses to the second mapping will crash.
+ const_cast<volatile char*>(cov.data)[0] = 1;
+}
+
#define SYZ_HAVE_FEATURES 1
static feature_t features[] = {
- {"leak", setup_leak},
- {"fault", setup_fault},
- {"binfmt_misc", setup_binfmt_misc},
- {"kcsan", setup_kcsan},
- {"usb", setup_usb},
- {"802154", setup_802154},
- {"swap", setup_swap},
+ {rpc::Feature::DelayKcovMmap, setup_delay_kcov},
+ {rpc::Feature::Fault, setup_fault},
+ {rpc::Feature::Leak, setup_leak},
+ {rpc::Feature::KCSAN, setup_kcsan},
+ {rpc::Feature::USBEmulation, setup_usb},
+ {rpc::Feature::LRWPANEmulation, setup_802154},
+ {rpc::Feature::BinFmtMisc, setup_binfmt_misc},
+ {rpc::Feature::Swap, setup_swap},
+ {rpc::Feature::NicVF, setup_nicvf},
+ {rpc::Feature::DevlinkPCI, setup_devlink_pci},
};
diff --git a/pkg/host/features.go b/pkg/host/features.go
index 8a8629084..ae0771785 100644
--- a/pkg/host/features.go
+++ b/pkg/host/features.go
@@ -4,212 +4,73 @@
package host
import (
+ "bytes"
"fmt"
"strings"
"time"
"github.com/google/syzkaller/pkg/csource"
"github.com/google/syzkaller/pkg/flatrpc"
+ "github.com/google/syzkaller/pkg/ipc"
"github.com/google/syzkaller/pkg/log"
"github.com/google/syzkaller/pkg/osutil"
"github.com/google/syzkaller/prog"
"github.com/google/syzkaller/sys/targets"
)
-const (
- FeatureCoverage = iota
- FeatureComparisons
- FeatureExtraCoverage
- FeatureDelayKcovMmap
- FeatureSandboxSetuid
- FeatureSandboxNamespace
- FeatureSandboxAndroid
- FeatureFault
- FeatureLeak
- FeatureNetInjection
- FeatureNetDevices
- FeatureKCSAN
- FeatureDevlinkPCI
- FeatureNicVF
- FeatureUSBEmulation
- FeatureVhciInjection
- FeatureWifiEmulation
- Feature802154Emulation
- FeatureSwap
- numFeatures
-)
-
-type Feature struct {
- Name string
- Enabled bool
- Reason string
-}
-
-type Features [numFeatures]Feature
-
-func (features *Features) Supported() *Features {
- return features
-}
-
-func (features *Features) ToFlatRPC() flatrpc.Feature {
- var result flatrpc.Feature
- if features[FeatureCoverage].Enabled {
- result |= flatrpc.FeatureCoverage
- }
- if features[FeatureComparisons].Enabled {
- result |= flatrpc.FeatureComparisons
- }
- if features[FeatureExtraCoverage].Enabled {
- result |= flatrpc.FeatureExtraCoverage
- }
- if features[FeatureDelayKcovMmap].Enabled {
- result |= flatrpc.FeatureDelayKcovMmap
- }
- if features[FeatureSandboxSetuid].Enabled {
- result |= flatrpc.FeatureSandboxSetuid
- }
- if features[FeatureSandboxNamespace].Enabled {
- result |= flatrpc.FeatureSandboxNamespace
- }
- if features[FeatureSandboxAndroid].Enabled {
- result |= flatrpc.FeatureSandboxAndroid
- }
- if features[FeatureFault].Enabled {
- result |= flatrpc.FeatureFault
- }
- if features[FeatureLeak].Enabled {
- result |= flatrpc.FeatureLeak
- }
- if features[FeatureNetInjection].Enabled {
- result |= flatrpc.FeatureNetInjection
- }
- if features[FeatureNetDevices].Enabled {
- result |= flatrpc.FeatureNetDevices
- }
- if features[FeatureKCSAN].Enabled {
- result |= flatrpc.FeatureKCSAN
- }
- if features[FeatureDevlinkPCI].Enabled {
- result |= flatrpc.FeatureDevlinkPCI
- }
- if features[FeatureNicVF].Enabled {
- result |= flatrpc.FeatureNicVF
- }
- if features[FeatureUSBEmulation].Enabled {
- result |= flatrpc.FeatureUSBEmulation
- }
- if features[FeatureVhciInjection].Enabled {
- result |= flatrpc.FeatureVhciInjection
- }
- if features[FeatureWifiEmulation].Enabled {
- result |= flatrpc.FeatureWifiEmulation
- }
- if features[Feature802154Emulation].Enabled {
- result |= flatrpc.FeatureLRWPANEmulation
- }
- if features[FeatureSwap].Enabled {
- result |= flatrpc.FeatureSwap
- }
- return result
-}
-
-var checkFeature [numFeatures]func() string
-
-func unconditionallyEnabled() string { return "" }
-
-// Check detects features supported on the host.
-// Empty string for a feature means the feature is supported,
-// otherwise the string contains the reason why the feature is not supported.
-func Check(target *prog.Target) (*Features, error) {
- const unsupported = "support is not implemented in syzkaller"
- res := &Features{
- FeatureCoverage: {Name: "code coverage", Reason: unsupported},
- FeatureComparisons: {Name: "comparison tracing", Reason: unsupported},
- FeatureExtraCoverage: {Name: "extra coverage", Reason: unsupported},
- FeatureDelayKcovMmap: {Name: "delay kcov mmap", Reason: unsupported},
- FeatureSandboxSetuid: {Name: "setuid sandbox", Reason: unsupported},
- FeatureSandboxNamespace: {Name: "namespace sandbox", Reason: unsupported},
- FeatureSandboxAndroid: {Name: "Android sandbox", Reason: unsupported},
- FeatureFault: {Name: "fault injection", Reason: unsupported},
- FeatureLeak: {Name: "leak checking", Reason: unsupported},
- FeatureNetInjection: {Name: "net packet injection", Reason: unsupported},
- FeatureNetDevices: {Name: "net device setup", Reason: unsupported},
- FeatureKCSAN: {Name: "concurrency sanitizer", Reason: unsupported},
- FeatureDevlinkPCI: {Name: "devlink PCI setup", Reason: unsupported},
- FeatureNicVF: {Name: "NIC VF setup", Reason: unsupported},
- FeatureUSBEmulation: {Name: "USB emulation", Reason: unsupported},
- FeatureVhciInjection: {Name: "hci packet injection", Reason: unsupported},
- FeatureWifiEmulation: {Name: "wifi device emulation", Reason: unsupported},
- Feature802154Emulation: {Name: "802.15.4 emulation", Reason: unsupported},
- FeatureSwap: {Name: "swap file", Reason: unsupported},
+// SetupFeatures enables and does any one-time setup for the requested features on the host.
+// Note: this can be called multiple times and must be idempotent.
+func SetupFeatures(target *prog.Target, executor string, mask flatrpc.Feature, flags csource.Features) (
+ []flatrpc.FeatureInfo, error) {
+ if noHostChecks(target) {
+ return nil, nil
}
- for n, check := range featureCheckers(target) {
- if check == nil {
+ var results []flatrpc.FeatureInfo
+ resultC := make(chan flatrpc.FeatureInfo)
+ for feat := range flatrpc.EnumNamesFeature {
+ feat := feat
+ if mask&feat == 0 {
continue
}
- if reason := check(); reason == "" {
- if n == FeatureCoverage && !target.ExecutorUsesShmem {
- return nil, fmt.Errorf("enabling FeatureCoverage requires enabling ExecutorUsesShmem")
- }
- res[n].Enabled = true
- res[n].Reason = "enabled"
- } else {
- res[n].Reason = reason
+ opt := ipc.FlatRPCFeaturesToCSource[feat]
+ if opt != "" && flags != nil && !flags["binfmt_misc"].Enabled {
+ continue
}
+ results = append(results, flatrpc.FeatureInfo{})
+ go setupFeature(executor, feat, resultC)
}
- return res, nil
+ // Feature 0 setups common things that are not part of any feature.
+ setupFeature(executor, 0, nil)
+ for i := range results {
+ results[i] = <-resultC
+ }
+ return results, nil
}
-// Setup enables and does any one-time setup for the requested features on the host.
-// Note: this can be called multiple times and must be idempotent.
-func Setup(target *prog.Target, features *Features, featureFlags csource.Features, executor string) error {
- if noHostChecks(target) {
- return nil
- }
+func setupFeature(executor string, feat flatrpc.Feature, resultC chan flatrpc.FeatureInfo) {
args := strings.Split(executor, " ")
executor = args[0]
- args = append(args[1:], "setup")
- if features[FeatureLeak].Enabled {
- args = append(args, "leak")
- }
- if features[FeatureFault].Enabled {
- args = append(args, "fault")
- }
- if target.OS == targets.Linux && featureFlags["binfmt_misc"].Enabled {
- args = append(args, "binfmt_misc")
- }
- if features[FeatureKCSAN].Enabled {
- args = append(args, "kcsan")
- }
- if features[FeatureUSBEmulation].Enabled {
- args = append(args, "usb")
- }
- if featureFlags["ieee802154"].Enabled && features[Feature802154Emulation].Enabled {
- args = append(args, "802154")
- }
- if features[FeatureSwap].Enabled {
- args = append(args, "swap")
- }
- output, err := osutil.RunCmd(5*time.Minute, "", executor, args...)
- log.Logf(1, "executor %v\n%s", args, output)
- return err
-}
-
-func featureCheckers(target *prog.Target) [numFeatures]func() string {
- ret := [numFeatures]func() string{}
- if target.OS == targets.TestOS {
- sysTarget := targets.Get(target.OS, target.Arch)
- ret[FeatureCoverage] = func() string {
- if sysTarget.ExecutorUsesShmem && sysTarget.PtrSize == 8 {
- return ""
- }
- return "disabled"
+ args = append(args[1:], "setup", fmt.Sprint(uint64(feat)))
+ output, err := osutil.RunCmd(3*time.Minute, "", executor, args...)
+ log.Logf(1, "executor %v\n%s", args, bytes.ReplaceAll(output, []byte("SYZFAIL:"), nil))
+ outputStr := string(output)
+ if err == nil {
+ outputStr = ""
+ } else if outputStr == "" {
+ outputStr = err.Error()
+ }
+ needSetup := true
+ if strings.Contains(outputStr, "feature setup is not needed") {
+ needSetup = false
+ outputStr = ""
+ }
+ if resultC != nil {
+ resultC <- flatrpc.FeatureInfo{
+ Id: feat,
+ NeedSetup: needSetup,
+ Reason: outputStr,
}
}
- if noHostChecks(target) {
- return ret
- }
- return checkFeature
}
func noHostChecks(target *prog.Target) bool {
diff --git a/pkg/host/features_linux.go b/pkg/host/features_linux.go
deleted file mode 100644
index 393617e45..000000000
--- a/pkg/host/features_linux.go
+++ /dev/null
@@ -1,355 +0,0 @@
-// Copyright 2015 syzkaller project authors. All rights reserved.
-// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
-
-package host
-
-import (
- "fmt"
- "os"
- "os/exec"
- "regexp"
- "runtime"
- "runtime/debug"
- "strconv"
- "syscall"
- "unsafe"
-
- "github.com/google/syzkaller/pkg/osutil"
- "github.com/google/syzkaller/prog"
- "github.com/google/syzkaller/sys/linux"
- "golang.org/x/sys/unix"
-)
-
-func init() {
- checkFeature[FeatureCoverage] = checkCoverage
- checkFeature[FeatureComparisons] = checkComparisons
- checkFeature[FeatureExtraCoverage] = checkExtraCoverage
- checkFeature[FeatureDelayKcovMmap] = checkDelayKcovMmap
- checkFeature[FeatureSandboxSetuid] = unconditionallyEnabled
- checkFeature[FeatureSandboxNamespace] = checkSandboxNamespace
- checkFeature[FeatureSandboxAndroid] = checkSandboxAndroid
- checkFeature[FeatureFault] = checkFault
- checkFeature[FeatureLeak] = checkLeak
- checkFeature[FeatureNetInjection] = checkNetInjection
- checkFeature[FeatureNetDevices] = unconditionallyEnabled
- checkFeature[FeatureKCSAN] = checkKCSAN
- checkFeature[FeatureDevlinkPCI] = checkDevlinkPCI
- checkFeature[FeatureNicVF] = checkNicVF
- checkFeature[FeatureUSBEmulation] = checkUSBEmulation
- checkFeature[FeatureVhciInjection] = checkVhciInjection
- checkFeature[FeatureWifiEmulation] = checkWifiEmulation
- checkFeature[Feature802154Emulation] = check802154Emulation
- checkFeature[FeatureSwap] = checkSwap
-}
-
-func checkCoverage() string {
- if reason := checkDebugFS(); reason != "" {
- return reason
- }
- if !osutil.IsExist("/sys/kernel/debug/kcov") {
- return "CONFIG_KCOV is not enabled"
- }
- if err := osutil.IsAccessible("/sys/kernel/debug/kcov"); err != nil {
- return err.Error()
- }
- return ""
-}
-
-func checkComparisons() (reason string) {
- return checkCoverageFeature(FeatureComparisons)
-}
-
-func checkExtraCoverage() (reason string) {
- return checkCoverageFeature(FeatureExtraCoverage)
-}
-
-func checkDelayKcovMmap() string {
- // The kcov implementation in Linux (currently) does not adequately handle subsequent
- // mmap invocations on the same descriptor. When that problem is fixed, we want
- // syzkaller to differentiate between distributions as this allows to noticeably speed
- // up fuzzing.
- return checkCoverageFeature(FeatureDelayKcovMmap)
-}
-
-func kcovTestMmap(fd int, coverSize uintptr) (reason string) {
- mem, err := syscall.Mmap(fd, 0, int(coverSize*unsafe.Sizeof(uintptr(0))),
- syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
- if err != nil {
- return fmt.Sprintf("KCOV mmap failed: %v", err)
- }
- defer func() {
- if err := syscall.Munmap(mem); err != nil {
- reason = fmt.Sprintf("munmap failed: %v", err)
- }
- }()
- // Now the tricky part - mmap may say it has succeeded, but the memory is
- // in fact not allocated.
- // We try to access it. If go does not panic, everything is fine.
- prevValue := debug.SetPanicOnFault(true)
- defer debug.SetPanicOnFault(prevValue)
- defer func() {
- if r := recover(); r != nil {
- reason = "mmap returned an invalid pointer"
- }
- }()
- mem[0] = 0
- return ""
-}
-
-func checkCoverageFeature(feature int) (reason string) {
- if reason = checkDebugFS(); reason != "" {
- return reason
- }
- // TODO(dvyukov): this should run under target arch.
- // E.g. KCOV ioctls were initially not supported on 386 (missing compat_ioctl),
- // and a 386 executor won't be able to use them, but an amd64 fuzzer will be.
- fd, err := syscall.Open("/sys/kernel/debug/kcov", syscall.O_RDWR, 0)
- if err != nil {
- return "CONFIG_KCOV is not enabled"
- }
- defer syscall.Close(fd)
- // Trigger host target lazy initialization, it will fill linux.KCOV_INIT_TRACE.
- // It's all wrong and needs to be refactored.
- if _, err := prog.GetTarget(runtime.GOOS, runtime.GOARCH); err != nil {
- return fmt.Sprintf("failed to get target: %v", err)
- }
- coverSize := uintptr(64 << 10)
- _, _, errno := syscall.Syscall(
- syscall.SYS_IOCTL, uintptr(fd), linux.KCOV_INIT_TRACE, coverSize)
- if errno != 0 {
- return fmt.Sprintf("ioctl(KCOV_INIT_TRACE) failed: %v", errno)
- }
- if reason = kcovTestMmap(fd, coverSize); reason != "" {
- return reason
- }
- disableKcov := func() {
- _, _, errno = syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), linux.KCOV_DISABLE, 0)
- if errno != 0 {
- reason = fmt.Sprintf("ioctl(KCOV_DISABLE) failed: %v", errno)
- }
- }
- switch feature {
- case FeatureComparisons:
- _, _, errno = syscall.Syscall(syscall.SYS_IOCTL,
- uintptr(fd), linux.KCOV_ENABLE, linux.KCOV_TRACE_CMP)
- if errno != 0 {
- if errno == 524 { // ENOTSUPP
- return "CONFIG_KCOV_ENABLE_COMPARISONS is not enabled"
- }
- return fmt.Sprintf("ioctl(KCOV_TRACE_CMP) failed: %v", errno)
- }
- defer disableKcov()
- case FeatureExtraCoverage:
- arg := KcovRemoteArg{
- TraceMode: uint32(linux.KCOV_TRACE_PC),
- AreaSize: uint32(coverSize * unsafe.Sizeof(uintptr(0))),
- NumHandles: 0,
- CommonHandle: 0,
- }
- _, _, errno = syscall.Syscall(syscall.SYS_IOCTL,
- uintptr(fd), linux.KCOV_REMOTE_ENABLE, uintptr(unsafe.Pointer(&arg)))
- if errno != 0 {
- if errno == 25 { // ENOTTY
- return "extra coverage is not supported by the kernel"
- }
- return fmt.Sprintf("ioctl(KCOV_REMOTE_ENABLE) failed: %v", errno)
- }
- defer disableKcov()
- case FeatureDelayKcovMmap:
- if reason = kcovTestMmap(fd, coverSize); reason != "" {
- return reason
- }
- default:
- panic("unknown feature in checkCoverageFeature")
- }
- return ""
-}
-
-type KcovRemoteArg struct {
- TraceMode uint32
- AreaSize uint32
- NumHandles uint32
- _ uint32
- CommonHandle uint64
- // Handles []uint64 goes here.
-}
-
-func checkFault() string {
- if err := osutil.IsAccessible("/proc/self/make-it-fail"); err != nil {
- return "CONFIG_FAULT_INJECTION is not enabled"
- }
- if err := osutil.IsAccessible("/proc/thread-self/fail-nth"); err != nil {
- return "kernel does not have systematic fault injection support"
- }
- if reason := checkDebugFS(); reason != "" {
- return reason
- }
- if err := osutil.IsAccessible("/sys/kernel/debug/failslab/ignore-gfp-wait"); err != nil {
- return "CONFIG_FAULT_INJECTION_DEBUG_FS or CONFIG_FAILSLAB are not enabled"
- }
- return ""
-}
-
-func checkLeak() string {
- if reason := checkDebugFS(); reason != "" {
- return reason
- }
- fd, err := syscall.Open("/sys/kernel/debug/kmemleak", syscall.O_RDWR, 0)
- if err != nil {
- return "CONFIG_DEBUG_KMEMLEAK is not enabled"
- }
- defer syscall.Close(fd)
- if _, err := syscall.Write(fd, []byte("scan=off")); err != nil {
- if err == syscall.EBUSY {
- return "KMEMLEAK disabled: increase CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE or unset CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF"
- }
- return fmt.Sprintf("/sys/kernel/debug/kmemleak write failed: %v", err)
- }
- return ""
-}
-
-func checkSandboxNamespace() string {
- if err := osutil.IsAccessible("/proc/self/ns/user"); err != nil {
- return err.Error()
- }
- return ""
-}
-
-func checkSandboxAndroid() string {
- if err := osutil.IsAccessible("/sys/fs/selinux/policy"); err != nil {
- return err.Error()
- }
- return ""
-}
-
-func checkNetInjection() string {
- if err := osutil.IsAccessible("/dev/net/tun"); err != nil {
- return err.Error()
- }
- return ""
-}
-
-func checkUSBEmulation() string {
- if err := osutil.IsAccessible("/dev/raw-gadget"); err != nil {
- return err.Error()
- }
- return ""
-}
-
-func checkVhciInjection() string {
- if err := osutil.IsAccessible("/dev/vhci"); err != nil {
- return err.Error()
- }
- return ""
-}
-
-func checkDebugFS() string {
- if err := osutil.IsAccessible("/sys/kernel/debug"); err != nil {
- return "debugfs is not enabled or not mounted"
- }
- return ""
-}
-
-func checkKCSAN() string {
- if err := osutil.IsAccessible("/sys/kernel/debug/kcsan"); err != nil {
- return err.Error()
- }
- return ""
-}
-
-func checkDevlinkPCI() string {
- if err := osutil.IsAccessible("/sys/bus/pci/devices/0000:00:10.0/"); err != nil {
- return "PCI device 0000:00:10.0 is not available"
- }
- if err := osutil.IsAccessible("/proc/self/ns/net"); err != nil {
- // Initialize_devlink_pci in executor fails w/o this.
- return "/proc/self/ns/net does not exist"
- }
- return ""
-}
-
-func checkNicVF() string {
- if err := osutil.IsAccessible("/sys/bus/pci/devices/0000:00:11.0/"); err != nil {
- return "PCI device 0000:00:11.0 is not available"
- }
- return ""
-}
-
-func checkWifiEmulation() string {
- if err := osutil.IsAccessible("/sys/class/mac80211_hwsim/"); err != nil {
- return err.Error()
- }
- // We use HWSIM_ATTR_PERM_ADDR which was added in 4.17.
- return requireKernel(4, 17)
-}
-
-func check802154Emulation() string {
- if err := osutil.IsAccessible("/sys/bus/platform/devices/mac802154_hwsim"); err != nil {
- return err.Error()
- }
- return ""
-}
-
-func checkSwap() string {
- if err := osutil.IsAccessible("/proc/swaps"); err != nil {
- return err.Error()
- }
- if _, err := exec.LookPath("mkswap"); err != nil {
- return "mkswap is not available"
- }
- // We use fallocate in syz-executor, so let's check if the filesystem supports it.
- // /tmp might not always be the best choice for this
- // (on some systems, a different filesystem might be used for /tmp).
- dir, err := os.UserHomeDir()
- if err != nil {
- return fmt.Sprintf("failed to get home dir: %v", err)
- }
- f, err := os.CreateTemp(dir, "any-file")
- if err != nil {
- return fmt.Sprintf("failed to create temp file: %v", err)
- }
- defer os.Remove(f.Name())
- err = syscall.Fallocate(int(f.Fd()), unix.FALLOC_FL_ZERO_RANGE, 0, 2048)
- if err != nil {
- return fmt.Sprintf("fallocate failed: %v", err)
- }
- return ""
-}
-
-func requireKernel(x, y int) string {
- info := new(unix.Utsname)
- if err := unix.Uname(info); err != nil {
- return fmt.Sprintf("uname failed: %v", err)
- }
- ver := string(info.Release[:])
- if ok, bad := matchKernelVersion(ver, x, y); bad {
- return fmt.Sprintf("failed to parse kernel version (%v)", ver)
- } else if !ok {
- return fmt.Sprintf("kernel %v.%v required (have %v)", x, y, ver)
- }
- return ""
-}
-
-func matchKernelVersion(ver string, x, y int) (bool, bool) {
- match := kernelVersionRe.FindStringSubmatch(ver)
- if match == nil {
- return false, true
- }
- major, err := strconv.Atoi(match[1])
- if err != nil {
- return false, true
- }
- if major <= 0 || major > 999 {
- return false, true
- }
- minor, err := strconv.Atoi(match[2])
- if err != nil {
- return false, true
- }
- if minor <= 0 || minor > 999 {
- return false, true
- }
- return major*1000+minor >= x*1000+y, false
-}
-
-var kernelVersionRe = regexp.MustCompile(`^([0-9]+)\.([0-9]+)`)
diff --git a/pkg/host/features_linux_test.go b/pkg/host/features_linux_test.go
deleted file mode 100644
index 35d6197c1..000000000
--- a/pkg/host/features_linux_test.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2021 syzkaller project authors. All rights reserved.
-// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
-
-package host
-
-import (
- "strings"
- "testing"
-)
-
-func TestRequireKernel(t *testing.T) {
- if what := requireKernel(2, 999); what != "" {
- t.Fatalf("requireKernel(2, 999) failed: %v", what)
- }
- if what := requireKernel(3, 0); what != "" {
- t.Fatalf("requireKernel(3, 0) failed: %v", what)
- }
- if what := requireKernel(99, 1); what == "" {
- t.Fatalf("requireKernel(99, 1) succeeded")
- } else if !strings.HasPrefix(what, "kernel 99.1 required") {
- t.Fatalf("requireKernel(99, 1) failed: %v", what)
- }
-}
diff --git a/pkg/host/host_darwin.go b/pkg/host/host_darwin.go
deleted file mode 100644
index c400d2895..000000000
--- a/pkg/host/host_darwin.go
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2018 syzkaller project authors. All rights reserved.
-// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
-
-package host
-
-import (
- "github.com/google/syzkaller/prog"
-)
-
-func isSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- return true, ""
-}
-
-func init() {
- checkFeature[FeatureCoverage] = unconditionallyEnabled
-}
diff --git a/pkg/host/host_freebsd.go b/pkg/host/host_freebsd.go
deleted file mode 100644
index 726429c18..000000000
--- a/pkg/host/host_freebsd.go
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2017 syzkaller project authors. All rights reserved.
-// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
-
-package host
-
-import (
- "github.com/google/syzkaller/prog"
-)
-
-func isSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- return true, ""
-}
-
-func init() {
- checkFeature[FeatureCoverage] = unconditionallyEnabled
- checkFeature[FeatureComparisons] = unconditionallyEnabled
- checkFeature[FeatureDelayKcovMmap] = unconditionallyEnabled
- checkFeature[FeatureNetInjection] = unconditionallyEnabled
-}
diff --git a/pkg/host/host_fuchsia.go b/pkg/host/host_fuchsia.go
deleted file mode 100644
index 49f1b6406..000000000
--- a/pkg/host/host_fuchsia.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2017 syzkaller project authors. All rights reserved.
-// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
-
-//go:build fuchsia
-
-package host
-
-import (
- "github.com/google/syzkaller/prog"
-)
-
-func isSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- return true, ""
-}
diff --git a/pkg/host/host_netbsd.go b/pkg/host/host_netbsd.go
deleted file mode 100644
index e4de2d408..000000000
--- a/pkg/host/host_netbsd.go
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2017 syzkaller project authors. All rights reserved.
-// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
-
-package host
-
-import (
- "github.com/google/syzkaller/pkg/osutil"
- "github.com/google/syzkaller/prog"
-)
-
-func isSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- switch c.CallName {
- case "syz_usb_connect", "syz_usb_disconnect":
- reason := checkUSBEmulation()
- return reason == "", reason
- default:
- return true, ""
- }
-}
-
-func init() {
- checkFeature[FeatureCoverage] = unconditionallyEnabled
- checkFeature[FeatureComparisons] = unconditionallyEnabled
- checkFeature[FeatureUSBEmulation] = checkUSBEmulation
- checkFeature[FeatureExtraCoverage] = checkUSBEmulation
- checkFeature[FeatureDelayKcovMmap] = unconditionallyEnabled
- checkFeature[FeatureFault] = checkFault
-}
-
-func checkUSBEmulation() string {
- if err := osutil.IsAccessible("/dev/vhci0"); err != nil {
- return err.Error()
- }
- return ""
-}
-
-func checkFault() string {
- if err := osutil.IsAccessible("/dev/fault"); err != nil {
- return err.Error()
- }
- return ""
-}
diff --git a/pkg/host/host_openbsd.go b/pkg/host/host_openbsd.go
deleted file mode 100644
index 3099bc771..000000000
--- a/pkg/host/host_openbsd.go
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2017 syzkaller project authors. All rights reserved.
-// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
-
-package host
-
-import (
- "fmt"
- "strings"
- "syscall"
-
- "github.com/google/syzkaller/prog"
-)
-
-func isSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- if strings.HasPrefix(c.CallName, "ioctl$VMM_") {
- return isSupportedVMM()
- }
- return true, ""
-}
-
-func isSupportedVMM() (bool, string) {
- device := "/dev/vmm"
- fd, err := syscall.Open(device, syscall.O_RDONLY, 0)
- if fd == -1 {
- return false, fmt.Sprintf("open(%v) failed: %v", device, err)
- }
- syscall.Close(fd)
- return true, ""
-}
-
-func init() {
- checkFeature[FeatureCoverage] = unconditionallyEnabled
- checkFeature[FeatureComparisons] = unconditionallyEnabled
- checkFeature[FeatureExtraCoverage] = unconditionallyEnabled
- checkFeature[FeatureDelayKcovMmap] = unconditionallyEnabled
- checkFeature[FeatureNetInjection] = unconditionallyEnabled
- checkFeature[FeatureSandboxSetuid] = unconditionallyEnabled
-}
diff --git a/pkg/host/host_test.go b/pkg/host/host_test.go
deleted file mode 100644
index f39b6bc68..000000000
--- a/pkg/host/host_test.go
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2015 syzkaller project authors. All rights reserved.
-// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
-
-package host
-
-import (
- "fmt"
- "runtime"
- "testing"
-
- "github.com/google/syzkaller/pkg/testutil"
- "github.com/google/syzkaller/prog"
- _ "github.com/google/syzkaller/sys"
-)
-
-func TestDetectSupportedSyscalls(t *testing.T) {
- // Note: this test is not parallel because it modifies global testFallback var.
- for _, fallback := range []bool{false, true} {
- if testutil.RaceEnabled && fallback {
- continue // too slow under race detector
- }
- t.Run(fmt.Sprintf("fallback=%v", fallback), func(t *testing.T) {
- oldFallback := testFallback
- testFallback = fallback
- defer func() { testFallback = oldFallback }()
- target, err := prog.GetTarget(runtime.GOOS, runtime.GOARCH)
- if err != nil {
- t.Fatal(err)
- }
- enabled := make(map[*prog.Syscall]bool)
- for _, c := range target.Syscalls {
- enabled[c] = true
- }
- // Dump for manual inspection.
- supp, disabled, err := DetectSupportedSyscalls(target, "none", enabled)
- if err != nil {
- t.Fatal(err)
- }
- for c, ok := range supp {
- if !ok {
- t.Fatalf("map contains false value for %v", c.Name)
- }
- }
- t.Logf("unsupported:")
- for c, reason := range disabled {
- t.Logf("%v: %v", c.Name, reason)
- }
- _, disabled = target.TransitivelyEnabledCalls(supp)
- t.Logf("\n\ntransitively unsupported:")
- for c, reason := range disabled {
- t.Logf("%v: %v", c.Name, reason)
- }
- })
- }
-}
-
-func TestCheck(t *testing.T) {
- t.Parallel()
- target, err := prog.GetTarget(runtime.GOOS, runtime.GOARCH)
- if err != nil {
- t.Fatal(err)
- }
- features, err := Check(target)
- if err != nil {
- t.Fatal(err)
- }
- for _, feat := range features.Supported() {
- t.Logf("%-24v: %v", feat.Name, feat.Reason)
- }
-}
diff --git a/pkg/host/host_windows.go b/pkg/host/host_windows.go
deleted file mode 100644
index 13f9b5eb1..000000000
--- a/pkg/host/host_windows.go
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2017 syzkaller project authors. All rights reserved.
-// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
-
-package host
-
-import (
- "github.com/google/syzkaller/prog"
-)
-
-func isSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- return true, ""
-}
diff --git a/pkg/host/syscalls.go b/pkg/host/syscalls.go
deleted file mode 100644
index 3068cec2b..000000000
--- a/pkg/host/syscalls.go
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2018 syzkaller project authors. All rights reserved.
-// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
-
-package host
-
-import (
- "github.com/google/syzkaller/pkg/log"
- "github.com/google/syzkaller/prog"
-)
-
-// DetectSupportedSyscalls returns list on supported and unsupported syscalls on the host.
-// For unsupported syscalls it also returns reason as to why it is unsupported.
-func DetectSupportedSyscalls(target *prog.Target, sandbox string, enabled map[*prog.Syscall]bool) (
- map[*prog.Syscall]bool, map[*prog.Syscall]string, error) {
- log.Logf(1, "detecting supported syscalls")
- supported := make(map[*prog.Syscall]bool)
- unsupported := make(map[*prog.Syscall]string)
- // These do not have own host and parasitize on some other OS.
- if noHostChecks(target) {
- for _, c := range target.Syscalls {
- if c.Attrs.Disabled || !enabled[c] {
- continue
- }
- supported[c] = true
- }
- } else {
- for _, c := range target.Syscalls {
- if c.Attrs.Disabled || !enabled[c] {
- continue
- }
- ok, reason := isSupported(c, target, sandbox)
- if ok {
- supported[c] = true
- } else {
- if reason == "" {
- reason = "unknown"
- }
- unsupported[c] = reason
- }
- }
- }
- return supported, unsupported, nil
-}
-
-var testFallback = false
diff --git a/pkg/host/syscalls_linux.go b/pkg/host/syscalls_linux.go
deleted file mode 100644
index b690f1de7..000000000
--- a/pkg/host/syscalls_linux.go
+++ /dev/null
@@ -1,568 +0,0 @@
-// Copyright 2015 syzkaller project authors. All rights reserved.
-// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
-
-package host
-
-import (
- "bytes"
- "fmt"
- "os"
- "regexp"
- "runtime"
- "strconv"
- "strings"
- "sync"
- "syscall"
- "time"
-
- "github.com/google/syzkaller/pkg/osutil"
- "github.com/google/syzkaller/prog"
- "github.com/google/syzkaller/sys/targets"
- "golang.org/x/sys/unix"
-)
-
-func isSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- if strings.HasPrefix(c.CallName, "syz_") {
- return isSupportedSyzkall(c, target, sandbox)
- }
- if reason := isSupportedLSM(c); reason != "" {
- return false, reason
- }
- if strings.HasPrefix(c.Name, "socket$") ||
- strings.HasPrefix(c.Name, "socketpair$") {
- return isSupportedSocket(c)
- }
- if strings.HasPrefix(c.Name, "openat$") {
- return isSupportedOpenAt(c)
- }
- if strings.HasPrefix(c.Name, "mount$") {
- return isSupportedMount(c, sandbox)
- }
- if c.CallName == "pkey_alloc" {
- return isSyzPkeySetSupported(c, target, sandbox)
- }
- return isSupportedSyscall(c, target)
-}
-
-func isSupportedSyscall(c *prog.Syscall, target *prog.Target) (bool, string) {
- // There are 3 possible strategies for detecting supported syscalls:
- // 1. Executes all syscalls with presumably invalid arguments and check for ENOprog.
- // But not all syscalls are safe to execute. For example, pause will hang,
- // while setpgrp will push the process into own process group.
- // 2. Check presence of /sys/kernel/debug/tracing/events/syscalls/sys_enter_* files.
- // This requires root and CONFIG_FTRACE_SYSCALLS. Also it lies for some syscalls.
- // For example, on x86_64 it says that sendfile is not present (only sendfile64).
- // 3. Check sys_syscallname in /proc/kallsyms.
- // Requires CONFIG_KALLSYMS.
- // Kallsyms seems to be the most reliable and fast. That's what we use first.
- // If kallsyms is not present, we fallback to execution of syscalls.
- kallsymsOnce.Do(func() {
- kallsyms, _ := os.ReadFile("/proc/kallsyms")
- if len(kallsyms) == 0 {
- return
- }
- kallsymsSyscallSet = parseKallsyms(kallsyms, target.Arch)
- })
- if !testFallback && len(kallsymsSyscallSet) != 0 {
- r, v := isSupportedKallsyms(c)
- return r, v
- }
- return isSupportedTrial(c)
-}
-
-func isSupportedSyscallName(name string, target *prog.Target) (bool, string) {
- syscall := target.SyscallMap[name]
- if syscall == nil {
- return false, fmt.Sprintf("sys_%v is not present in the target", name)
- }
- return isSupportedSyscall(syscall, target)
-}
-
-func parseKallsyms(kallsyms []byte, arch string) map[string]bool {
- set := make(map[string]bool)
- var re *regexp.Regexp
- switch arch {
- case targets.I386, targets.AMD64:
- re = regexp.MustCompile(` T (__ia32_|__x64_)?sys_([^\n]+)\n`)
- case targets.ARM, targets.ARM64:
- re = regexp.MustCompile(` T (__arm64_)?sys_([^\n]+)\n`)
- case targets.PPC64LE:
- re = regexp.MustCompile(` T ()?sys_([^\n]+)\n`)
- case targets.MIPS64LE:
- re = regexp.MustCompile(` T sys_(mips_)?([^\n]+)\n`)
- case targets.S390x:
- re = regexp.MustCompile(` T (__s390_|__s390x_)?sys_([^\n]+)\n`)
- case targets.RiscV64:
- re = regexp.MustCompile(` T sys_(riscv_)?([^\n]+)\n`)
- default:
- panic("unsupported arch for kallsyms parsing")
- }
- matches := re.FindAllSubmatch(kallsyms, -1)
- for _, m := range matches {
- name := string(m[2])
- set[name] = true
- }
- return set
-}
-
-func isSupportedKallsyms(c *prog.Syscall) (bool, string) {
- name := c.CallName
- if newname := kallsymsRenameMap[name]; newname != "" {
- name = newname
- }
- if !kallsymsSyscallSet[name] {
- return false, fmt.Sprintf("sys_%v is not enabled in the kernel (not present in /proc/kallsyms)", name)
- }
- return true, ""
-}
-
-func isSupportedTrial(c *prog.Syscall) (bool, string) {
- switch c.CallName {
- // These known to cause hangs.
- case "exit", "pause":
- return true, ""
- }
- reason := fmt.Sprintf("sys_%v is not enabled in the kernel (returns ENOSYS)", c.CallName)
- trialMu.Lock()
- defer trialMu.Unlock()
- if res, ok := trialSupported[c.NR]; ok {
- return res, reason
- }
- cmd := osutil.Command(os.Args[0])
- cmd.Env = []string{fmt.Sprintf("SYZ_TRIAL_TEST=%v", c.NR)}
- _, err := osutil.Run(10*time.Second, cmd)
- res := err != nil
- trialSupported[c.NR] = res
- return res, reason
-}
-
-func init() {
- str := os.Getenv("SYZ_TRIAL_TEST")
- if str == "" {
- return
- }
- nr, err := strconv.Atoi(str)
- if err != nil {
- panic(err)
- }
- arg := ^uintptr(0) - 1e4 // something as invalid as possible
- _, _, err = syscall.Syscall6(uintptr(nr), arg, arg, arg, arg, arg, arg)
- if err == syscall.ENOSYS {
- os.Exit(0)
- }
- os.Exit(1)
-}
-
-// Some syscall names diverge in __NR_* consts and kallsyms.
-// umount2 is renamed to umount in arch/x86/entry/syscalls/syscall_64.tbl.
-// Where umount is renamed to oldumount is unclear.
-var (
- kallsymsOnce sync.Once
- kallsymsSyscallSet map[string]bool
- kallsymsRenameMap = map[string]string{
- "umount": "oldumount",
- "umount2": "umount",
- "stat": "newstat",
- }
- trialMu sync.Mutex
- trialSupported = make(map[uint64]bool)
- filesystems []byte
- filesystemsOnce sync.Once
- lsmOnce sync.Once
- lsmError error
- lsmDisabled map[string]bool
-)
-
-func isSyzUsbSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- reason := checkUSBEmulation()
- return reason == "", reason
-}
-
-func alwaysSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- return true, ""
-}
-
-func isNetInjectionSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- reason := checkNetInjection()
- return reason == "", reason
-}
-
-func isVhciInjectionSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- reason := checkVhciInjection()
- return reason == "", reason
-}
-
-func isWifiEmulationSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- reason := checkWifiEmulation()
- return reason == "", reason
-}
-
-func isSyzKvmSetupCPUSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- switch c.Name {
- case "syz_kvm_setup_cpu$x86":
- if runtime.GOARCH == targets.AMD64 || runtime.GOARCH == targets.I386 {
- return true, ""
- }
- case "syz_kvm_setup_cpu$arm64":
- if runtime.GOARCH == targets.ARM64 {
- return true, ""
- }
- case "syz_kvm_setup_cpu$ppc64":
- if runtime.GOARCH == targets.PPC64LE {
- return true, ""
- }
- }
- return false, "unsupported arch"
-}
-
-func isSyzOpenDevSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- return isSupportedSyzOpenDev(sandbox, c)
-}
-
-func isSyzInitNetSocketSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- if ok, reason := onlySandboxNone(sandbox); !ok {
- return false, reason
- }
- return isSupportedSocket(c)
-}
-
-func isSyzSocketConnectNvmeTCPSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- return onlySandboxNone(sandbox)
-}
-
-func isSyzGenetlinkGetFamilyIDSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_GENERIC)
- if fd == -1 {
- return false, fmt.Sprintf("socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC) failed: %v", err)
- }
- // TODO: try to obtain actual family ID here. It will disable whole sets of sendmsg syscalls.
- syscall.Close(fd)
- return true, ""
-}
-
-func isSyzMountImageSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- if ok, reason := onlySandboxNone(sandbox); !ok {
- return ok, reason
- }
- fstype, ok := extractStringConst(c.Args[0].Type)
- if !ok {
- panic("syz_mount_image arg is not string")
- }
- return isSupportedFilesystem(fstype)
-}
-
-func isSyzReadPartTableSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- return onlySandboxNone(sandbox)
-}
-
-func isSyzIoUringSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- return isSupportedSyscallName("io_uring_setup", target)
-}
-
-func isSyzMemcpySupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- ret, msg := isSyzIoUringSupported(c, target, sandbox)
- if ret {
- return ret, msg
- }
- return isSyzKvmSetupCPUSupported(c, target, sandbox)
-}
-
-func isBtfVmlinuxSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- if err := osutil.IsAccessible("/sys/kernel/btf/vmlinux"); err != nil {
- return false, err.Error()
- }
- return onlySandboxNone(sandbox)
-}
-
-func isSyzFuseSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- if ok, reason := isSupportedFilesystem("fuse"); !ok {
- return ok, reason
- }
- if ok, reason := onlySandboxNoneOrNamespace(sandbox); !ok {
- return false, reason
- }
- return true, ""
-}
-
-func isSyzUsbIPSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- if err := osutil.IsWritable("/sys/devices/platform/vhci_hcd.0/attach"); err != nil {
- return false, err.Error()
- }
- return onlySandboxNoneOrNamespace(sandbox)
-}
-
-var syzkallSupport = map[string]func(*prog.Syscall, *prog.Target, string) (bool, string){
- "syz_open_dev": isSyzOpenDevSupported,
- "syz_open_procfs": isSyzOpenProcfsSupported,
- "syz_open_pts": alwaysSupported,
- "syz_execute_func": alwaysSupported,
- "syz_emit_ethernet": isNetInjectionSupported,
- "syz_extract_tcp_res": isNetInjectionSupported,
- "syz_usb_connect": isSyzUsbSupported,
- "syz_usb_connect_ath9k": isSyzUsbSupported,
- "syz_usb_disconnect": isSyzUsbSupported,
- "syz_usb_control_io": isSyzUsbSupported,
- "syz_usb_ep_write": isSyzUsbSupported,
- "syz_usb_ep_read": isSyzUsbSupported,
- "syz_kvm_setup_cpu": isSyzKvmSetupCPUSupported,
- "syz_emit_vhci": isVhciInjectionSupported,
- "syz_init_net_socket": isSyzInitNetSocketSupported,
- "syz_genetlink_get_family_id": isSyzGenetlinkGetFamilyIDSupported,
- "syz_mount_image": isSyzMountImageSupported,
- "syz_read_part_table": isSyzReadPartTableSupported,
- "syz_io_uring_submit": isSyzIoUringSupported,
- "syz_io_uring_complete": isSyzIoUringSupported,
- "syz_io_uring_setup": isSyzIoUringSupported,
- "syz_memcpy_off": isSyzMemcpySupported,
- "syz_btf_id_by_name": isBtfVmlinuxSupported,
- "syz_fuse_handle_req": isSyzFuseSupported,
- "syz_80211_inject_frame": isWifiEmulationSupported,
- "syz_80211_join_ibss": isWifiEmulationSupported,
- "syz_usbip_server_init": isSyzUsbIPSupported,
- "syz_clone": alwaysSupported,
- "syz_clone3": alwaysSupported,
- "syz_pkey_set": isSyzPkeySetSupported,
- "syz_socket_connect_nvme_tcp": isSyzSocketConnectNvmeTCPSupported,
- "syz_pidfd_open": alwaysSupported,
-}
-
-func isSupportedSyzkall(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- sysTarget := targets.Get(target.OS, target.Arch)
- for _, depCall := range sysTarget.PseudoSyscallDeps[c.CallName] {
- if ok, reason := isSupportedSyscallName(depCall, target); !ok {
- return ok, reason
- }
- }
- if strings.HasPrefix(c.CallName, "syz_ext_") {
- // Non-mainline pseudo-syscalls in executor/common_ext.h can't have the checking function
- // and are assumed to be unconditionally supported.
- if syzkallSupport[c.CallName] != nil {
- panic("syz_ext_ prefix is reserved for non-mainline pseudo-syscalls")
- }
- return true, ""
- }
- if isSupported, ok := syzkallSupport[c.CallName]; ok {
- return isSupported(c, target, sandbox)
- }
- panic("unknown syzkall: " + c.Name)
-}
-
-func isSupportedSyzOpenDev(sandbox string, c *prog.Syscall) (bool, string) {
- if _, ok := c.Args[0].Type.(*prog.ConstType); ok {
- // This is for syz_open_dev$char/block.
- return true, ""
- }
- fname, ok := extractStringConst(c.Args[0].Type)
- if !ok {
- panic("first open arg is not a pointer to string const")
- }
- if strings.Contains(fname, "/dev/raw/raw#") {
- // For syz_open_dev$char_raw, these files don't exist initially.
- return true, ""
- }
- if !strings.Contains(fname, "#") {
- panic(fmt.Sprintf("%v does not contain # in the file name (should be openat)", c.Name))
- }
- if checkUSBEmulation() == "" {
- // These entries might not be available at boot time,
- // but will be created by connected USB devices.
- USBDevicePrefixes := []string{
- "/dev/hidraw", "/dev/usb/hiddev", "/dev/input/",
- }
- for _, prefix := range USBDevicePrefixes {
- if strings.HasPrefix(fname, prefix) {
- return true, ""
- }
- }
- }
- var check func(dev string) bool
- check = func(dev string) bool {
- if !strings.Contains(dev, "#") {
- // Note: don't try to open them all, some can hang (e.g. /dev/snd/pcmC#D#p).
- return osutil.IsExist(dev)
- }
- for i := 0; i < 10; i++ {
- if check(strings.Replace(dev, "#", strconv.Itoa(i), 1)) {
- return true
- }
- }
- return false
- }
- if !check(fname) {
- return false, fmt.Sprintf("file %v does not exist", fname)
- }
- return onlySandboxNoneOrNamespace(sandbox)
-}
-
-func isSupportedLSM(c *prog.Syscall) string {
- lsmOnce.Do(func() {
- data, err := os.ReadFile("/sys/kernel/security/lsm")
- if err != nil {
- // securityfs may not be mounted, but it does not mean
- // that no LSMs are enabled.
- if !os.IsNotExist(err) {
- lsmError = err
- }
- return
- }
- lsmDisabled = make(map[string]bool)
- for _, lsm := range []string{"selinux", "apparmor", "smack"} {
- if !strings.Contains(string(data), lsm) {
- lsmDisabled[lsm] = true
- }
- }
- })
- if lsmError != nil {
- return lsmError.Error()
- }
- for lsm := range lsmDisabled {
- if strings.Contains(strings.ToLower(c.Name), lsm) {
- return fmt.Sprintf("LSM %v is not enabled", lsm)
- }
- }
- return ""
-}
-
-func onlySandboxNone(sandbox string) (bool, string) {
- if syscall.Getuid() != 0 || sandbox != "none" {
- return false, "only supported under root with sandbox=none"
- }
- return true, ""
-}
-
-func onlySandboxNoneOrNamespace(sandbox string) (bool, string) {
- if syscall.Getuid() != 0 || sandbox == "setuid" {
- return false, "only supported under root with sandbox=none/namespace"
- }
- return true, ""
-}
-
-func isSupportedSocket(c *prog.Syscall) (bool, string) {
- af, ok := c.Args[0].Type.(*prog.ConstType)
- if !ok {
- panic("socket family is not const")
- }
- fd, err := syscall.Socket(int(af.Val), 0, 0)
- if fd != -1 {
- syscall.Close(fd)
- }
- if err == syscall.ENOSYS {
- return false, "socket syscall returns ENOSYS"
- }
- if err == syscall.EAFNOSUPPORT {
- return false, "socket family is not supported (EAFNOSUPPORT)"
- }
- proto, ok := c.Args[2].Type.(*prog.ConstType)
- if !ok {
- return true, ""
- }
- var typ uint64
- if arg, ok := c.Args[1].Type.(*prog.ConstType); ok {
- typ = arg.Val
- } else if arg, ok := c.Args[1].Type.(*prog.FlagsType); ok {
- typ = arg.Vals[0]
- } else {
- return true, ""
- }
- fd, err = syscall.Socket(int(af.Val), int(typ), int(proto.Val))
- if fd != -1 {
- syscall.Close(fd)
- return true, ""
- }
- return false, err.Error()
-}
-
-func isSyzOpenProcfsSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- return isSupportedOpenFile(c, 1, nil)
-}
-
-func isSupportedOpenAt(c *prog.Syscall) (bool, string) {
- // Attempt to extract flags from the syscall description.
- var modes []int
- if mode, ok := c.Args[2].Type.(*prog.ConstType); ok {
- modes = []int{int(mode.Val)}
- }
- return isSupportedOpenFile(c, 1, modes)
-}
-
-func isSupportedOpenFile(c *prog.Syscall, filenameArg int, modes []int) (bool, string) {
- fname, ok := extractStringConst(c.Args[filenameArg].Type)
- if !ok || fname == "" || fname[0] != '/' {
- return true, ""
- }
- if len(modes) == 0 {
- modes = []int{syscall.O_RDONLY, syscall.O_WRONLY, syscall.O_RDWR, syscall.O_RDONLY | syscall.O_NONBLOCK}
- }
- var err error
- for _, mode := range modes {
- var fd int
- fd, err = syscall.Open(fname, mode, 0)
- if fd != -1 {
- syscall.Close(fd)
- }
- if err == nil {
- return true, ""
- }
- }
- return false, fmt.Sprintf("open(%v) failed: %v", fname, err)
-}
-
-func isSupportedMount(c *prog.Syscall, sandbox string) (bool, string) {
- fstype, ok := extractStringConst(c.Args[2].Type)
- if !ok {
- panic(fmt.Sprintf("%v: filesystem is not string const", c.Name))
- }
- if ok, reason := isSupportedFilesystem(fstype); !ok {
- return ok, reason
- }
- switch fstype {
- case "fuse", "fuseblk":
- if err := osutil.IsAccessible("/dev/fuse"); err != nil {
- return false, err.Error()
- }
- return onlySandboxNoneOrNamespace(sandbox)
- default:
- return onlySandboxNone(sandbox)
- }
-}
-
-func isSupportedFilesystem(fstype string) (bool, string) {
- filesystemsOnce.Do(func() {
- filesystems, _ = os.ReadFile("/proc/filesystems")
- })
- if !bytes.Contains(filesystems, []byte("\t"+fstype+"\n")) {
- return false, fmt.Sprintf("/proc/filesystems does not contain %v", fstype)
- }
- return true, ""
-}
-
-func extractStringConst(typ prog.Type) (string, bool) {
- ptr, ok := typ.(*prog.PtrType)
- if !ok {
- panic("first open arg is not a pointer to string const")
- }
- str, ok := ptr.Elem.(*prog.BufferType)
- if !ok || str.Kind != prog.BufferString || len(str.Values) == 0 {
- return "", false
- }
- v := str.Values[0]
- for v != "" && v[len(v)-1] == 0 {
- v = v[:len(v)-1] // string terminating \x00
- }
- return v, true
-}
-
-func isSyzPkeySetSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- if target.Arch != targets.AMD64 && target.Arch != targets.I386 {
- return false, "unsupported arch"
- }
- key, _, err := syscall.Syscall6(unix.SYS_PKEY_ALLOC, 0, 0, 0, 0, 0, 0)
- if err != 0 {
- return false, fmt.Sprintf("pkey_alloc failed: %v", err)
- }
- _, _, err = syscall.Syscall6(unix.SYS_PKEY_FREE, key, 0, 0, 0, 0, 0)
- if err != 0 {
- return false, fmt.Sprintf("pkey_free failed: %v", err)
- }
- return true, ""
-}
diff --git a/pkg/host/syscalls_linux_test.go b/pkg/host/syscalls_linux_test.go
deleted file mode 100644
index fc5d13dd5..000000000
--- a/pkg/host/syscalls_linux_test.go
+++ /dev/null
@@ -1,307 +0,0 @@
-// Copyright 2015 syzkaller project authors. All rights reserved.
-// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
-
-//go:build linux
-
-package host
-
-import (
- "fmt"
- "runtime"
- "syscall"
- "testing"
-
- "github.com/google/syzkaller/prog"
- "github.com/google/syzkaller/sys/targets"
-)
-
-func TestSupportedSyscalls(t *testing.T) {
- t.Parallel()
- target, err := prog.GetTarget(targets.Linux, runtime.GOARCH)
- if err != nil {
- t.Fatal(err)
- }
- // These are safe to execute with invalid arguments.
- safe := []string{
- "memfd_create",
- "sendfile",
- "bpf$MAP_CREATE",
- "openat",
- "read",
- "write",
- }
- enabled := make(map[*prog.Syscall]bool)
- for _, name := range safe {
- c := target.SyscallMap[name]
- if c == nil {
- t.Fatalf("can't find syscall '%v'", name)
- }
- enabled[c] = true
- }
- supp, _, err := DetectSupportedSyscalls(target, "none", enabled)
- if err != nil {
- t.Skipf("skipping: %v", err)
- }
- for c := range enabled {
- a := ^uintptr(0) - 4097 // hopefully invalid
- _, _, err := syscall.Syscall6(uintptr(c.NR), a, a, a, a, a, a)
- if err == 0 {
- t.Fatalf("%v did not fail", c.Name)
- }
- if ok := err != syscall.ENOSYS; ok != supp[c] {
- t.Fatalf("syscall %v: perse=%v kallsyms=%v", c.Name, ok, supp[c])
- }
- }
-}
-
-func TestKallsymsParse(t *testing.T) {
- tests := []struct {
- Arch string
- Kallsyms []byte
- ParsedSyscalls []string
- SupportedSyscalls []string
- }{
- {
- targets.AMD64,
- []byte(`
-ffffffff817cdcc0 T __sys_bind
-ffffffff817cdda0 T __x64_sys_bind
-ffffffff817cddc0 T __ia32_sys_bind
-ffffffff817cdde0 T __sys_listen
-ffffffff817cde80 T __x64_sys_listen
-ffffffff817cde90 T __ia32_sys_listen
-ffffffff817cdea0 T __sys_accept4
-ffffffff817ce080 T __x64_sys_accept4
-ffffffff817ce0a0 T __ia32_sys_accept4
- `),
- []string{"bind", "listen", "accept4"},
- []string{"bind", "listen", "accept4"},
- },
- {
- targets.ARM64,
- []byte(`
-ffff000010a3ddf8 T __sys_bind
-ffff000010a3def8 T __arm64_sys_bind
-ffff000010a3df20 T __sys_listen
-ffff000010a3dfd8 T __arm64_sys_listen
-ffff000010a3e000 T __sys_accept4
-ffff000010a3e1f0 T __arm64_sys_accept4
- `),
- []string{"bind", "listen", "accept4"},
- []string{"bind", "listen", "accept4"},
- },
- {
- targets.PPC64LE,
- []byte(`
-c0000000011ec810 T __sys_bind
-c0000000011eca10 T sys_bind
-c0000000011eca10 T __se_sys_bind
-c0000000011eca70 T __sys_listen
-c0000000011ecc10 T sys_listen
-c0000000011ecc10 T __se_sys_listen
-c0000000011ecc70 T __sys_accept4
-c0000000011ed050 T sys_accept4
-c0000000011ed050 T __se_sys_accept4
- `),
- []string{"bind", "listen", "accept4"},
- []string{"bind", "listen", "accept4"},
- },
- {
- targets.ARM,
- []byte(`
-c037c67c T __se_sys_setfsuid
-c037c694 T __sys_setfsgid
-c037c790 T sys_setfsgid
-c037c790 T __se_sys_setfsgid
-c037c7a8 T sys_getpid
-c037c7d0 T sys_gettid
-c037c7f8 T sys_getppid
- `),
- []string{"setfsgid", "getpid", "gettid", "getppid"},
- []string{"setfsgid", "getpid", "gettid", "getppid"},
- },
- // Test kallsymsRenameMap.
- {
- targets.PPC64LE,
- []byte(`
-c00000000037eb00 T sys_newstat
- `),
- []string{"newstat"},
- []string{"stat"},
- },
- {
- targets.S390x,
- []byte(`
-0000000000e4f760 T __sys_bind
-0000000000e4f8e8 T __s390_sys_bind
-0000000000e4f938 T __s390x_sys_bind
-0000000000e4f938 T __se_sys_bind
-0000000000e4f988 T __sys_listen
-0000000000e4fab0 T __s390_sys_listen
-0000000000e4faf8 T __s390x_sys_listen
-0000000000e4faf8 T __se_sys_listen
-0000000000e4fb40 T __sys_accept4
-0000000000e4fe58 T __s390_sys_accept4
-0000000000e4feb0 T __s390x_sys_accept4
-0000000000e4feb0 T __se_sys_accept4
- `),
- []string{"bind", "listen", "accept4"},
- []string{"bind", "listen", "accept4"},
- },
- {
- targets.RiscV64,
- []byte(`
-ffffffe0005c9b02 T __sys_bind
-ffffffe0005c9ba0 T sys_bind
-ffffffe0005c9ba0 T __se_sys_bind
-ffffffe0005c9e72 T __sys_accept4
-ffffffe0005c9f00 T sys_accept4
-ffffffe0005c9f00 T __se_sys_accept4
-ffffffe0005c9bd8 T __sys_listen
-ffffffe0005c9c76 T sys_listen
-ffffffe0005c9c76 T __se_sys_listen
- `),
- []string{"bind", "listen", "accept4"},
- []string{"bind", "listen", "accept4"},
- },
- }
-
- for _, test := range tests {
- syscallSet := parseKallsyms(test.Kallsyms, test.Arch)
- if len(syscallSet) != len(test.ParsedSyscalls) {
- t.Fatalf("wrong number of parse syscalls, expected: %v, got: %v",
- len(test.ParsedSyscalls), len(syscallSet))
- }
- for _, syscall := range test.ParsedSyscalls {
- if _, ok := syscallSet[syscall]; !ok {
- t.Fatalf("syscall %v not found in parsed syscall list", syscall)
- }
- }
- for _, syscall := range test.SupportedSyscalls {
- if newname := kallsymsRenameMap[syscall]; newname != "" {
- syscall = newname
- }
-
- if _, ok := syscallSet[syscall]; !ok {
- t.Fatalf("syscall %v not found in supported syscall list", syscall)
- }
- }
- }
-}
-
-func TestMatchKernelVersion(t *testing.T) {
- tests := []struct {
- version string
- major int
- minor int
- res bool
- bad bool
- }{
- {
- version: "5.9.0-rc5-next-20200918",
- major: 5,
- minor: 8,
- res: true,
- },
- {
- version: "5.9.0-rc5-next-20200918",
- major: 4,
- minor: 10,
- res: true,
- },
- {
- version: "5.9.0-rc5-next-20200918",
- major: 5,
- minor: 9,
- res: true,
- },
- {
- version: "5.9.0-rc5-next-20200918",
- major: 5,
- minor: 10,
- res: false,
- },
- {
- version: "5.9.0-rc5-next-20200918",
- major: 6,
- minor: 0,
- res: false,
- },
- {
- version: "4.4.246-19567-g2c01e3dada31",
- major: 4,
- minor: 3,
- res: true,
- },
- {
- version: "4.4.246-19567-g2c01e3dada31",
- major: 4,
- minor: 4,
- res: true,
- },
- {
- version: "5.17.17-1debian-amd64",
- major: 5,
- minor: 16,
- res: true,
- },
- {
- version: "5.17.17-1debian-amd64",
- major: 5,
- minor: 17,
- res: true,
- },
- {
- version: "3.5",
- major: 3,
- minor: 4,
- res: true,
- },
- {
- version: "3.5",
- major: 3,
- minor: 5,
- res: true,
- },
- {
- version: "3.5",
- major: 3,
- minor: 6,
- res: false,
- },
- {
- version: "",
- bad: true,
- },
- {
- version: "something unparsable",
- bad: true,
- },
- {
- version: "mykernel 4.17-5",
- bad: true,
- },
- {
- version: "999999.999999",
- bad: true,
- },
- {
- version: "4.abc.def",
- bad: true,
- },
- }
- for i, test := range tests {
- t.Run(fmt.Sprint(i), func(t *testing.T) {
- ok, bad := matchKernelVersion(test.version, test.major, test.minor)
- if test.bad && !bad {
- t.Fatal("want error, but got no error")
- }
- if !test.bad && bad {
- t.Fatalf("want no error, but got error")
- }
- if test.res != ok {
- t.Fatalf("want match %v, but got %v", test.res, ok)
- }
- })
- }
-}
diff --git a/pkg/ipc/ipc.go b/pkg/ipc/ipc.go
index 79b2acc18..c83fabe84 100644
--- a/pkg/ipc/ipc.go
+++ b/pkg/ipc/ipc.go
@@ -159,6 +159,12 @@ func FlagsToSandbox(flags EnvFlags) string {
// nolint: gocyclo
func FeaturesToFlags(features flatrpc.Feature, manual csource.Features) EnvFlags {
+ for feat := range flatrpc.EnumNamesFeature {
+ opt := FlatRPCFeaturesToCSource[feat]
+ if opt != "" && manual != nil && !manual[opt].Enabled {
+ features &= ^feat
+ }
+ }
var flags EnvFlags
if manual == nil || manual["net_reset"].Enabled {
flags |= FlagEnableNetReset
@@ -175,27 +181,40 @@ func FeaturesToFlags(features flatrpc.Feature, manual csource.Features) EnvFlags
if features&flatrpc.FeatureDelayKcovMmap != 0 {
flags |= FlagDelayKcovMmap
}
- if features&flatrpc.FeatureNetInjection != 0 && (manual == nil || manual["tun"].Enabled) {
+ if features&flatrpc.FeatureNetInjection != 0 {
flags |= FlagEnableTun
}
- if features&flatrpc.FeatureNetDevices != 0 && (manual == nil || manual["net_dev"].Enabled) {
+ if features&flatrpc.FeatureNetDevices != 0 {
flags |= FlagEnableNetDev
}
- if features&flatrpc.FeatureDevlinkPCI != 0 && (manual == nil || manual["devlink_pci"].Enabled) {
+ if features&flatrpc.FeatureDevlinkPCI != 0 {
flags |= FlagEnableDevlinkPCI
}
- if features&flatrpc.FeatureNicVF != 0 && (manual == nil || manual["nic_vf"].Enabled) {
+ if features&flatrpc.FeatureNicVF != 0 {
flags |= FlagEnableNicVF
}
- if features&flatrpc.FeatureVhciInjection != 0 && (manual == nil || manual["vhci"].Enabled) {
+ if features&flatrpc.FeatureVhciInjection != 0 {
flags |= FlagEnableVhciInjection
}
- if features&flatrpc.FeatureWifiEmulation != 0 && (manual == nil || manual["wifi"].Enabled) {
+ if features&flatrpc.FeatureWifiEmulation != 0 {
flags |= FlagEnableWifi
}
return flags
}
+var FlatRPCFeaturesToCSource = map[flatrpc.Feature]string{
+ flatrpc.FeatureNetInjection: "tun",
+ flatrpc.FeatureNetDevices: "net_dev",
+ flatrpc.FeatureDevlinkPCI: "devlink_pci",
+ flatrpc.FeatureNicVF: "nic_vf",
+ flatrpc.FeatureVhciInjection: "vhci",
+ flatrpc.FeatureWifiEmulation: "wifi",
+ flatrpc.FeatureUSBEmulation: "usb",
+ flatrpc.FeatureBinFmtMisc: "binfmt_misc",
+ flatrpc.FeatureLRWPANEmulation: "ieee802154",
+ flatrpc.FeatureSwap: "swap",
+}
+
func MakeEnv(config *Config, pid int) (*Env, error) {
if config.Timeouts.Slowdown == 0 || config.Timeouts.Scale == 0 ||
config.Timeouts.Syscall == 0 || config.Timeouts.Program == 0 {
diff --git a/pkg/ipc/ipc_test.go b/pkg/ipc/ipc_test.go
index 8a8cdfac8..af4eaf24a 100644
--- a/pkg/ipc/ipc_test.go
+++ b/pkg/ipc/ipc_test.go
@@ -226,7 +226,7 @@ func TestExecutorCommonExt(t *testing.T) {
t.Skipf("skipping, broken cross-compiler: %v", sysTarget.BrokenCompiler)
}
bin := csource.BuildExecutor(t, target, "../..", "-DSYZ_TEST_COMMON_EXT_EXAMPLE=1")
- out, err := osutil.RunCmd(time.Minute, "", bin, "setup")
+ out, err := osutil.RunCmd(time.Minute, "", bin, "setup", "0")
if err != nil {
t.Fatal(err)
}
diff --git a/pkg/rpctype/rpctype.go b/pkg/rpctype/rpctype.go
index ffc089271..5fa128249 100644
--- a/pkg/rpctype/rpctype.go
+++ b/pkg/rpctype/rpctype.go
@@ -10,7 +10,6 @@ import (
"time"
"github.com/google/syzkaller/pkg/flatrpc"
- "github.com/google/syzkaller/pkg/host"
"github.com/google/syzkaller/pkg/ipc"
"github.com/google/syzkaller/pkg/signal"
)
@@ -92,8 +91,8 @@ type ConnectArgs struct {
type ConnectRes struct {
MemoryLeakFrames []string
DataRaceFrames []string
- // This is forwarded from CheckArgs, if checking was already done.
- Features *host.Features
+ // Bitmask of features to try to setup.
+ Features flatrpc.Feature
// Fuzzer reads these files inside of the VM and returns contents in CheckArgs.Files.
ReadFiles []string
ReadGlobs []string
@@ -102,7 +101,7 @@ type ConnectRes struct {
type CheckArgs struct {
Name string
Error string
- Features *host.Features
+ Features []flatrpc.FeatureInfo
Globs map[string][]string
Files []flatrpc.FileInfo
}
diff --git a/pkg/runtest/run_test.go b/pkg/runtest/run_test.go
index c06136078..f5798a9b4 100644
--- a/pkg/runtest/run_test.go
+++ b/pkg/runtest/run_test.go
@@ -14,7 +14,6 @@ import (
"time"
"github.com/google/syzkaller/pkg/csource"
- "github.com/google/syzkaller/pkg/host"
"github.com/google/syzkaller/pkg/ipc"
"github.com/google/syzkaller/pkg/osutil"
"github.com/google/syzkaller/pkg/testutil"
@@ -55,29 +54,14 @@ func test(t *testing.T, sysTarget *targets.Target) {
t.Fatal(err)
}
executor := csource.BuildExecutor(t, target, "../../", "-fsanitize-coverage=trace-pc")
- features, err := host.Check(target)
- if err != nil {
- t.Fatalf("failed to detect host features: %v", err)
- }
- enabled := make(map[*prog.Syscall]bool)
- for _, c := range target.Syscalls {
- enabled[c] = true
- }
- calls, _, err := host.DetectSupportedSyscalls(target, "none", enabled)
- if err != nil {
- t.Fatalf("failed to detect supported syscalls: %v", err)
+ calls := make(map[*prog.Syscall]bool)
+ for _, call := range target.Syscalls {
+ calls[call] = true
}
enabledCalls := map[string]map[*prog.Syscall]bool{
"": calls,
"none": calls,
}
- featureFlags, err := csource.ParseFeaturesFlags("none", "none", true)
- if err != nil {
- t.Fatal(err)
- }
- if err := host.Setup(target, features, featureFlags, executor); err != nil {
- t.Fatal(err)
- }
requests := make(chan *RunRequest, 2*runtime.GOMAXPROCS(0))
go func() {
for req := range requests {
@@ -93,7 +77,7 @@ func test(t *testing.T, sysTarget *targets.Target) {
Dir: filepath.Join("..", "..", "sys", target.OS, targets.TestOS),
Target: target,
Tests: *flagFilter,
- Features: features.ToFlatRPC(),
+ Features: 0,
EnabledCalls: enabledCalls,
Requests: requests,
LogFunc: func(text string) {
diff --git a/pkg/vminfo/features.go b/pkg/vminfo/features.go
new file mode 100644
index 000000000..2ba76a255
--- /dev/null
+++ b/pkg/vminfo/features.go
@@ -0,0 +1,192 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package vminfo
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/google/syzkaller/pkg/flatrpc"
+ "github.com/google/syzkaller/pkg/ipc"
+ "github.com/google/syzkaller/pkg/rpctype"
+ "github.com/google/syzkaller/prog"
+)
+
+type Feature struct {
+ Enabled bool
+ NeedSetup bool
+ Reason string
+}
+
+type Features map[flatrpc.Feature]Feature
+
+func (features Features) Enabled() flatrpc.Feature {
+ var mask flatrpc.Feature
+ for feat, info := range features {
+ if info.Enabled {
+ mask |= feat
+ }
+ }
+ return mask
+}
+
+func (features Features) NeedSetup() flatrpc.Feature {
+ var mask flatrpc.Feature
+ for feat, info := range features {
+ if info.Enabled && info.NeedSetup {
+ mask |= feat
+ }
+ }
+ return mask
+}
+
+type featureResult struct {
+ id flatrpc.Feature
+ reason string
+}
+
+func (ctx *checkContext) checkFeatures() {
+ testProg := ctx.target.DataMmapProg()
+ for feat := range flatrpc.EnumNamesFeature {
+ feat := feat
+ ctx.pendingRequests++
+ go func() {
+ envFlags, execFlags := ctx.featureToFlags(feat)
+ res := ctx.execProg(testProg, envFlags, execFlags)
+ reason := ctx.featureSucceeded(feat, testProg, res)
+ ctx.features <- featureResult{feat, reason}
+ }()
+ }
+}
+
+func (ctx *checkContext) finishFeatures(featureInfos []flatrpc.FeatureInfo) (Features, error) {
+ // Feature checking consists of 2 parts:
+ // - we ask executor to try to setup each feature (results are returned in featureInfos)
+ // - we also try to run a simple program with feature-specific flags
+ // Here we combine both results.
+ features := make(Features)
+ for _, info := range featureInfos {
+ features[info.Id] = Feature{
+ Reason: info.Reason,
+ NeedSetup: info.NeedSetup,
+ }
+ }
+ outputReplacer := strings.NewReplacer(
+ "SYZFAIL:", "",
+ "\n", ". ",
+ )
+ for range flatrpc.EnumNamesFeature {
+ res := <-ctx.features
+ feat := features[res.id]
+ if feat.Reason == "" {
+ feat.Reason = res.reason
+ }
+ if feat.Reason == "" {
+ feat.Reason = "enabled"
+ feat.Enabled = true
+ }
+ if pos := strings.Index(feat.Reason, "loop exited with status"); pos != -1 {
+ feat.Reason = feat.Reason[:pos]
+ }
+ // If executor exited the output is prefixed with "executor 4: EOF".
+ const executorPrefix = ": EOF\n"
+ if pos := strings.Index(feat.Reason, executorPrefix); pos != -1 {
+ feat.Reason = feat.Reason[pos+len(executorPrefix):]
+ }
+ feat.Reason = strings.TrimSpace(outputReplacer.Replace(feat.Reason))
+ features[res.id] = feat
+ }
+ if feat := features[flatrpc.FeatureSandboxSetuid]; !feat.Enabled {
+ return features, fmt.Errorf("execution of simple program fails: %v", feat.Reason)
+ }
+ if feat := features[flatrpc.FeatureCoverage]; ctx.cfg.Cover && !feat.Enabled {
+ return features, fmt.Errorf("coverage is not supported: %v", feat.Reason)
+ }
+ return features, nil
+}
+
+// featureToFlags creates ipc flags required to test the feature on a simple program.
+// For features that has setup procedure in the executor, we just execute with the default flags.
+func (ctx *checkContext) featureToFlags(feat flatrpc.Feature) (ipc.EnvFlags, ipc.ExecFlags) {
+ envFlags := ctx.sandbox
+ // These don't have a corresponding feature and are always enabled.
+ envFlags |= ipc.FlagEnableCloseFds | ipc.FlagEnableCgroups | ipc.FlagEnableNetReset
+ execFlags := ipc.FlagThreaded
+ switch feat {
+ case flatrpc.FeatureCoverage:
+ envFlags |= ipc.FlagSignal
+ execFlags |= ipc.FlagCollectSignal | ipc.FlagCollectCover
+ case flatrpc.FeatureComparisons:
+ envFlags |= ipc.FlagSignal
+ execFlags |= ipc.FlagCollectComps
+ case flatrpc.FeatureExtraCoverage:
+ envFlags |= ipc.FlagSignal | ipc.FlagExtraCover
+ execFlags |= ipc.FlagCollectSignal | ipc.FlagCollectCover
+ case flatrpc.FeatureDelayKcovMmap:
+ envFlags |= ipc.FlagSignal | ipc.FlagDelayKcovMmap
+ execFlags |= ipc.FlagCollectSignal | ipc.FlagCollectCover
+ case flatrpc.FeatureSandboxSetuid:
+ // We use setuid sandbox feature to test that the simple program
+ // succeeds under the actual sandbox (not necessary setuid).
+ // We do this because we don't have a feature for sandbox 'none'.
+ case flatrpc.FeatureSandboxNamespace:
+ case flatrpc.FeatureSandboxAndroid:
+ case flatrpc.FeatureFault:
+ case flatrpc.FeatureLeak:
+ case flatrpc.FeatureNetInjection:
+ envFlags |= ipc.FlagEnableTun
+ case flatrpc.FeatureNetDevices:
+ envFlags |= ipc.FlagEnableNetDev
+ case flatrpc.FeatureKCSAN:
+ case flatrpc.FeatureDevlinkPCI:
+ envFlags |= ipc.FlagEnableDevlinkPCI
+ case flatrpc.FeatureNicVF:
+ envFlags |= ipc.FlagEnableNicVF
+ case flatrpc.FeatureUSBEmulation:
+ case flatrpc.FeatureVhciInjection:
+ envFlags |= ipc.FlagEnableVhciInjection
+ case flatrpc.FeatureWifiEmulation:
+ envFlags |= ipc.FlagEnableWifi
+ case flatrpc.FeatureLRWPANEmulation:
+ case flatrpc.FeatureBinFmtMisc:
+ case flatrpc.FeatureSwap:
+ default:
+ panic(fmt.Sprintf("unknown feature %v", flatrpc.EnumNamesFeature[feat]))
+ }
+ return envFlags, execFlags
+}
+
+// featureSucceeded checks if execution of a simple program with feature-specific flags succeed.
+// This generally checks that just all syscalls were executed and succeed,
+// for coverage features we also check that we got actual coverage.
+func (ctx *checkContext) featureSucceeded(feat flatrpc.Feature, testProg *prog.Prog,
+ res rpctype.ExecutionResult) string {
+ if res.Error != "" {
+ if len(res.Output) != 0 {
+ return string(res.Output)
+ }
+ return res.Error
+ }
+ if len(res.Info.Calls) != len(testProg.Calls) {
+ return fmt.Sprintf("only %v calls are executed out of %v",
+ len(res.Info.Calls), len(testProg.Calls))
+ }
+ for i, call := range res.Info.Calls {
+ if call.Errno != 0 {
+ return fmt.Sprintf("call %v failed with errno %v", i, call.Errno)
+ }
+ }
+ call := res.Info.Calls[0]
+ switch feat {
+ case flatrpc.FeatureCoverage:
+ if len(call.Cover) == 0 || len(call.Signal) == 0 {
+ return "got no coverage"
+ }
+ case flatrpc.FeatureComparisons:
+ if len(call.Comps) == 0 {
+ return "got no coverage"
+ }
+ }
+ return ""
+}
diff --git a/pkg/vminfo/linux_syscalls.go b/pkg/vminfo/linux_syscalls.go
index 529fed3bd..e96ce79c1 100644
--- a/pkg/vminfo/linux_syscalls.go
+++ b/pkg/vminfo/linux_syscalls.go
@@ -62,7 +62,7 @@ func linuxSupportedLSM(ctx *checkContext, call *prog.Syscall) string {
}
var linuxSyscallChecks = map[string]func(*checkContext, *prog.Syscall) string{
- "openat": linuxSupportedOpenat,
+ "openat": supportedOpenat,
"mount": linuxSupportedMount,
"socket": linuxSupportedSocket,
"socketpair": linuxSupportedSocket,
@@ -185,24 +185,6 @@ func linuxSyzKvmSetupCPUSupported(ctx *checkContext, call *prog.Syscall) string
return "unsupported arch"
}
-func linuxSupportedOpenat(ctx *checkContext, call *prog.Syscall) string {
- fname, ok := extractStringConst(call.Args[1].Type)
- if !ok || fname[0] != '/' {
- return ""
- }
- modes := ctx.allOpenModes()
- // Attempt to extract flags from the syscall description.
- if mode, ok := call.Args[2].Type.(*prog.ConstType); ok {
- modes = []uint64{mode.Val}
- }
- var calls []string
- for _, mode := range modes {
- call := fmt.Sprintf("openat(0x%0x, &AUTO='%v', 0x%x, 0x0)", ctx.val("AT_FDCWD"), fname, mode)
- calls = append(calls, call)
- }
- return ctx.anyCallSucceeds(calls, fmt.Sprintf("failed to open %v", fname))
-}
-
func linuxSupportedMount(ctx *checkContext, call *prog.Syscall) string {
return linuxSupportedFilesystem(ctx, call, 2)
}
diff --git a/pkg/vminfo/linux_test.go b/pkg/vminfo/linux_test.go
index 217740f6d..e9be133c1 100644
--- a/pkg/vminfo/linux_test.go
+++ b/pkg/vminfo/linux_test.go
@@ -45,8 +45,8 @@ func TestLinuxSyscalls(t *testing.T) {
Data: []byte(strings.Join(filesystems, "\nnodev\t")),
},
}
- results := createSuccessfulResults(t, cfg.Target, checkProgs)
- enabled, disabled, err := checker.FinishCheck(files, results)
+ results, featureInfos := createSuccessfulResults(t, cfg.Target, checkProgs)
+ enabled, disabled, features, err := checker.FinishCheck(files, results, featureInfos)
if err != nil {
t.Fatal(err)
}
@@ -74,6 +74,14 @@ func TestLinuxSyscalls(t *testing.T) {
if len(enabled) != expectEnabled {
t.Errorf("enabled only %v calls out of %v", len(enabled), expectEnabled)
}
+ if len(features) != len(flatrpc.EnumNamesFeature) {
+ t.Errorf("enabled only %v features out of %v", len(features), len(flatrpc.EnumNamesFeature))
+ }
+ for feat, info := range features {
+ if !info.Enabled {
+ t.Errorf("feature %v is not enabled: %v", flatrpc.EnumNamesFeature[feat], info.Reason)
+ }
+ }
}
func TestReadKVMInfo(t *testing.T) {
diff --git a/pkg/vminfo/netbsd.go b/pkg/vminfo/netbsd.go
new file mode 100644
index 000000000..cb6c1b33a
--- /dev/null
+++ b/pkg/vminfo/netbsd.go
@@ -0,0 +1,38 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package vminfo
+
+import (
+ "github.com/google/syzkaller/pkg/cover"
+ "github.com/google/syzkaller/prog"
+)
+
+type netbsd int
+
+func (netbsd) RequiredFiles() []string {
+ return nil
+}
+
+func (netbsd) checkFiles() []string {
+ return nil
+}
+
+func (netbsd) parseModules(files filesystem) ([]cover.KernelModule, error) {
+ return nil, nil
+}
+
+func (netbsd) machineInfos() []machineInfoFunc {
+ return nil
+}
+
+func (netbsd) syscallCheck(ctx *checkContext, call *prog.Syscall) string {
+ switch call.CallName {
+ case "openat":
+ return supportedOpenat(ctx, call)
+ case "syz_usb_connect", "syz_usb_disconnect":
+ return ctx.rootCanOpen("/dev/vhci0")
+ default:
+ return ""
+ }
+}
diff --git a/pkg/vminfo/openbsd.go b/pkg/vminfo/openbsd.go
new file mode 100644
index 000000000..07ada0a98
--- /dev/null
+++ b/pkg/vminfo/openbsd.go
@@ -0,0 +1,36 @@
+// Copyright 2024 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package vminfo
+
+import (
+ "github.com/google/syzkaller/pkg/cover"
+ "github.com/google/syzkaller/prog"
+)
+
+type openbsd int
+
+func (openbsd) RequiredFiles() []string {
+ return nil
+}
+
+func (openbsd) checkFiles() []string {
+ return nil
+}
+
+func (openbsd) parseModules(files filesystem) ([]cover.KernelModule, error) {
+ return nil, nil
+}
+
+func (openbsd) machineInfos() []machineInfoFunc {
+ return nil
+}
+
+func (openbsd) syscallCheck(ctx *checkContext, call *prog.Syscall) string {
+ switch call.CallName {
+ case "openat":
+ return supportedOpenat(ctx, call)
+ default:
+ return ""
+ }
+}
diff --git a/pkg/vminfo/syscalls.go b/pkg/vminfo/syscalls.go
index 60112ddac..78cc973c8 100644
--- a/pkg/vminfo/syscalls.go
+++ b/pkg/vminfo/syscalls.go
@@ -62,12 +62,13 @@ type checkContext struct {
// Ready channel is closed after we've recevied results of execution of test
// programs and file contents. After this results maps and fs are populated.
ready chan bool
- results map[int64]*ipc.ProgInfo
+ results map[int64]rpctype.ExecutionResult
fs filesystem
// Once checking of a syscall is finished, the result is sent to syscalls.
// The main goroutine will wait for exactly pendingSyscalls messages.
syscalls chan syscallResult
pendingSyscalls int
+ features chan featureResult
}
type syscallResult struct {
@@ -86,8 +87,9 @@ func newCheckContext(cfg *mgrconfig.Config, impl checker) *checkContext {
target: cfg.Target,
sandbox: sandbox,
requests: make(chan []*rpctype.ExecutionRequest),
- results: make(map[int64]*ipc.ProgInfo),
+ results: make(map[int64]rpctype.ExecutionResult),
syscalls: make(chan syscallResult),
+ features: make(chan featureResult, 100),
ready: make(chan bool),
}
}
@@ -130,6 +132,7 @@ func (ctx *checkContext) startCheck() []rpctype.ExecutionRequest {
ctx.syscalls <- syscallResult{call, reason}
}()
}
+ ctx.checkFeatures()
var progs []rpctype.ExecutionRequest
dedup := make(map[hash.Sig]int64)
for i := 0; i < ctx.pendingRequests; i++ {
@@ -148,12 +151,11 @@ func (ctx *checkContext) startCheck() []rpctype.ExecutionRequest {
return progs
}
-func (ctx *checkContext) finishCheck(fileInfos []flatrpc.FileInfo, progs []rpctype.ExecutionResult) (
- map[*prog.Syscall]bool, map[*prog.Syscall]string, error) {
+func (ctx *checkContext) finishCheck(fileInfos []flatrpc.FileInfo, progs []rpctype.ExecutionResult,
+ featureInfos []flatrpc.FeatureInfo) (map[*prog.Syscall]bool, map[*prog.Syscall]string, Features, error) {
ctx.fs = createVirtualFilesystem(fileInfos)
- for i := range progs {
- res := &progs[i]
- ctx.results[res.ID] = &res.Info
+ for _, res := range progs {
+ ctx.results[res.ID] = res
}
close(ctx.ready)
enabled := make(map[*prog.Syscall]bool)
@@ -166,7 +168,8 @@ func (ctx *checkContext) finishCheck(fileInfos []flatrpc.FileInfo, progs []rpcty
disabled[res.call] = res.reason
}
}
- return enabled, disabled, nil
+ features, err := ctx.finishFeatures(featureInfos)
+ return enabled, disabled, features, err
}
func (ctx *checkContext) rootCanOpen(file string) string {
@@ -220,6 +223,24 @@ func (ctx *checkContext) supportedSyscalls(names []string) string {
return ""
}
+func supportedOpenat(ctx *checkContext, call *prog.Syscall) string {
+ fname, ok := extractStringConst(call.Args[1].Type)
+ if !ok || fname[0] != '/' {
+ return ""
+ }
+ modes := ctx.allOpenModes()
+ // Attempt to extract flags from the syscall description.
+ if mode, ok := call.Args[2].Type.(*prog.ConstType); ok {
+ modes = []uint64{mode.Val}
+ }
+ var calls []string
+ for _, mode := range modes {
+ call := fmt.Sprintf("openat(0x%0x, &AUTO='%v', 0x%x, 0x0)", ctx.val("AT_FDCWD"), fname, mode)
+ calls = append(calls, call)
+ }
+ return ctx.anyCallSucceeds(calls, fmt.Sprintf("failed to open %v", fname))
+}
+
func (ctx *checkContext) allOpenModes() []uint64 {
// Various open modes we need to try if we don't have a concrete mode.
// Some files can be opened only for reading, some only for writing,
@@ -307,14 +328,14 @@ func (ctx *checkContext) execRaw(calls []string, mode prog.DeserializeMode, root
<-ctx.ready
info := &ipc.ProgInfo{}
for _, req := range requests {
- res := ctx.results[req.ID]
- if res == nil {
+ res, ok := ctx.results[req.ID]
+ if !ok {
panic(fmt.Sprintf("no result for request %v", req.ID))
}
- if len(res.Calls) == 0 {
+ if len(res.Info.Calls) == 0 {
panic(fmt.Sprintf("result for request %v has no calls", req.ID))
}
- info.Calls = append(info.Calls, res.Calls...)
+ info.Calls = append(info.Calls, res.Info.Calls...)
}
if len(info.Calls) != len(calls) {
panic(fmt.Sprintf("got only %v results for program %v with %v calls:\n%s",
@@ -323,6 +344,34 @@ func (ctx *checkContext) execRaw(calls []string, mode prog.DeserializeMode, root
return info
}
+func (ctx *checkContext) execProg(p *prog.Prog, envFlags ipc.EnvFlags,
+ execFlags ipc.ExecFlags) rpctype.ExecutionResult {
+ if ctx.requests == nil {
+ panic("only one test execution per checker is supported")
+ }
+ data, err := p.SerializeForExec()
+ if err != nil {
+ panic(fmt.Sprintf("failed to serialize test program: %v\n%s", err, p.Serialize()))
+ }
+ req := &rpctype.ExecutionRequest{
+ ProgData: data,
+ ReturnOutput: true,
+ ReturnError: true,
+ ExecOpts: ipc.ExecOpts{
+ EnvFlags: envFlags,
+ ExecFlags: execFlags,
+ SandboxArg: ctx.cfg.SandboxArg,
+ },
+ }
+ ctx.requests <- []*rpctype.ExecutionRequest{req}
+ <-ctx.ready
+ res, ok := ctx.results[req.ID]
+ if !ok {
+ panic(fmt.Sprintf("no result for request %v", req.ID))
+ }
+ return res
+}
+
func (ctx *checkContext) readFile(name string) ([]byte, error) {
ctx.waitForResults()
return ctx.fs.ReadFile(name)
diff --git a/pkg/vminfo/vminfo.go b/pkg/vminfo/vminfo.go
index efc471b7d..8c5af606e 100644
--- a/pkg/vminfo/vminfo.go
+++ b/pkg/vminfo/vminfo.go
@@ -36,9 +36,13 @@ type Checker struct {
func New(cfg *mgrconfig.Config) *Checker {
var impl checker
- switch {
- case cfg.TargetOS == targets.Linux:
+ switch cfg.TargetOS {
+ case targets.Linux:
impl = new(linux)
+ case targets.NetBSD:
+ impl = new(netbsd)
+ case targets.OpenBSD:
+ impl = new(openbsd)
default:
impl = new(stub)
}
@@ -77,11 +81,11 @@ func (checker *Checker) StartCheck() ([]string, []rpctype.ExecutionRequest) {
return checker.checkFiles(), checker.checkContext.startCheck()
}
-func (checker *Checker) FinishCheck(files []flatrpc.FileInfo, progs []rpctype.ExecutionResult) (
- map[*prog.Syscall]bool, map[*prog.Syscall]string, error) {
+func (checker *Checker) FinishCheck(files []flatrpc.FileInfo, progs []rpctype.ExecutionResult,
+ featureInfos []flatrpc.FeatureInfo) (map[*prog.Syscall]bool, map[*prog.Syscall]string, Features, error) {
ctx := checker.checkContext
checker.checkContext = nil
- return ctx.finishCheck(files, progs)
+ return ctx.finishCheck(files, progs, featureInfos)
}
type machineInfoFunc func(files filesystem, w io.Writer) (string, error)
diff --git a/pkg/vminfo/vminfo_test.go b/pkg/vminfo/vminfo_test.go
index de41b48fe..2f72131a5 100644
--- a/pkg/vminfo/vminfo_test.go
+++ b/pkg/vminfo/vminfo_test.go
@@ -56,8 +56,8 @@ func TestSyscalls(t *testing.T) {
cfg := testConfig(t, target.OS, target.Arch)
checker := New(cfg)
_, checkProgs := checker.StartCheck()
- results := createSuccessfulResults(t, cfg.Target, checkProgs)
- enabled, disabled, err := checker.FinishCheck(nil, results)
+ results, featureInfos := createSuccessfulResults(t, cfg.Target, checkProgs)
+ enabled, disabled, _, err := checker.FinishCheck(nil, results, featureInfos)
if err != nil {
t.Fatal(err)
}
@@ -73,7 +73,7 @@ func TestSyscalls(t *testing.T) {
}
func createSuccessfulResults(t *testing.T, target *prog.Target,
- progs []rpctype.ExecutionRequest) []rpctype.ExecutionResult {
+ progs []rpctype.ExecutionRequest) ([]rpctype.ExecutionResult, []flatrpc.FeatureInfo) {
var results []rpctype.ExecutionResult
for _, req := range progs {
p, err := target.DeserializeExec(req.ProgData, nil)
@@ -82,13 +82,25 @@ func createSuccessfulResults(t *testing.T, target *prog.Target,
}
res := rpctype.ExecutionResult{
ID: req.ID,
- Info: ipc.ProgInfo{
- Calls: make([]ipc.CallInfo, len(p.Calls)),
- },
+ }
+ for range p.Calls {
+ res.Info.Calls = append(res.Info.Calls, ipc.CallInfo{
+ Cover: []uint32{1},
+ Signal: []uint32{1},
+ Comps: map[uint64]map[uint64]bool{
+ 1: {2: true},
+ },
+ })
}
results = append(results, res)
}
- return results
+ var features []flatrpc.FeatureInfo
+ for feat := range flatrpc.EnumNamesFeature {
+ features = append(features, flatrpc.FeatureInfo{
+ Id: feat,
+ })
+ }
+ return results, features
}
func hostChecker(t *testing.T) (*Checker, []flatrpc.FileInfo) {
diff --git a/syz-fuzzer/fuzzer.go b/syz-fuzzer/fuzzer.go
index bdccf9d82..6870583df 100644
--- a/syz-fuzzer/fuzzer.go
+++ b/syz-fuzzer/fuzzer.go
@@ -16,7 +16,7 @@ import (
"sync/atomic"
"time"
- "github.com/google/syzkaller/pkg/csource"
+ "github.com/google/syzkaller/pkg/flatrpc"
"github.com/google/syzkaller/pkg/host"
"github.com/google/syzkaller/pkg/ipc"
"github.com/google/syzkaller/pkg/ipc/ipcconfig"
@@ -38,6 +38,7 @@ type FuzzerTool struct {
// TODO: repair triagedCandidates logic, it's broken now.
triagedCandidates uint32
timeouts targets.Timeouts
+ leakFrames []string
noExecRequests atomic.Uint64
noExecDuration atomic.Uint64
@@ -93,7 +94,6 @@ func main() {
log.SyzFatalf("failed to create default ipc config: %v", err)
}
timeouts := config.Timeouts
- sandbox := ipc.FlagsToSandbox(execOpts.EnvFlags)
executor := config.Executor
shutdown := make(chan struct{})
osutil.HandleInterrupts(shutdown)
@@ -108,15 +108,15 @@ func main() {
setupPprofHandler(*flagPprofPort)
}
- checkArgs := &checkArgs{
- target: target,
- sandbox: sandbox,
- ipcConfig: config,
- ipcExecOpts: execOpts,
- gitRevision: prog.GitRevision,
- targetRevision: target.Revision,
- }
if *flagTest {
+ checkArgs := &checkArgs{
+ target: target,
+ sandbox: ipc.FlagsToSandbox(execOpts.EnvFlags),
+ ipcConfig: config,
+ ipcExecOpts: execOpts,
+ gitRevision: prog.GitRevision,
+ targetRevision: target.Revision,
+ }
testImage(*flagManager, checkArgs)
return
}
@@ -141,53 +141,16 @@ func main() {
if err := manager.Call("Manager.Connect", a, r); err != nil {
log.SyzFatalf("failed to call Manager.Connect(): %v ", err)
}
- featureFlags, err := csource.ParseFeaturesFlags("none", "none", true)
- if err != nil {
- log.SyzFatal(err)
- }
- var checkReq *rpctype.CheckArgs
- if r.Features == nil {
- checkArgs.featureFlags = featureFlags
- checkReq, err = checkMachine(checkArgs)
- if err != nil {
- if checkReq == nil {
- checkReq = new(rpctype.CheckArgs)
- }
- checkReq.Error = err.Error()
- }
- r.Features = checkReq.Features
- } else {
- if err = host.Setup(target, r.Features, featureFlags, executor); err != nil {
- log.SyzFatal(err)
- }
- checkReq = new(rpctype.CheckArgs)
- }
-
- for _, feat := range r.Features.Supported() {
- log.Logf(0, "%v: %v", feat.Name, feat.Reason)
+ checkReq := &rpctype.CheckArgs{
+ Name: *flagName,
+ Files: host.ReadFiles(r.ReadFiles),
+ Globs: make(map[string][]string),
}
-
- inputsCount := *flagProcs * 2
- fuzzerTool := &FuzzerTool{
- name: *flagName,
- executor: executor,
- manager: manager,
- timeouts: timeouts,
-
- requests: make(chan rpctype.ExecutionRequest, 2*inputsCount),
- results: make(chan executionResult, 2*inputsCount),
- }
- gateCb := fuzzerTool.useBugFrames(r.Features, r.MemoryLeakFrames, r.DataRaceFrames)
- fuzzerTool.gate = ipc.NewGate(gateSize, gateCb)
-
- log.Logf(0, "starting %v executor processes", *flagProcs)
- for pid := 0; pid < *flagProcs; pid++ {
- startProc(fuzzerTool, pid, config)
+ features, err := host.SetupFeatures(target, executor, r.Features, nil)
+ if err != nil {
+ log.SyzFatalf("failed to setup features: %v ", err)
}
-
- checkReq.Name = *flagName
- checkReq.Files = host.ReadFiles(r.ReadFiles)
- checkReq.Globs = make(map[string][]string)
+ checkReq.Features = features
for _, glob := range r.ReadGlobs {
files, err := filepath.Glob(filepath.FromSlash(glob))
if err != nil && checkReq.Error == "" {
@@ -208,28 +171,39 @@ func main() {
log.SyzFatalf("failed to write syz-cover-bitmap: %v", err)
}
}
- // Query enough inputs at the beginning.
- fuzzerTool.exchangeDataCall(nil, 0)
- go fuzzerTool.exchangeDataWorker()
- fuzzerTool.exchangeDataWorker()
-}
-// Returns gateCallback for leak checking if enabled.
-func (tool *FuzzerTool) useBugFrames(featues *host.Features, leakFrames, raceFrames []string) func() {
- var gateCallback func()
+ inputsCount := *flagProcs * 2
+ fuzzerTool := &FuzzerTool{
+ name: *flagName,
+ executor: executor,
+ manager: manager,
+ timeouts: timeouts,
+ leakFrames: r.MemoryLeakFrames,
- if featues[host.FeatureLeak].Enabled {
- gateCallback = func() { tool.gateCallback(leakFrames) }
+ requests: make(chan rpctype.ExecutionRequest, 2*inputsCount),
+ results: make(chan executionResult, 2*inputsCount),
}
+ fuzzerTool.filterDataRaceFrames(r.DataRaceFrames)
+ var gateCallback func()
+ for _, feat := range features {
+ if feat.Id == flatrpc.FeatureLeak && feat.Reason == "" {
+ gateCallback = fuzzerTool.leakGateCallback
+ }
+ }
+ fuzzerTool.gate = ipc.NewGate(gateSize, gateCallback)
- if featues[host.FeatureKCSAN].Enabled && len(raceFrames) != 0 {
- tool.filterDataRaceFrames(raceFrames)
+ log.Logf(0, "starting %v executor processes", *flagProcs)
+ for pid := 0; pid < *flagProcs; pid++ {
+ startProc(fuzzerTool, pid, config)
}
- return gateCallback
+ // Query enough inputs at the beginning.
+ fuzzerTool.exchangeDataCall(nil, 0)
+ go fuzzerTool.exchangeDataWorker()
+ fuzzerTool.exchangeDataWorker()
}
-func (tool *FuzzerTool) gateCallback(leakFrames []string) {
+func (tool *FuzzerTool) leakGateCallback() {
// Leak checking is very slow so we don't do it while triaging the corpus
// (otherwise it takes infinity). When we have presumably triaged the corpus
// (triagedCandidates == 1), we run leak checking bug ignore the result
@@ -239,7 +213,7 @@ func (tool *FuzzerTool) gateCallback(leakFrames []string) {
if triagedCandidates == 0 {
return
}
- args := append([]string{"leak"}, leakFrames...)
+ args := append([]string{"leak"}, tool.leakFrames...)
timeout := tool.timeouts.NoOutput * 9 / 10
output, err := osutil.RunCmd(timeout, "", tool.executor, args...)
if err != nil && triagedCandidates == 2 {
@@ -255,6 +229,9 @@ func (tool *FuzzerTool) gateCallback(leakFrames []string) {
}
func (tool *FuzzerTool) filterDataRaceFrames(frames []string) {
+ if len(frames) == 0 {
+ return
+ }
args := append([]string{"setup_kcsan_filterlist"}, frames...)
timeout := time.Minute * tool.timeouts.Scale
output, err := osutil.RunCmd(timeout, "", tool.executor, args...)
diff --git a/syz-fuzzer/proc.go b/syz-fuzzer/proc.go
index c80de0518..6978b2591 100644
--- a/syz-fuzzer/proc.go
+++ b/syz-fuzzer/proc.go
@@ -121,7 +121,10 @@ func (proc *Proc) executeProgram(req rpctype.ExecutionRequest) (*ipc.ProgInfo, [
proc.tool.startExecutingCall(req.ID, proc.pid, try)
output, info, hanged, err = proc.env.ExecProg(&req.ExecOpts, req.ProgData)
proc.tool.gate.Leave(ticket)
- log.Logf(2, "result hanged=%v err=%v: %s", hanged, err, output)
+ // Don't print output if returning error b/c it may contain SYZFAIL.
+ if !req.ReturnError {
+ log.Logf(2, "result hanged=%v err=%v: %s", hanged, err, output)
+ }
if hanged && err == nil && req.ReturnError {
err = errors.New("hanged")
}
diff --git a/syz-fuzzer/testing.go b/syz-fuzzer/testing.go
index ce21aaa11..5f5b6dc98 100644
--- a/syz-fuzzer/testing.go
+++ b/syz-fuzzer/testing.go
@@ -10,12 +10,9 @@ import (
"strings"
"time"
- "github.com/google/syzkaller/pkg/csource"
- "github.com/google/syzkaller/pkg/host"
"github.com/google/syzkaller/pkg/ipc"
"github.com/google/syzkaller/pkg/log"
"github.com/google/syzkaller/pkg/osutil"
- "github.com/google/syzkaller/pkg/rpctype"
"github.com/google/syzkaller/prog"
)
@@ -26,7 +23,6 @@ type checkArgs struct {
targetRevision string
ipcConfig *ipc.Config
ipcExecOpts *ipc.ExecOpts
- featureFlags map[string]csource.Feature
}
func testImage(hostAddr string, args *checkArgs) {
@@ -43,62 +39,9 @@ func testImage(hostAddr string, args *checkArgs) {
if err := checkRevisions(args); err != nil {
log.SyzFatal(err)
}
- if _, err := checkMachine(args); err != nil {
+ if err := checkSimpleProgram(args); err != nil {
log.SyzFatal(err)
}
- if err := buildCallList(args.target, args.sandbox); err != nil {
- log.SyzFatal(err)
- }
-}
-
-func checkMachineHeartbeats(done chan bool) {
- ticker := time.NewTicker(3 * time.Second)
- defer ticker.Stop()
- for {
- select {
- case <-done:
- return
- case <-ticker.C:
- fmt.Printf("executing program\n")
- }
- }
-}
-
-func checkMachine(args *checkArgs) (*rpctype.CheckArgs, error) {
- log.Logf(0, "checking machine...")
- // Machine checking can be very slow on some machines (qemu without kvm, KMEMLEAK linux, etc),
- // so print periodic heartbeats for vm.MonitorExecution so that it does not decide that we are dead.
- done := make(chan bool)
- defer close(done)
- go checkMachineHeartbeats(done)
- features, err := host.Check(args.target)
- if err != nil {
- return nil, err
- }
- if feat := features[host.FeatureCoverage]; !feat.Enabled &&
- args.ipcExecOpts.EnvFlags&ipc.FlagSignal != 0 {
- return nil, fmt.Errorf("coverage is not supported (%v)", feat.Reason)
- }
- if feat := features[host.FeatureSandboxSetuid]; !feat.Enabled &&
- args.ipcExecOpts.EnvFlags&ipc.FlagSandboxSetuid != 0 {
- return nil, fmt.Errorf("sandbox=setuid is not supported (%v)", feat.Reason)
- }
- if feat := features[host.FeatureSandboxNamespace]; !feat.Enabled &&
- args.ipcExecOpts.EnvFlags&ipc.FlagSandboxNamespace != 0 {
- return nil, fmt.Errorf("sandbox=namespace is not supported (%v)", feat.Reason)
- }
- if feat := features[host.FeatureSandboxAndroid]; !feat.Enabled &&
- args.ipcExecOpts.EnvFlags&ipc.FlagSandboxAndroid != 0 {
- return nil, fmt.Errorf("sandbox=android is not supported (%v)", feat.Reason)
- }
- args.ipcExecOpts.EnvFlags |= ipc.FeaturesToFlags(features.ToFlatRPC(), nil)
- if err := checkSimpleProgram(args, features); err != nil {
- return nil, err
- }
- res := &rpctype.CheckArgs{
- Features: features,
- }
- return res, nil
}
func checkRevisions(args *checkArgs) error {
@@ -149,11 +92,8 @@ func executorVersion(bin string) (string, string, string, error) {
return vers[1], vers[2], vers[3], nil
}
-func checkSimpleProgram(args *checkArgs, features *host.Features) error {
+func checkSimpleProgram(args *checkArgs) error {
log.Logf(0, "testing simple program...")
- if err := host.Setup(args.target, features, args.featureFlags, args.ipcConfig.Executor); err != nil {
- return fmt.Errorf("host setup failed: %w", err)
- }
env, err := ipc.MakeEnv(args.ipcConfig, 0)
if err != nil {
return fmt.Errorf("failed to create ipc env: %w", err)
@@ -178,35 +118,3 @@ func checkSimpleProgram(args *checkArgs, features *host.Features) error {
}
return nil
}
-
-func buildCallList(target *prog.Target, sandbox string) error {
- log.Logf(0, "building call list...")
- calls := make(map[*prog.Syscall]bool)
- for _, c := range target.Syscalls {
- calls[c] = true
- }
-
- _, unsupported, err := host.DetectSupportedSyscalls(target, sandbox, calls)
- if err != nil {
- return fmt.Errorf("failed to detect host supported syscalls: %w", err)
- }
- for c := range calls {
- if reason, ok := unsupported[c]; ok {
- // Note: if we print call name followed by ':', it may be detected
- // as a kernel crash if the call ends with "BUG" or "INFO".
- log.Logf(1, "unsupported syscall: %v(): %v", c.Name, reason)
- delete(calls, c)
- }
- }
- _, unsupported = target.TransitivelyEnabledCalls(calls)
- for c := range calls {
- if reason, ok := unsupported[c]; ok {
- log.Logf(1, "transitively unsupported: %v(): %v", c.Name, reason)
- delete(calls, c)
- }
- }
- if len(calls) == 0 {
- return fmt.Errorf("all system calls are disabled")
- }
- return nil
-}
diff --git a/syz-manager/rpc.go b/syz-manager/rpc.go
index ef4b81361..5a9074b15 100644
--- a/syz-manager/rpc.go
+++ b/syz-manager/rpc.go
@@ -16,7 +16,6 @@ import (
"github.com/google/syzkaller/pkg/cover"
"github.com/google/syzkaller/pkg/flatrpc"
"github.com/google/syzkaller/pkg/fuzzer"
- "github.com/google/syzkaller/pkg/host"
"github.com/google/syzkaller/pkg/ipc"
"github.com/google/syzkaller/pkg/log"
"github.com/google/syzkaller/pkg/mgrconfig"
@@ -35,15 +34,17 @@ type RPCServer struct {
checker *vminfo.Checker
port int
+ infoDone bool
checkDone atomic.Bool
checkFiles []string
checkFilesInfo []flatrpc.FileInfo
+ checkFeatureInfo []flatrpc.FeatureInfo
checkProgs []rpctype.ExecutionRequest
checkResults []rpctype.ExecutionResult
needCheckResults int
checkFailures int
- checkFeatures *host.Features
enabledFeatures flatrpc.Feature
+ setupFeatures flatrpc.Feature
modules []cover.KernelModule
canonicalModules *cover.Canonicalizer
execCoverFilter map[uint32]uint32
@@ -154,11 +155,13 @@ func (serv *RPCServer) Connect(a *rpctype.ConnectArgs, r *rpctype.ConnectRes) er
serv.mu.Lock()
defer serv.mu.Unlock()
- r.Features = serv.checkFeatures
r.ReadFiles = serv.checker.RequiredFiles()
- if serv.checkFeatures == nil {
+ if serv.checkDone.Load() {
+ r.Features = serv.setupFeatures
+ } else {
r.ReadFiles = append(r.ReadFiles, serv.checkFiles...)
r.ReadGlobs = serv.target.RequiredGlobs()
+ r.Features = flatrpc.AllFeatures
}
return nil
}
@@ -206,9 +209,9 @@ func (serv *RPCServer) Check(a *rpctype.CheckArgs, r *rpctype.CheckRes) error {
return fmt.Errorf("machine check failed: %v", a.Error)
}
- if serv.checkFeatures == nil {
- serv.checkFeatures = a.Features
- serv.enabledFeatures = a.Features.ToFlatRPC()
+ if !serv.infoDone {
+ serv.infoDone = true
+ serv.checkFeatureInfo = a.Features
serv.checkFilesInfo = a.Files
serv.modules = modules
serv.target.UpdateGlobs(a.Globs)
@@ -241,10 +244,8 @@ func (serv *RPCServer) Check(a *rpctype.CheckArgs, r *rpctype.CheckRes) error {
func (serv *RPCServer) finishCheck() error {
// Note: need to print disbled syscalls before failing due to an error.
// This helps to debug "all system calls are disabled".
- enabledCalls, disabledCalls, err := serv.checker.FinishCheck(serv.checkFilesInfo, serv.checkResults)
- if err != nil {
- return fmt.Errorf("failed to detect enabled syscalls: %w", err)
- }
+ enabledCalls, disabledCalls, features, checkErr := serv.checker.FinishCheck(
+ serv.checkFilesInfo, serv.checkResults, serv.checkFeatureInfo)
enabledCalls, transitivelyDisabled := serv.target.TransitivelyEnabledCalls(enabledCalls)
buf := new(bytes.Buffer)
if len(serv.cfg.EnabledSyscalls) != 0 || log.V(1) {
@@ -281,15 +282,25 @@ func (serv *RPCServer) finishCheck() error {
if hasFileErrors {
fmt.Fprintf(buf, "\n")
}
- fmt.Fprintf(buf, "%-24v: %v/%v\n", "syscalls", len(enabledCalls), len(serv.cfg.Target.Syscalls))
- for _, feat := range serv.checkFeatures.Supported() {
- fmt.Fprintf(buf, "%-24v: %v\n", feat.Name, feat.Reason)
+ var lines []string
+ lines = append(lines, fmt.Sprintf("%-24v: %v/%v\n", "syscalls",
+ len(enabledCalls), len(serv.cfg.Target.Syscalls)))
+ for feat, info := range features {
+ lines = append(lines, fmt.Sprintf("%-24v: %v\n",
+ flatrpc.EnumNamesFeature[feat], info.Reason))
}
+ sort.Strings(lines)
+ buf.WriteString(strings.Join(lines, ""))
fmt.Fprintf(buf, "\n")
log.Logf(0, "machine check:\n%s", buf.Bytes())
+ if checkErr != nil {
+ return checkErr
+ }
if len(enabledCalls) == 0 {
- log.Fatalf("all system calls are disabled")
+ return fmt.Errorf("all system calls are disabled")
}
+ serv.enabledFeatures = features.Enabled()
+ serv.setupFeatures = features.NeedSetup()
serv.mgr.machineChecked(serv.enabledFeatures, enabledCalls)
return nil
}
@@ -344,6 +355,7 @@ func (serv *RPCServer) ExchangeInfo(a *rpctype.ExchangeInfoRequest, r *rpctype.E
serv.checkResults = nil
serv.checkFiles = nil
serv.checkFilesInfo = nil
+ serv.checkFeatureInfo = nil
serv.checkDone.Store(true)
}
}
diff --git a/syz-runner/runner.go b/syz-runner/runner.go
index 329b340a3..8c9f3a5c3 100644
--- a/syz-runner/runner.go
+++ b/syz-runner/runner.go
@@ -4,11 +4,9 @@ package main
import (
"flag"
- "fmt"
"log"
"runtime"
- "github.com/google/syzkaller/pkg/host"
"github.com/google/syzkaller/pkg/ipc"
"github.com/google/syzkaller/pkg/ipc/ipcconfig"
"github.com/google/syzkaller/pkg/rpctype"
@@ -74,19 +72,7 @@ func main() {
enabled[c] = true
}
if r.CheckUnsupportedCalls {
- sandbox := ipc.FlagsToSandbox(opts.EnvFlags)
- _, unsupported, err := host.DetectSupportedSyscalls(target, sandbox, enabled)
- if err != nil {
- log.Fatalf("failed to get unsupported system calls: %v", err)
- }
-
- calls := make([]rpctype.SyscallReason, 0)
- for c, reason := range unsupported {
- calls = append(calls, rpctype.SyscallReason{
- ID: c.ID,
- Reason: fmt.Sprintf("%s (not supported on kernel %d)", reason, rn.pool)})
- }
- a := &rpctype.UpdateUnsupportedArgs{Pool: rn.pool, UnsupportedCalls: calls}
+ a := &rpctype.UpdateUnsupportedArgs{Pool: rn.pool, UnsupportedCalls: nil}
if err := vrf.Call("Verifier.UpdateUnsupported", a, nil); err != nil {
log.Fatalf("failed to send unsupported system calls: %v", err)
}
diff --git a/tools/syz-execprog/execprog.go b/tools/syz-execprog/execprog.go
index 7f328e31a..518e7511f 100644
--- a/tools/syz-execprog/execprog.go
+++ b/tools/syz-execprog/execprog.go
@@ -21,13 +21,16 @@ import (
"github.com/google/syzkaller/pkg/cover/backend"
"github.com/google/syzkaller/pkg/csource"
"github.com/google/syzkaller/pkg/db"
+ "github.com/google/syzkaller/pkg/flatrpc"
"github.com/google/syzkaller/pkg/host"
"github.com/google/syzkaller/pkg/ipc"
"github.com/google/syzkaller/pkg/ipc/ipcconfig"
"github.com/google/syzkaller/pkg/log"
"github.com/google/syzkaller/pkg/mgrconfig"
"github.com/google/syzkaller/pkg/osutil"
+ "github.com/google/syzkaller/pkg/rpctype"
"github.com/google/syzkaller/pkg/tool"
+ "github.com/google/syzkaller/pkg/vminfo"
"github.com/google/syzkaller/prog"
_ "github.com/google/syzkaller/sys"
"github.com/google/syzkaller/sys/targets"
@@ -91,24 +94,23 @@ func main() {
flag.Usage()
os.Exit(1)
}
- features, err := host.Check(target)
- if err != nil {
- log.Fatalf("%v", err)
- }
- if *flagOutput {
- for _, feat := range features.Supported() {
- log.Logf(0, "%-24v: %v", feat.Name, feat.Reason)
- }
- }
if *flagCollide {
log.Logf(0, "note: setting -collide to true is deprecated now and has no effect")
}
- config, execOpts := createConfig(target, features, featuresFlags)
- if err = host.Setup(target, features, featuresFlags, config.Executor); err != nil {
- log.Fatal(err)
+ var requestedSyscalls []int
+ if *flagStress {
+ syscallList := strings.Split(*flagSyscalls, ",")
+ if *flagSyscalls == "" {
+ syscallList = nil
+ }
+ requestedSyscalls, err = mgrconfig.ParseEnabledSyscalls(target, syscallList, nil)
+ if err != nil {
+ log.Fatalf("failed to parse enabled syscalls: %v", err)
+ }
}
+ config, execOpts, syscalls, features := createConfig(target, featuresFlags, requestedSyscalls)
var gateCallback func()
- if features[host.FeatureLeak].Enabled {
+ if features&flatrpc.FeatureLeak != 0 {
gateCallback = func() {
output, err := osutil.RunCmd(10*time.Minute, "", config.Executor, "leak")
if err != nil {
@@ -119,12 +121,7 @@ func main() {
}
var choiceTable *prog.ChoiceTable
if *flagStress {
- var syscalls []string
- if *flagSyscalls != "" {
- syscalls = strings.Split(*flagSyscalls, ",")
- }
- calls := buildCallList(target, syscalls)
- choiceTable = target.BuildChoiceTable(progs, calls)
+ choiceTable = target.BuildChoiceTable(progs, syscalls)
}
sysTarget := targets.Get(*flagOS, *flagArch)
upperBase := getKernelUpperBase(sysTarget)
@@ -398,8 +395,8 @@ func loadPrograms(target *prog.Target, files []string) []*prog.Prog {
return progs
}
-func createConfig(target *prog.Target, features *host.Features, featuresFlags csource.Features) (
- *ipc.Config, *ipc.ExecOpts) {
+func createConfig(target *prog.Target, featuresFlags csource.Features, syscalls []int) (
+ *ipc.Config, *ipc.ExecOpts, map[*prog.Syscall]bool, flatrpc.Feature) {
config, execOpts, err := ipcconfig.Default(target)
if err != nil {
log.Fatalf("%v", err)
@@ -418,29 +415,64 @@ func createConfig(target *prog.Target, features *host.Features, featuresFlags cs
}
execOpts.ExecFlags |= ipc.FlagCollectComps
}
- execOpts.EnvFlags |= ipc.FeaturesToFlags(features.ToFlatRPC(), featuresFlags)
- return config, execOpts
-}
-
-func buildCallList(target *prog.Target, enabled []string) map[*prog.Syscall]bool {
- syscallsIDs, err := mgrconfig.ParseEnabledSyscalls(target, enabled, nil)
+ cfg := &mgrconfig.Config{
+ Sandbox: ipc.FlagsToSandbox(execOpts.EnvFlags),
+ SandboxArg: execOpts.SandboxArg,
+ Derived: mgrconfig.Derived{
+ TargetOS: target.OS,
+ TargetArch: target.Arch,
+ TargetVMArch: target.Arch,
+ Target: target,
+ SysTarget: targets.Get(target.OS, target.Arch),
+ Syscalls: syscalls,
+ },
+ }
+ checker := vminfo.New(cfg)
+ files, requests := checker.StartCheck()
+ fileInfos := host.ReadFiles(files)
+ featureInfos, err := host.SetupFeatures(target, config.Executor, flatrpc.AllFeatures, featuresFlags)
if err != nil {
- log.Fatalf("failed to parse enabled syscalls: %v", err)
- }
- enabledSyscalls := make(map[*prog.Syscall]bool)
- for _, id := range syscallsIDs {
- enabledSyscalls[target.Syscalls[id]] = true
+ log.Fatal(err)
}
- calls, disabled, err := host.DetectSupportedSyscalls(target, "none", enabledSyscalls)
+ env, err := ipc.MakeEnv(config, 0)
if err != nil {
- log.Fatalf("failed to detect host supported syscalls: %v", err)
+ log.Fatalf("failed to create ipc env: %v", err)
}
- for c, reason := range disabled {
- log.Logf(0, "unsupported syscall: %v: %v", c.Name, reason)
+ defer env.Close()
+ var results []rpctype.ExecutionResult
+ for _, req := range requests {
+ output, info, hanged, err := env.ExecProg(&req.ExecOpts, req.ProgData)
+ res := rpctype.ExecutionResult{
+ ID: req.ID,
+ Output: output,
+ }
+ if info != nil {
+ res.Info = *info
+ }
+ if err != nil {
+ res.Error = err.Error()
+ }
+ if hanged && err == nil {
+ res.Error = "hanged"
+ }
+ results = append(results, res)
}
- calls, disabled = target.TransitivelyEnabledCalls(calls)
- for c, reason := range disabled {
- log.Logf(0, "transitively unsupported: %v: %v", c.Name, reason)
+ enabledSyscalls, disabledSyscalls, features, err := checker.FinishCheck(fileInfos, results, featureInfos)
+ if err != nil {
+ log.Fatal(err)
+ }
+ if *flagOutput {
+ for feat, info := range features {
+ log.Logf(0, "%-24v: %v", flatrpc.EnumNamesFeature[feat], info.Reason)
+ }
+ for c, reason := range disabledSyscalls {
+ log.Logf(0, "unsupported syscall: %v: %v", c.Name, reason)
+ }
+ enabledSyscalls, disabledSyscalls = target.TransitivelyEnabledCalls(enabledSyscalls)
+ for c, reason := range disabledSyscalls {
+ log.Logf(0, "transitively unsupported: %v: %v", c.Name, reason)
+ }
}
- return calls
+ execOpts.EnvFlags |= ipc.FeaturesToFlags(features.Enabled(), featuresFlags)
+ return config, execOpts, enabledSyscalls, features.Enabled()
}
diff --git a/tools/syz-runtest/runtest.go b/tools/syz-runtest/runtest.go
index 1203abbb5..6f71519c7 100644
--- a/tools/syz-runtest/runtest.go
+++ b/tools/syz-runtest/runtest.go
@@ -19,7 +19,6 @@ import (
"time"
"github.com/google/syzkaller/pkg/flatrpc"
- "github.com/google/syzkaller/pkg/host"
"github.com/google/syzkaller/pkg/instance"
"github.com/google/syzkaller/pkg/mgrconfig"
"github.com/google/syzkaller/pkg/osutil"
@@ -54,18 +53,17 @@ func main() {
}
osutil.MkdirAll(cfg.Workdir)
mgr := &Manager{
- cfg: cfg,
- vmPool: vmPool,
- checker: vminfo.New(cfg),
- reporter: reporter,
- debug: *flagDebug,
- requests: make(chan *runtest.RunRequest, 4*vmPool.Count()*cfg.Procs),
- checkResultC: make(chan *rpctype.CheckArgs, 1),
- checkProgsDone: make(chan bool),
- checkFeaturesReady: make(chan bool),
- vmStop: make(chan bool),
- reqMap: make(map[int64]*runtest.RunRequest),
- pending: make(map[string]map[int64]bool),
+ cfg: cfg,
+ vmPool: vmPool,
+ checker: vminfo.New(cfg),
+ reporter: reporter,
+ debug: *flagDebug,
+ requests: make(chan *runtest.RunRequest, 4*vmPool.Count()*cfg.Procs),
+ checkResultC: make(chan *rpctype.CheckArgs, 1),
+ checkProgsDone: make(chan bool),
+ vmStop: make(chan bool),
+ reqMap: make(map[int64]*runtest.RunRequest),
+ pending: make(map[string]map[int64]bool),
}
mgr.checkFiles, mgr.checkProgs = mgr.checker.StartCheck()
mgr.needCheckResults = len(mgr.checkProgs)
@@ -99,11 +97,8 @@ func main() {
}()
}
checkResult := <-mgr.checkResultC
- mgr.checkFeatures = checkResult.Features
- mgr.checkFilesInfo = checkResult.Files
- close(mgr.checkFeaturesReady)
<-mgr.checkProgsDone
- calls, _, err := mgr.checker.FinishCheck(mgr.checkFilesInfo, mgr.checkResults)
+ calls, _, features, err := mgr.checker.FinishCheck(checkResult.Files, mgr.checkResults, checkResult.Features)
if err != nil {
log.Fatalf("failed to detect enabled syscalls: %v", err)
}
@@ -113,8 +108,8 @@ func main() {
// Note: syz_emit_ethernet/syz_extract_tcp_res were manually disabled for "" ("no") sandbox,
// b/c tun is not setup without sandbox.
enabledCalls[mgr.cfg.Sandbox] = calls
- for _, feat := range checkResult.Features.Supported() {
- fmt.Printf("%-24v: %v\n", feat.Name, feat.Reason)
+ for feat, info := range features {
+ fmt.Printf("%-24v: %v\n", flatrpc.EnumNamesFeature[feat], info.Reason)
}
for sandbox, calls := range enabledCalls {
if sandbox == "" {
@@ -125,7 +120,7 @@ func main() {
ctx := &runtest.Context{
Dir: filepath.Join(cfg.Syzkaller, "sys", cfg.Target.OS, "test"),
Target: cfg.Target,
- Features: checkResult.Features.ToFlatRPC(),
+ Features: features.Enabled(),
EnabledCalls: enabledCalls,
Requests: mgr.requests,
LogFunc: func(text string) { fmt.Println(text) },
@@ -143,23 +138,20 @@ func main() {
}
type Manager struct {
- cfg *mgrconfig.Config
- vmPool *vm.Pool
- checker *vminfo.Checker
- checkFiles []string
- checkFilesInfo []flatrpc.FileInfo
- checkProgs []rpctype.ExecutionRequest
- checkResults []rpctype.ExecutionResult
- needCheckResults int
- checkProgsDone chan bool
- reporter *report.Reporter
- requests chan *runtest.RunRequest
- checkFeatures *host.Features
- checkFeaturesReady chan bool
- checkResultC chan *rpctype.CheckArgs
- vmStop chan bool
- port int
- debug bool
+ cfg *mgrconfig.Config
+ vmPool *vm.Pool
+ checker *vminfo.Checker
+ checkFiles []string
+ checkProgs []rpctype.ExecutionRequest
+ checkResults []rpctype.ExecutionResult
+ needCheckResults int
+ checkProgsDone chan bool
+ reporter *report.Reporter
+ requests chan *runtest.RunRequest
+ checkResultC chan *rpctype.CheckArgs
+ vmStop chan bool
+ port int
+ debug bool
reqMu sync.Mutex
reqSeq int64
@@ -240,12 +232,10 @@ func (mgr *Manager) finishRequests(name string, rep *report.Report) error {
}
func (mgr *Manager) Connect(a *rpctype.ConnectArgs, r *rpctype.ConnectRes) error {
- select {
- case <-mgr.checkFeaturesReady:
- r.Features = mgr.checkFeatures
- default:
- r.ReadFiles = append(mgr.checker.RequiredFiles(), mgr.checkFiles...)
- r.ReadGlobs = mgr.cfg.Target.RequiredGlobs()
+ r.ReadFiles = append(mgr.checker.RequiredFiles(), mgr.checkFiles...)
+ r.ReadGlobs = mgr.cfg.Target.RequiredGlobs()
+ for feat := range flatrpc.EnumNamesFeature {
+ r.Features |= feat
}
return nil
}