aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
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
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')
-rw-r--r--pkg/host/features.go (renamed from pkg/host/host.go)52
-rw-r--r--pkg/host/features_linux.go212
-rw-r--r--pkg/host/syscalls.go61
-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