aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
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) {