diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2019-11-14 17:09:07 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2019-11-16 09:58:54 +0100 |
| commit | 8d85129b3cff7398f5aba861f0049afffb566865 (patch) | |
| tree | 0ba0f3668ca0c0a367276ca164ec321a386614d5 /pkg | |
| parent | b5c36524a29838b0ec9345fc1a07daeebf50c833 (diff) | |
pkg/host: split files into syscalls/features
pkg/host does 2 things: detects supported syscalls and supported features.
There is enough code for each for a separate file.
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/host/features.go (renamed from pkg/host/host.go) | 52 | ||||
| -rw-r--r-- | pkg/host/features_linux.go | 212 | ||||
| -rw-r--r-- | pkg/host/syscalls.go | 61 | ||||
| -rw-r--r-- | pkg/host/syscalls_linux.go (renamed from pkg/host/host_linux.go) | 199 | ||||
| -rw-r--r-- | pkg/host/syscalls_linux_test.go (renamed from pkg/host/host_linux_test.go) | 0 |
5 files changed, 273 insertions, 251 deletions
diff --git a/pkg/host/host.go b/pkg/host/features.go index 216b9158d..8c3945a98 100644 --- a/pkg/host/host.go +++ b/pkg/host/features.go @@ -7,63 +7,11 @@ import ( "time" "github.com/google/syzkaller/pkg/csource" - "github.com/google/syzkaller/pkg/log" "github.com/google/syzkaller/pkg/osutil" "github.com/google/syzkaller/prog" "github.com/google/syzkaller/sys/targets" ) -// 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) ( - 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 targets.Get(target.OS, target.Arch).HostFuzzer { - for _, c := range target.Syscalls { - supported[c] = true - } - return supported, unsupported, nil - } - for _, c := range target.Syscalls { - ok, reason := false, "" - switch c.CallName { - case "syz_execute_func": - // syz_execute_func caused multiple problems: - // 1. First it lead to corpus exploision. The program used existing values in registers - // to pollute output area. We tried to zero registers (though, not reliably). - // 2. It lead to explosion again. The exact mechanics are unknown, here is one sample: - // syz_execute_func(&(0x7f0000000440)="f2af91930f0124eda133fa20430fbafce842f66188d0d4 - // 430fc7f314c1ab5bf9e2f9660f3a0fae5e090000ba023c1fb63ac4817d73d74ec482310d46f44 - // 9f216c863fa438036a91bdbae95aaaa420f383c02c401405c6bfd49d768d768f833fefbab6464 - // 660f38323c8f26dbc1a1fe5ff6f6df0804f4c4efa59c0f01c4288ba6452e000054c4431d5cc100") - // 3. The code can also execute syscalls (and it is know to), but it's not subject to - // target.SanitizeCall. As the result it can do things that programs are not supposed to do. - // 4. Besides linux, corpus explosion also happens on freebsd and is clearly attributable - // to syz_execute_func based on corpus contents. Mechanics are also not known. - // It also did not cause finding of any new bugs (at least not that I know of). - // Let's disable it for now until we figure out how to resolve all these problems. - ok = false - reason = "always disabled for now" - default: - 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 - const ( FeatureCoverage = iota FeatureComparisons diff --git a/pkg/host/features_linux.go b/pkg/host/features_linux.go new file mode 100644 index 000000000..780c938a1 --- /dev/null +++ b/pkg/host/features_linux.go @@ -0,0 +1,212 @@ +// 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" + "syscall" + "unsafe" + + "github.com/google/syzkaller/pkg/osutil" + "github.com/google/syzkaller/prog" + "github.com/google/syzkaller/sys/linux" +) + +func init() { + checkFeature[FeatureCoverage] = checkCoverage + checkFeature[FeatureComparisons] = checkComparisons + checkFeature[FeatureExtraCoverage] = checkExtraCoverage + checkFeature[FeatureSandboxSetuid] = unconditionallyEnabled + checkFeature[FeatureSandboxNamespace] = checkSandboxNamespace + checkFeature[FeatureSandboxAndroidUntrustedApp] = checkSandboxAndroidUntrustedApp + checkFeature[FeatureFaultInjection] = checkFaultInjection + checkFeature[FeatureLeakChecking] = checkLeakChecking + checkFeature[FeatureNetworkInjection] = checkNetworkInjection + checkFeature[FeatureNetworkDevices] = unconditionallyEnabled + checkFeature[FeatureKCSAN] = checkKCSAN + checkFeature[FeatureDevlinkPCI] = checkDevlinkPCI +} + +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 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) + } + 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) + } + }() + 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) + } + 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) + } + default: + panic("unknown feature in checkCoverageFeature") + } + defer 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) + } + }() + return "" +} + +type KcovRemoteArg struct { + TraceMode uint32 + AreaSize uint32 + NumHandles uint32 + CommonHandle uint64 + // Handles []uint64 goes here. +} + +func checkFaultInjection() 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 checkLeakChecking() 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 checkSandboxAndroidUntrustedApp() string { + if err := osutil.IsAccessible("/sys/fs/selinux/policy"); err != nil { + return err.Error() + } + return "" +} + +func checkNetworkInjection() string { + if err := osutil.IsAccessible("/dev/net/tun"); err != nil { + return err.Error() + } + return "" +} + +func checkUSBInjection() string { + if err := osutil.IsAccessible("/dev/raw-gadget"); 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" + } + return "" +} diff --git a/pkg/host/syscalls.go b/pkg/host/syscalls.go new file mode 100644 index 000000000..17ec0f25a --- /dev/null +++ b/pkg/host/syscalls.go @@ -0,0 +1,61 @@ +// 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" + "github.com/google/syzkaller/sys/targets" +) + +// 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) ( + 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 targets.Get(target.OS, target.Arch).HostFuzzer { + for _, c := range target.Syscalls { + supported[c] = true + } + return supported, unsupported, nil + } + for _, c := range target.Syscalls { + ok, reason := false, "" + switch c.CallName { + case "syz_execute_func": + // syz_execute_func caused multiple problems: + // 1. First it lead to corpus exploision. The program used existing values in registers + // to pollute output area. We tried to zero registers (though, not reliably). + // 2. It lead to explosion again. The exact mechanics are unknown, here is one sample: + // syz_execute_func(&(0x7f0000000440)="f2af91930f0124eda133fa20430fbafce842f66188d0d4 + // 430fc7f314c1ab5bf9e2f9660f3a0fae5e090000ba023c1fb63ac4817d73d74ec482310d46f44 + // 9f216c863fa438036a91bdbae95aaaa420f383c02c401405c6bfd49d768d768f833fefbab6464 + // 660f38323c8f26dbc1a1fe5ff6f6df0804f4c4efa59c0f01c4288ba6452e000054c4431d5cc100") + // 3. The code can also execute syscalls (and it is know to), but it's not subject to + // target.SanitizeCall. As the result it can do things that programs are not supposed to do. + // 4. Besides linux, corpus explosion also happens on freebsd and is clearly attributable + // to syz_execute_func based on corpus contents. Mechanics are also not known. + // It also did not cause finding of any new bugs (at least not that I know of). + // Let's disable it for now until we figure out how to resolve all these problems. + ok = false + reason = "always disabled for now" + default: + 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/host_linux.go b/pkg/host/syscalls_linux.go index d9a8c24f9..dff19fb01 100644 --- a/pkg/host/host_linux.go +++ b/pkg/host/syscalls_linux.go @@ -15,22 +15,12 @@ import ( "sync" "syscall" "time" - "unsafe" "github.com/google/syzkaller/pkg/log" "github.com/google/syzkaller/pkg/osutil" "github.com/google/syzkaller/prog" - "github.com/google/syzkaller/sys/linux" ) -type KcovRemoteArg struct { - TraceMode uint32 - AreaSize uint32 - NumHandles uint32 - CommonHandle uint64 - // Handles []uint64 goes here. -} - func isSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) { log.Logf(2, "checking support for %v", c.Name) if strings.HasPrefix(c.CallName, "syz_") { @@ -383,192 +373,3 @@ func extractStringConst(typ prog.Type) (string, bool) { } return v, true } - -func init() { - checkFeature[FeatureCoverage] = checkCoverage - checkFeature[FeatureComparisons] = checkComparisons - checkFeature[FeatureExtraCoverage] = checkExtraCoverage - checkFeature[FeatureSandboxSetuid] = unconditionallyEnabled - checkFeature[FeatureSandboxNamespace] = checkSandboxNamespace - checkFeature[FeatureSandboxAndroidUntrustedApp] = checkSandboxAndroidUntrustedApp - checkFeature[FeatureFaultInjection] = checkFaultInjection - checkFeature[FeatureLeakChecking] = checkLeakChecking - checkFeature[FeatureNetworkInjection] = checkNetworkInjection - checkFeature[FeatureNetworkDevices] = unconditionallyEnabled - checkFeature[FeatureKCSAN] = checkKCSAN - checkFeature[FeatureDevlinkPCI] = checkDevlinkPCI -} - -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 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) - } - 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) - } - }() - 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) - } - 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) - } - default: - panic("unknown feature in checkCoverageFeature") - } - defer 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) - } - }() - return "" -} - -func checkFaultInjection() 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 checkLeakChecking() 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 checkSandboxAndroidUntrustedApp() string { - if err := osutil.IsAccessible("/sys/fs/selinux/policy"); err != nil { - return err.Error() - } - return "" -} - -func checkNetworkInjection() string { - if err := osutil.IsAccessible("/dev/net/tun"); err != nil { - return err.Error() - } - return "" -} - -func checkUSBInjection() string { - if err := osutil.IsAccessible("/dev/raw-gadget"); 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" - } - return "" -} diff --git a/pkg/host/host_linux_test.go b/pkg/host/syscalls_linux_test.go index 2064a10e3..2064a10e3 100644 --- a/pkg/host/host_linux_test.go +++ b/pkg/host/syscalls_linux_test.go |
