From 4b2a9e225c495475272e8a67f525329afa5f892a Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Sat, 17 Jun 2017 12:37:05 +0200 Subject: pkg/host: move from host --- host/host.go | 164 --------------------------------------------- host/host_test.go | 74 -------------------- pkg/host/host.go | 164 +++++++++++++++++++++++++++++++++++++++++++++ pkg/host/host_test.go | 74 ++++++++++++++++++++ syz-fuzzer/fuzzer.go | 2 +- tools/syz-stress/stress.go | 2 +- 6 files changed, 240 insertions(+), 240 deletions(-) delete mode 100644 host/host.go delete mode 100644 host/host_test.go create mode 100644 pkg/host/host.go create mode 100644 pkg/host/host_test.go diff --git a/host/host.go b/host/host.go deleted file mode 100644 index 4e634f1aa..000000000 --- a/host/host.go +++ /dev/null @@ -1,164 +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" - "io/ioutil" - "runtime" - "strconv" - "strings" - "syscall" - - "github.com/google/syzkaller/pkg/osutil" - "github.com/google/syzkaller/sys" -) - -// DetectSupportedSyscalls returns list on supported syscalls on host. -func DetectSupportedSyscalls() (map[*sys.Call]bool, error) { - // There are 3 possible strategies: - // 1. Executes all syscalls with presumably invalid arguments and check for ENOSYS. - // 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. Seems to be the most reliable. That's what we use here. - - kallsyms, _ := ioutil.ReadFile("/proc/kallsyms") - supported := make(map[*sys.Call]bool) - for _, c := range sys.Calls { - if isSupported(kallsyms, c) { - supported[c] = true - } - } - return supported, nil -} - -func isSupported(kallsyms []byte, c *sys.Call) bool { - if c.NR == -1 { - return false // don't even have a syscall number - } - if strings.HasPrefix(c.CallName, "syz_") { - return isSupportedSyzkall(c) - } - if strings.HasPrefix(c.Name, "socket$") { - return isSupportedSocket(c) - } - if strings.HasPrefix(c.Name, "open$") { - return isSupportedOpen(c) - } - if strings.HasPrefix(c.Name, "openat$") { - return isSupportedOpenAt(c) - } - if len(kallsyms) == 0 { - return true - } - return bytes.Index(kallsyms, []byte(" T sys_"+c.CallName+"\n")) != -1 -} - -func isSupportedSyzkall(c *sys.Call) bool { - switch c.CallName { - case "syz_test": - return false - case "syz_open_dev": - if _, ok := c.Args[0].(*sys.ConstType); ok { - // This is for syz_open_dev$char/block. - // They are currently commented out, but in case one enables them. - return true - } - fname, ok := extractStringConst(c.Args[0]) - if !ok { - panic("first open arg is not a pointer to string const") - } - if syscall.Getuid() != 0 { - return false - } - var check func(dev string) bool - check = func(dev string) bool { - if !strings.Contains(dev, "#") { - return osutil.IsExist(dev) - } - for i := 0; i < 10; i++ { - if check(strings.Replace(dev, "#", strconv.Itoa(i), 1)) { - return true - } - } - return false - } - return check(fname) - case "syz_open_pts": - return true - case "syz_fuse_mount": - return osutil.IsExist("/dev/fuse") - case "syz_fuseblk_mount": - return osutil.IsExist("/dev/fuse") && syscall.Getuid() == 0 - case "syz_emit_ethernet", "syz_extract_tcp_res": - fd, err := syscall.Open("/dev/net/tun", syscall.O_RDWR, 0) - if err == nil { - syscall.Close(fd) - } - return err == nil && syscall.Getuid() == 0 - case "syz_kvm_setup_cpu": - switch c.Name { - case "syz_kvm_setup_cpu$x86": - return runtime.GOARCH == "amd64" || runtime.GOARCH == "386" - case "syz_kvm_setup_cpu$arm64": - return runtime.GOARCH == "arm64" - } - } - panic("unknown syzkall: " + c.Name) -} - -func isSupportedSocket(c *sys.Call) bool { - af, ok := c.Args[0].(*sys.ConstType) - if !ok { - println(c.Name) - panic("socket family is not const") - } - fd, err := syscall.Socket(int(af.Val), 0, 0) - if fd != -1 { - syscall.Close(fd) - } - return err != syscall.ENOSYS && err != syscall.EAFNOSUPPORT -} - -func isSupportedOpen(c *sys.Call) bool { - fname, ok := extractStringConst(c.Args[0]) - if !ok { - return true - } - fd, err := syscall.Open(fname, syscall.O_RDONLY, 0) - if fd != -1 { - syscall.Close(fd) - } - return err == nil -} - -func isSupportedOpenAt(c *sys.Call) bool { - fname, ok := extractStringConst(c.Args[1]) - if !ok { - return true - } - fd, err := syscall.Open(fname, syscall.O_RDONLY, 0) - if fd != -1 { - syscall.Close(fd) - } - return err == nil -} - -func extractStringConst(typ sys.Type) (string, bool) { - ptr, ok := typ.(*sys.PtrType) - if !ok { - panic("first open arg is not a pointer to string const") - } - str, ok := ptr.Type.(*sys.BufferType) - if !ok || str.Kind != sys.BufferString || len(str.Values) != 1 { - return "", false - } - v := str.Values[0] - v = v[:len(v)-1] // string terminating \x00 - return v, true -} diff --git a/host/host_test.go b/host/host_test.go deleted file mode 100644 index 9b44d8e36..000000000 --- a/host/host_test.go +++ /dev/null @@ -1,74 +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 ( - "syscall" - "testing" - - "github.com/google/syzkaller/sys" -) - -func TestLog(t *testing.T) { - t.Parallel() - // Dump for manual inspection. - supp, err := DetectSupportedSyscalls() - if err != nil { - t.Skipf("skipping: %v", err) - } - t.Logf("unsupported:") - for _, c := range sys.Calls { - s, ok := supp[c] - if ok && !s { - t.Fatalf("map contains false value") - } - if !s { - t.Logf("\t%v", c.Name) - } - } - trans := sys.TransitivelyEnabledCalls(supp) - t.Logf("transitively unsupported:") - for _, c := range sys.Calls { - s, ok := trans[c] - if ok && !s { - t.Fatalf("map contains false value") - } - if !s && supp[c] { - t.Logf("\t%v", c.Name) - } - } -} - -func TestSupportedSyscalls(t *testing.T) { - t.Parallel() - supp, err := DetectSupportedSyscalls() - if err != nil { - t.Skipf("skipping: %v", err) - } - // These are safe to execute with invalid arguments. - safe := []string{ - "memfd_create", - "sendfile", - "bpf$MAP_CREATE", - "open", - "openat", - "read", - "write", - "stat", - } - for _, name := range safe { - c := sys.CallMap[name] - if c == nil { - t.Fatalf("can't find syscall '%v'", name) - } - 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", name) - } - if ok := err != syscall.ENOSYS; ok != supp[c] { - t.Fatalf("syscall %v: perse=%v kallsyms=%v", name, ok, supp[c]) - } - } -} diff --git a/pkg/host/host.go b/pkg/host/host.go new file mode 100644 index 000000000..4e634f1aa --- /dev/null +++ b/pkg/host/host.go @@ -0,0 +1,164 @@ +// 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" + "io/ioutil" + "runtime" + "strconv" + "strings" + "syscall" + + "github.com/google/syzkaller/pkg/osutil" + "github.com/google/syzkaller/sys" +) + +// DetectSupportedSyscalls returns list on supported syscalls on host. +func DetectSupportedSyscalls() (map[*sys.Call]bool, error) { + // There are 3 possible strategies: + // 1. Executes all syscalls with presumably invalid arguments and check for ENOSYS. + // 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. Seems to be the most reliable. That's what we use here. + + kallsyms, _ := ioutil.ReadFile("/proc/kallsyms") + supported := make(map[*sys.Call]bool) + for _, c := range sys.Calls { + if isSupported(kallsyms, c) { + supported[c] = true + } + } + return supported, nil +} + +func isSupported(kallsyms []byte, c *sys.Call) bool { + if c.NR == -1 { + return false // don't even have a syscall number + } + if strings.HasPrefix(c.CallName, "syz_") { + return isSupportedSyzkall(c) + } + if strings.HasPrefix(c.Name, "socket$") { + return isSupportedSocket(c) + } + if strings.HasPrefix(c.Name, "open$") { + return isSupportedOpen(c) + } + if strings.HasPrefix(c.Name, "openat$") { + return isSupportedOpenAt(c) + } + if len(kallsyms) == 0 { + return true + } + return bytes.Index(kallsyms, []byte(" T sys_"+c.CallName+"\n")) != -1 +} + +func isSupportedSyzkall(c *sys.Call) bool { + switch c.CallName { + case "syz_test": + return false + case "syz_open_dev": + if _, ok := c.Args[0].(*sys.ConstType); ok { + // This is for syz_open_dev$char/block. + // They are currently commented out, but in case one enables them. + return true + } + fname, ok := extractStringConst(c.Args[0]) + if !ok { + panic("first open arg is not a pointer to string const") + } + if syscall.Getuid() != 0 { + return false + } + var check func(dev string) bool + check = func(dev string) bool { + if !strings.Contains(dev, "#") { + return osutil.IsExist(dev) + } + for i := 0; i < 10; i++ { + if check(strings.Replace(dev, "#", strconv.Itoa(i), 1)) { + return true + } + } + return false + } + return check(fname) + case "syz_open_pts": + return true + case "syz_fuse_mount": + return osutil.IsExist("/dev/fuse") + case "syz_fuseblk_mount": + return osutil.IsExist("/dev/fuse") && syscall.Getuid() == 0 + case "syz_emit_ethernet", "syz_extract_tcp_res": + fd, err := syscall.Open("/dev/net/tun", syscall.O_RDWR, 0) + if err == nil { + syscall.Close(fd) + } + return err == nil && syscall.Getuid() == 0 + case "syz_kvm_setup_cpu": + switch c.Name { + case "syz_kvm_setup_cpu$x86": + return runtime.GOARCH == "amd64" || runtime.GOARCH == "386" + case "syz_kvm_setup_cpu$arm64": + return runtime.GOARCH == "arm64" + } + } + panic("unknown syzkall: " + c.Name) +} + +func isSupportedSocket(c *sys.Call) bool { + af, ok := c.Args[0].(*sys.ConstType) + if !ok { + println(c.Name) + panic("socket family is not const") + } + fd, err := syscall.Socket(int(af.Val), 0, 0) + if fd != -1 { + syscall.Close(fd) + } + return err != syscall.ENOSYS && err != syscall.EAFNOSUPPORT +} + +func isSupportedOpen(c *sys.Call) bool { + fname, ok := extractStringConst(c.Args[0]) + if !ok { + return true + } + fd, err := syscall.Open(fname, syscall.O_RDONLY, 0) + if fd != -1 { + syscall.Close(fd) + } + return err == nil +} + +func isSupportedOpenAt(c *sys.Call) bool { + fname, ok := extractStringConst(c.Args[1]) + if !ok { + return true + } + fd, err := syscall.Open(fname, syscall.O_RDONLY, 0) + if fd != -1 { + syscall.Close(fd) + } + return err == nil +} + +func extractStringConst(typ sys.Type) (string, bool) { + ptr, ok := typ.(*sys.PtrType) + if !ok { + panic("first open arg is not a pointer to string const") + } + str, ok := ptr.Type.(*sys.BufferType) + if !ok || str.Kind != sys.BufferString || len(str.Values) != 1 { + return "", false + } + v := str.Values[0] + v = v[:len(v)-1] // string terminating \x00 + return v, true +} diff --git a/pkg/host/host_test.go b/pkg/host/host_test.go new file mode 100644 index 000000000..9b44d8e36 --- /dev/null +++ b/pkg/host/host_test.go @@ -0,0 +1,74 @@ +// 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 ( + "syscall" + "testing" + + "github.com/google/syzkaller/sys" +) + +func TestLog(t *testing.T) { + t.Parallel() + // Dump for manual inspection. + supp, err := DetectSupportedSyscalls() + if err != nil { + t.Skipf("skipping: %v", err) + } + t.Logf("unsupported:") + for _, c := range sys.Calls { + s, ok := supp[c] + if ok && !s { + t.Fatalf("map contains false value") + } + if !s { + t.Logf("\t%v", c.Name) + } + } + trans := sys.TransitivelyEnabledCalls(supp) + t.Logf("transitively unsupported:") + for _, c := range sys.Calls { + s, ok := trans[c] + if ok && !s { + t.Fatalf("map contains false value") + } + if !s && supp[c] { + t.Logf("\t%v", c.Name) + } + } +} + +func TestSupportedSyscalls(t *testing.T) { + t.Parallel() + supp, err := DetectSupportedSyscalls() + if err != nil { + t.Skipf("skipping: %v", err) + } + // These are safe to execute with invalid arguments. + safe := []string{ + "memfd_create", + "sendfile", + "bpf$MAP_CREATE", + "open", + "openat", + "read", + "write", + "stat", + } + for _, name := range safe { + c := sys.CallMap[name] + if c == nil { + t.Fatalf("can't find syscall '%v'", name) + } + 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", name) + } + if ok := err != syscall.ENOSYS; ok != supp[c] { + t.Fatalf("syscall %v: perse=%v kallsyms=%v", name, ok, supp[c]) + } + } +} diff --git a/syz-fuzzer/fuzzer.go b/syz-fuzzer/fuzzer.go index 167a6a7cb..ce7b93c0c 100644 --- a/syz-fuzzer/fuzzer.go +++ b/syz-fuzzer/fuzzer.go @@ -22,9 +22,9 @@ import ( "time" "github.com/google/syzkaller/cover" - "github.com/google/syzkaller/host" "github.com/google/syzkaller/ipc" "github.com/google/syzkaller/pkg/hash" + "github.com/google/syzkaller/pkg/host" . "github.com/google/syzkaller/pkg/log" "github.com/google/syzkaller/pkg/osutil" "github.com/google/syzkaller/prog" diff --git a/tools/syz-stress/stress.go b/tools/syz-stress/stress.go index 43bd258eb..f4b0ae565 100644 --- a/tools/syz-stress/stress.go +++ b/tools/syz-stress/stress.go @@ -13,9 +13,9 @@ import ( "sync/atomic" "time" - "github.com/google/syzkaller/host" "github.com/google/syzkaller/ipc" "github.com/google/syzkaller/pkg/db" + "github.com/google/syzkaller/pkg/host" . "github.com/google/syzkaller/pkg/log" "github.com/google/syzkaller/prog" "github.com/google/syzkaller/sys" -- cgit mrf-deployment