diff options
Diffstat (limited to 'pkg/host')
| -rw-r--r-- | pkg/host/features.go | 227 | ||||
| -rw-r--r-- | pkg/host/features_linux.go | 355 | ||||
| -rw-r--r-- | pkg/host/features_linux_test.go | 23 | ||||
| -rw-r--r-- | pkg/host/host_darwin.go | 16 | ||||
| -rw-r--r-- | pkg/host/host_freebsd.go | 19 | ||||
| -rw-r--r-- | pkg/host/host_fuchsia.go | 14 | ||||
| -rw-r--r-- | pkg/host/host_netbsd.go | 42 | ||||
| -rw-r--r-- | pkg/host/host_openbsd.go | 38 | ||||
| -rw-r--r-- | pkg/host/host_test.go | 70 | ||||
| -rw-r--r-- | pkg/host/host_windows.go | 12 | ||||
| -rw-r--r-- | pkg/host/syscalls.go | 45 | ||||
| -rw-r--r-- | pkg/host/syscalls_linux.go | 568 | ||||
| -rw-r--r-- | pkg/host/syscalls_linux_test.go | 307 |
13 files changed, 44 insertions, 1692 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) - } - }) - } -} |
