aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/host/features_linux.go
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2019-11-14 17:09:07 +0100
committerDmitry Vyukov <dvyukov@google.com>2019-11-16 09:58:54 +0100
commit8d85129b3cff7398f5aba861f0049afffb566865 (patch)
tree0ba0f3668ca0c0a367276ca164ec321a386614d5 /pkg/host/features_linux.go
parentb5c36524a29838b0ec9345fc1a07daeebf50c833 (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/host/features_linux.go')
-rw-r--r--pkg/host/features_linux.go212
1 files changed, 212 insertions, 0 deletions
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 ""
+}