aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2024-05-03 10:16:58 +0200
committerDmitry Vyukov <dvyukov@google.com>2024-05-15 12:55:36 +0000
commit0b3dad4606c0984ce2d81ba5dd698fa248ce91b8 (patch)
treed732c2d7c4096a3a3223529088725c1adb54e3e0 /pkg
parent94b087b1f1dce14942bc35bb35a8f58e57b1fc63 (diff)
pkg/vminfo: move feature checking to host
Feature checking procedure is split into 2 phases: 1. syz-fuzzer invokes "syz-executor setup feature" for each feature one-by-one, and checks if executor does not fail. Executor can also return a special "this feature does not need custom setup", this allows to not call setup of these features in each new VM. 2. pkg/vminfo runs a simple program with ipc.ExecOpts specific for a concrete feature, e.g. for wifi injection it will try to run a program with wifi feature enabled, if setup of the feature fails, executor should also exit with an error. For coverage features we also additionally check that we actually got coverage. Then pkg/vminfo combines results of these 2 checks into final result. syz-execprog now also uses vminfo package and mimics the same checking procedure. Update #1541
Diffstat (limited to 'pkg')
-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
25 files changed, 443 insertions, 1768 deletions
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) {