mirror of
https://github.com/reactos/syzkaller.git
synced 2024-11-27 13:20:34 +00:00
pkg/host: explain why syscalls are disabled
This commit is contained in:
parent
48a846e42b
commit
4daf8570eb
27
pkg/host/host.go
Normal file
27
pkg/host/host.go
Normal file
@ -0,0 +1,27 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// 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) {
|
||||
supported := make(map[*prog.Syscall]bool)
|
||||
unsupported := make(map[*prog.Syscall]string)
|
||||
for _, c := range target.Syscalls {
|
||||
ok, reason := isSupported(c, sandbox)
|
||||
if ok {
|
||||
supported[c] = true
|
||||
} else {
|
||||
if reason == "" {
|
||||
reason = "unknown"
|
||||
}
|
||||
unsupported[c] = reason
|
||||
}
|
||||
}
|
||||
return supported, unsupported, nil
|
||||
}
|
@ -9,13 +9,8 @@ import (
|
||||
"github.com/google/syzkaller/prog"
|
||||
)
|
||||
|
||||
// DetectSupportedSyscalls returns list on supported syscalls on host.
|
||||
func DetectSupportedSyscalls(target *prog.Target, sandbox string) (map[*prog.Syscall]bool, error) {
|
||||
supported := make(map[*prog.Syscall]bool)
|
||||
for _, c := range target.Syscalls {
|
||||
supported[c] = true
|
||||
}
|
||||
return supported, nil
|
||||
func isSupported(c *prog.Syscall, sandbox string) (bool, string) {
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func EnableFaultInjection() error {
|
||||
|
@ -7,13 +7,8 @@ import (
|
||||
"github.com/google/syzkaller/prog"
|
||||
)
|
||||
|
||||
// DetectSupportedSyscalls returns list on supported syscalls on host.
|
||||
func DetectSupportedSyscalls(target *prog.Target, sandbox string) (map[*prog.Syscall]bool, error) {
|
||||
supported := make(map[*prog.Syscall]bool)
|
||||
for _, c := range target.Syscalls {
|
||||
supported[c] = true
|
||||
}
|
||||
return supported, nil
|
||||
func isSupported(c *prog.Syscall, sandbox string) (bool, string) {
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func EnableFaultInjection() error {
|
||||
|
@ -9,13 +9,8 @@ import (
|
||||
"github.com/google/syzkaller/prog"
|
||||
)
|
||||
|
||||
// DetectSupportedSyscalls returns list on supported syscalls on host.
|
||||
func DetectSupportedSyscalls(target *prog.Target, sandbox string) (map[*prog.Syscall]bool, error) {
|
||||
supported := make(map[*prog.Syscall]bool)
|
||||
for _, c := range target.Syscalls {
|
||||
supported[c] = true
|
||||
}
|
||||
return supported, nil
|
||||
func isSupported(c *prog.Syscall, sandbox string) (bool, string) {
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func EnableFaultInjection() error {
|
||||
|
@ -10,15 +10,15 @@ import (
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/google/syzkaller/pkg/osutil"
|
||||
"github.com/google/syzkaller/prog"
|
||||
)
|
||||
|
||||
// DetectSupportedSyscalls returns list on supported syscalls on host.
|
||||
func DetectSupportedSyscalls(target *prog.Target, sandbox string) (map[*prog.Syscall]bool, error) {
|
||||
// There are 3 possible strategies:
|
||||
func isSupported(c *prog.Syscall, sandbox string) (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.
|
||||
@ -27,18 +27,9 @@ func DetectSupportedSyscalls(target *prog.Target, sandbox string) (map[*prog.Sys
|
||||
// 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[*prog.Syscall]bool)
|
||||
for _, c := range target.Syscalls {
|
||||
if isSupported(sandbox, kallsyms, c) {
|
||||
supported[c] = true
|
||||
}
|
||||
}
|
||||
return supported, nil
|
||||
}
|
||||
|
||||
func isSupported(sandbox string, kallsyms []byte, c *prog.Syscall) bool {
|
||||
kallsymsOnce.Do(func() {
|
||||
kallsyms, _ = ioutil.ReadFile("/proc/kallsyms")
|
||||
})
|
||||
if strings.HasPrefix(c.CallName, "syz_") {
|
||||
return isSupportedSyzkall(sandbox, c)
|
||||
}
|
||||
@ -49,42 +40,52 @@ func isSupported(sandbox string, kallsyms []byte, c *prog.Syscall) bool {
|
||||
return isSupportedOpenAt(c)
|
||||
}
|
||||
if len(kallsyms) == 0 {
|
||||
return true
|
||||
return true, ""
|
||||
}
|
||||
name := c.CallName
|
||||
if newname := kallsymsMap[name]; newname != "" {
|
||||
name = newname
|
||||
}
|
||||
return bytes.Contains(kallsyms, []byte(" T sys_"+name+"\n"))
|
||||
if !bytes.Contains(kallsyms, []byte(" T sys_"+name+"\n")) {
|
||||
return false, fmt.Sprintf("sys_%v is not present in /proc/kallsyms", name)
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
||||
// 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 kallsymsMap = map[string]string{
|
||||
"umount": "oldumount",
|
||||
"umount2": "umount",
|
||||
}
|
||||
var (
|
||||
kallsyms []byte
|
||||
kallsymsOnce sync.Once
|
||||
kallsymsMap = map[string]string{
|
||||
"umount": "oldumount",
|
||||
"umount2": "umount",
|
||||
}
|
||||
)
|
||||
|
||||
func isSupportedSyzkall(sandbox string, c *prog.Syscall) bool {
|
||||
func isSupportedSyzkall(sandbox string, c *prog.Syscall) (bool, string) {
|
||||
switch c.CallName {
|
||||
case "syz_open_dev":
|
||||
if _, ok := c.Args[0].(*prog.ConstType); ok {
|
||||
// This is for syz_open_dev$char/block.
|
||||
// They are currently commented out, but in case one enables them.
|
||||
return true
|
||||
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 || sandbox == "setuid" {
|
||||
return false
|
||||
return false, "only supported under root with sandbox=none/namespace"
|
||||
}
|
||||
var check func(dev string) bool
|
||||
check = func(dev string) bool {
|
||||
if !strings.Contains(dev, "#") {
|
||||
return osutil.IsExist(dev)
|
||||
if !osutil.IsExist(dev) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
if check(strings.Replace(dev, "#", strconv.Itoa(i), 1)) {
|
||||
@ -93,59 +94,80 @@ func isSupportedSyzkall(sandbox string, c *prog.Syscall) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
return check(fname)
|
||||
if !check(fname) {
|
||||
return false, fmt.Sprintf("file %v does not exist", fname)
|
||||
}
|
||||
return true, ""
|
||||
case "syz_open_procfs":
|
||||
return true
|
||||
return true, ""
|
||||
case "syz_open_pts":
|
||||
return true
|
||||
return true, ""
|
||||
case "syz_fuse_mount":
|
||||
if syscall.Getuid() != 0 || sandbox == "setuid" {
|
||||
return false
|
||||
return false, "only supported under root with sandbox=none/namespace"
|
||||
}
|
||||
return osutil.IsExist("/dev/fuse")
|
||||
if !osutil.IsExist("/dev/fuse") {
|
||||
return false, "/dev/fuse does not exist"
|
||||
}
|
||||
return true, ""
|
||||
case "syz_fuseblk_mount":
|
||||
if syscall.Getuid() != 0 || sandbox == "setuid" {
|
||||
return false
|
||||
return false, "only supported under root with sandbox=none/namespace"
|
||||
}
|
||||
return osutil.IsExist("/dev/fuse")
|
||||
if !osutil.IsExist("/dev/fuse") {
|
||||
return false, "/dev/fuse does not exist"
|
||||
}
|
||||
return true, ""
|
||||
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)
|
||||
if err != nil {
|
||||
return false, fmt.Sprintf("open(/dev/net/tun) failed: %v", err)
|
||||
}
|
||||
return err == nil
|
||||
syscall.Close(fd)
|
||||
return true, ""
|
||||
case "syz_kvm_setup_cpu":
|
||||
switch c.Name {
|
||||
case "syz_kvm_setup_cpu$x86":
|
||||
return runtime.GOARCH == "amd64" || runtime.GOARCH == "386"
|
||||
if runtime.GOARCH == "amd64" || runtime.GOARCH == "386" {
|
||||
return true, ""
|
||||
}
|
||||
case "syz_kvm_setup_cpu$arm64":
|
||||
return runtime.GOARCH == "arm64"
|
||||
if runtime.GOARCH == "arm64" {
|
||||
return true, ""
|
||||
}
|
||||
}
|
||||
return false, "unsupported arch"
|
||||
case "syz_init_net_socket":
|
||||
// Unfortunately this only works with sandbox none at the moment.
|
||||
// The problem is that setns of a network namespace requires CAP_SYS_ADMIN
|
||||
// in the target namespace, and we've lost all privs in the init namespace
|
||||
// during creation of a user namespace.
|
||||
if syscall.Getuid() != 0 || sandbox != "none" {
|
||||
return false
|
||||
return false, "only supported under root with sandbox=none"
|
||||
}
|
||||
return isSupportedSocket(c)
|
||||
case "syz_genetlink_get_family_id":
|
||||
fd, _ := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_GENERIC)
|
||||
fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_GENERIC)
|
||||
if fd == -1 {
|
||||
return false
|
||||
return false, fmt.Sprintf("socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC) failed: %v", err)
|
||||
}
|
||||
syscall.Close(fd)
|
||||
return true
|
||||
return true, ""
|
||||
case "syz_mount_image":
|
||||
return sandbox != "setuid"
|
||||
if syscall.Getuid() != 0 || sandbox == "setuid" {
|
||||
return false, "only supported under root with sandbox=none/namespace"
|
||||
}
|
||||
return true, ""
|
||||
case "syz_read_part_table":
|
||||
return sandbox == "none" && syscall.Getuid() == 0
|
||||
if syscall.Getuid() != 0 || sandbox != "none" {
|
||||
return false, "only supported under root with sandbox=none"
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
panic("unknown syzkall: " + c.Name)
|
||||
}
|
||||
|
||||
func isSupportedSocket(c *prog.Syscall) bool {
|
||||
func isSupportedSocket(c *prog.Syscall) (bool, string) {
|
||||
af, ok := c.Args[0].(*prog.ConstType)
|
||||
if !ok {
|
||||
panic("socket family is not const")
|
||||
@ -154,19 +176,28 @@ func isSupportedSocket(c *prog.Syscall) bool {
|
||||
if fd != -1 {
|
||||
syscall.Close(fd)
|
||||
}
|
||||
return err != syscall.ENOSYS && err != syscall.EAFNOSUPPORT
|
||||
if err == syscall.ENOSYS {
|
||||
return false, "socket syscall returns ENOSYS"
|
||||
}
|
||||
if err == syscall.EAFNOSUPPORT {
|
||||
return false, "socket family is not supported (EAFNOSUPPORT)"
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func isSupportedOpenAt(c *prog.Syscall) bool {
|
||||
func isSupportedOpenAt(c *prog.Syscall) (bool, string) {
|
||||
fname, ok := extractStringConst(c.Args[1])
|
||||
if !ok || len(fname) == 0 || fname[0] != '/' {
|
||||
return true
|
||||
return true, ""
|
||||
}
|
||||
fd, err := syscall.Open(fname, syscall.O_RDONLY, 0)
|
||||
if fd != -1 {
|
||||
syscall.Close(fd)
|
||||
}
|
||||
return err == nil
|
||||
if err != nil {
|
||||
return false, fmt.Sprintf("open(%v) failed: %v", fname, err)
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func extractStringConst(typ prog.Type) (string, bool) {
|
||||
|
@ -11,50 +11,15 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/syzkaller/prog"
|
||||
_ "github.com/google/syzkaller/sys"
|
||||
)
|
||||
|
||||
func TestLog(t *testing.T) {
|
||||
t.Parallel()
|
||||
target, err := prog.GetTarget("linux", runtime.GOARCH)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Dump for manual inspection.
|
||||
supp, err := DetectSupportedSyscalls(target, "none")
|
||||
if err != nil {
|
||||
t.Skipf("skipping: %v", err)
|
||||
}
|
||||
t.Logf("unsupported:")
|
||||
for _, c := range target.Syscalls {
|
||||
s, ok := supp[c]
|
||||
if ok && !s {
|
||||
t.Fatalf("map contains false value")
|
||||
}
|
||||
if !s {
|
||||
t.Logf("\t%v", c.Name)
|
||||
}
|
||||
}
|
||||
trans := target.TransitivelyEnabledCalls(supp)
|
||||
t.Logf("transitively unsupported:")
|
||||
for _, c := range target.Syscalls {
|
||||
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()
|
||||
target, err := prog.GetTarget("linux", runtime.GOARCH)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
supp, err := DetectSupportedSyscalls(target, "none")
|
||||
supp, _, err := DetectSupportedSyscalls(target, "none")
|
||||
if err != nil {
|
||||
t.Skipf("skipping: %v", err)
|
||||
}
|
||||
|
@ -7,13 +7,8 @@ import (
|
||||
"github.com/google/syzkaller/prog"
|
||||
)
|
||||
|
||||
// DetectSupportedSyscalls returns list on supported syscalls on host.
|
||||
func DetectSupportedSyscalls(target *prog.Target, sandbox string) (map[*prog.Syscall]bool, error) {
|
||||
supported := make(map[*prog.Syscall]bool)
|
||||
for _, c := range target.Syscalls {
|
||||
supported[c] = true
|
||||
}
|
||||
return supported, nil
|
||||
func isSupported(c *prog.Syscall, sandbox string) (bool, string) {
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func EnableFaultInjection() error {
|
||||
|
45
pkg/host/host_test.go
Normal file
45
pkg/host/host_test.go
Normal file
@ -0,0 +1,45 @@
|
||||
// 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 (
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/google/syzkaller/prog"
|
||||
_ "github.com/google/syzkaller/sys"
|
||||
)
|
||||
|
||||
func TestLog(t *testing.T) {
|
||||
t.Parallel()
|
||||
target, err := prog.GetTarget(runtime.GOOS, runtime.GOARCH)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Dump for manual inspection.
|
||||
supp, disabled, err := DetectSupportedSyscalls(target, "none")
|
||||
if err != nil {
|
||||
t.Skipf("skipping: %v", 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)
|
||||
}
|
||||
trans := target.TransitivelyEnabledCalls(supp)
|
||||
t.Logf("\n\ntransitively unsupported:")
|
||||
for _, c := range target.Syscalls {
|
||||
s, ok := trans[c]
|
||||
if ok && !s {
|
||||
t.Fatalf("map contains false value")
|
||||
}
|
||||
if !s && supp[c] {
|
||||
t.Logf("%v", c.Name)
|
||||
}
|
||||
}
|
||||
}
|
@ -7,13 +7,8 @@ import (
|
||||
"github.com/google/syzkaller/prog"
|
||||
)
|
||||
|
||||
// DetectSupportedSyscalls returns list on supported syscalls on host.
|
||||
func DetectSupportedSyscalls(target *prog.Target, sandbox string) (map[*prog.Syscall]bool, error) {
|
||||
supported := make(map[*prog.Syscall]bool)
|
||||
for _, c := range target.Syscalls {
|
||||
supported[c] = true
|
||||
}
|
||||
return supported, nil
|
||||
func isSupported(c *prog.Syscall, sandbox string) (bool, string) {
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func EnableFaultInjection() error {
|
||||
|
@ -393,12 +393,12 @@ func buildCallList(target *prog.Target, enabledCalls, sandbox string) map[*prog.
|
||||
}
|
||||
}
|
||||
|
||||
if supp, err := host.DetectSupportedSyscalls(target, sandbox); err != nil {
|
||||
if _, disabled, err := host.DetectSupportedSyscalls(target, sandbox); err != nil {
|
||||
Logf(0, "failed to detect host supported syscalls: %v", err)
|
||||
} else {
|
||||
for c := range calls {
|
||||
if !supp[c] {
|
||||
Logf(1, "disabling unsupported syscall: %v", c.Name)
|
||||
if reason, ok := disabled[c]; ok {
|
||||
Logf(1, "unsupported syscall: %v: %v", c.Name, reason)
|
||||
delete(calls, c)
|
||||
}
|
||||
}
|
||||
@ -407,7 +407,7 @@ func buildCallList(target *prog.Target, enabledCalls, sandbox string) map[*prog.
|
||||
trans := target.TransitivelyEnabledCalls(calls)
|
||||
for c := range calls {
|
||||
if !trans[c] {
|
||||
Logf(1, "disabling transitively unsupported syscall: %v", c.Name)
|
||||
Logf(1, "transitively unsupported: %v", c.Name)
|
||||
delete(calls, c)
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ func testImage(hostAddr string, target *prog.Target, sandbox string) {
|
||||
if config.Flags&ipc.FlagSandboxNamespace != 0 && !osutil.IsExist("/proc/self/ns/user") {
|
||||
Fatalf("/proc/self/ns/user is not present for namespace sandbox")
|
||||
}
|
||||
calls, err := host.DetectSupportedSyscalls(target, sandbox)
|
||||
calls, _, err := host.DetectSupportedSyscalls(target, sandbox)
|
||||
if err != nil {
|
||||
Fatalf("failed to detect supported syscalls: %v", err)
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ func buildCallList(target *prog.Target) map[*prog.Syscall]bool {
|
||||
}
|
||||
return calls
|
||||
}
|
||||
calls, err := host.DetectSupportedSyscalls(target, "none")
|
||||
calls, disabled, err := host.DetectSupportedSyscalls(target, "none")
|
||||
if err != nil {
|
||||
Logf(0, "failed to detect host supported syscalls: %v", err)
|
||||
calls = make(map[*prog.Syscall]bool)
|
||||
@ -147,10 +147,8 @@ func buildCallList(target *prog.Target) map[*prog.Syscall]bool {
|
||||
calls[c] = true
|
||||
}
|
||||
}
|
||||
for _, c := range target.Syscalls {
|
||||
if !calls[c] {
|
||||
Logf(0, "disabling unsupported syscall: %v", c.Name)
|
||||
}
|
||||
for c, reason := range disabled {
|
||||
Logf(0, "disabling unsupported syscall: %v: %v", c.Name, reason)
|
||||
}
|
||||
trans := target.TransitivelyEnabledCalls(calls)
|
||||
for c := range calls {
|
||||
|
Loading…
Reference in New Issue
Block a user