diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2018-03-05 12:07:59 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2018-03-05 12:10:27 +0100 |
| commit | 42467f5b7bf4eef20f78f796fc6eb10401784d86 (patch) | |
| tree | 6dd3caddad413b777f407abdcd1969b15cb40a84 /pkg | |
| parent | e91c118db99874bef7e2cd657505aa4bafbbb6fa (diff) | |
sys/linux: add syz_init_net_socket syscall
The new pseudo syscall allows opening sockets that can only
be created in init net namespace (BLUETOOTH, NFC, LLC).
Use it to open these sockets.
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.
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/csource/akaros_common.go | 7 | ||||
| -rw-r--r-- | pkg/csource/freebsd_common.go | 7 | ||||
| -rw-r--r-- | pkg/csource/linux_common.go | 52 | ||||
| -rw-r--r-- | pkg/csource/netbsd_common.go | 7 | ||||
| -rw-r--r-- | pkg/host/host_akaros.go | 2 | ||||
| -rw-r--r-- | pkg/host/host_freebsd.go | 2 | ||||
| -rw-r--r-- | pkg/host/host_fuchsia.go | 2 | ||||
| -rw-r--r-- | pkg/host/host_linux.go | 47 | ||||
| -rw-r--r-- | pkg/host/host_linux_test.go | 4 | ||||
| -rw-r--r-- | pkg/host/host_netbsd.go | 2 | ||||
| -rw-r--r-- | pkg/host/host_windows.go | 2 |
11 files changed, 90 insertions, 44 deletions
diff --git a/pkg/csource/akaros_common.go b/pkg/csource/akaros_common.go index 5fc46dc29..c0984ed99 100644 --- a/pkg/csource/akaros_common.go +++ b/pkg/csource/akaros_common.go @@ -109,9 +109,10 @@ const int kRetryStatus = 69; const int kErrorStatus = 68; #endif -#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ - defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ - defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(__NR_syz_kvm_setup_cpu) +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ + defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ + defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(__NR_syz_kvm_setup_cpu) || \ + defined(__NR_syz_init_net_socket) NORETURN PRINTF static void fail(const char* msg, ...) { int e = errno; diff --git a/pkg/csource/freebsd_common.go b/pkg/csource/freebsd_common.go index 8265e7013..1e6597bbc 100644 --- a/pkg/csource/freebsd_common.go +++ b/pkg/csource/freebsd_common.go @@ -100,9 +100,10 @@ const int kRetryStatus = 69; const int kErrorStatus = 68; #endif -#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ - defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ - defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(__NR_syz_kvm_setup_cpu) +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ + defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ + defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(__NR_syz_kvm_setup_cpu) || \ + defined(__NR_syz_init_net_socket) NORETURN PRINTF static void fail(const char* msg, ...) { int e = errno; diff --git a/pkg/csource/linux_common.go b/pkg/csource/linux_common.go index 5b630e4fe..ee92f9fe5 100644 --- a/pkg/csource/linux_common.go +++ b/pkg/csource/linux_common.go @@ -113,11 +113,19 @@ var commonHeaderLinux = ` #include <sys/ioctl.h> #include <sys/stat.h> #endif +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_init_net_socket) +#include <fcntl.h> +#include <sched.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#endif #if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ defined(SYZ_USE_TMP_DIR) || defined(SYZ_HANDLE_SEGV) || defined(SYZ_TUN_ENABLE) || \ defined(SYZ_SANDBOX_NAMESPACE) || defined(SYZ_SANDBOX_SETUID) || \ - defined(SYZ_SANDBOX_NONE) || defined(SYZ_FAULT_INJECTION) || defined(__NR_syz_kvm_setup_cpu) + defined(SYZ_SANDBOX_NONE) || defined(SYZ_FAULT_INJECTION) || \ + defined(__NR_syz_kvm_setup_cpu) || defined(__NR_syz_init_net_socket) __attribute__((noreturn)) static void doexit(int status) { volatile unsigned i; @@ -193,9 +201,10 @@ const int kRetryStatus = 69; const int kErrorStatus = 68; #endif -#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ - defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ - defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(__NR_syz_kvm_setup_cpu) +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ + defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ + defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(__NR_syz_kvm_setup_cpu) || \ + defined(__NR_syz_init_net_socket) NORETURN PRINTF static void fail(const char* msg, ...) { int e = errno; @@ -797,6 +806,32 @@ static uintptr_t syz_fuseblk_mount(uintptr_t a0, uintptr_t a1, uintptr_t a2, uin } #endif +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_init_net_socket) +#if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_SANDBOX_NAMESPACE) +const int kInitNetNsFd = 253; +static uintptr_t syz_init_net_socket(uintptr_t domain, uintptr_t type, uintptr_t proto) +{ + int netns = open("/proc/self/ns/net", O_RDONLY); + if (netns == -1) + return netns; + if (setns(kInitNetNsFd, 0)) + return -1; + int sock = syscall(__NR_socket, domain, type, proto); + int err = errno; + if (setns(netns, 0)) + fail("setns(netns) failed"); + close(netns); + errno = err; + return sock; +} +#else +static uintptr_t syz_init_net_socket(uintptr_t domain, uintptr_t type, uintptr_t proto) +{ + return syscall(__NR_socket, domain, type, proto); +} +#endif +#endif + #if defined(SYZ_EXECUTOR) || defined(__NR_syz_kvm_setup_cpu) #if defined(__x86_64__) @@ -1771,6 +1806,15 @@ static void sandbox_common() setpgrp(); setsid(); +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_init_net_socket) + int netns = open("/proc/self/ns/net", O_RDONLY); + if (netns == -1) + fail("open(/proc/self/ns/net) failed"); + if (dup2(netns, kInitNetNsFd) < 0) + fail("dup2(netns, kInitNetNsFd) failed"); + close(netns); +#endif + struct rlimit rlim; rlim.rlim_cur = rlim.rlim_max = 128 << 20; setrlimit(RLIMIT_AS, &rlim); diff --git a/pkg/csource/netbsd_common.go b/pkg/csource/netbsd_common.go index 9d3885d46..3714f29da 100644 --- a/pkg/csource/netbsd_common.go +++ b/pkg/csource/netbsd_common.go @@ -100,9 +100,10 @@ const int kRetryStatus = 69; const int kErrorStatus = 68; #endif -#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ - defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ - defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(__NR_syz_kvm_setup_cpu) +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) || \ + defined(SYZ_USE_TMP_DIR) || defined(SYZ_TUN_ENABLE) || defined(SYZ_SANDBOX_NAMESPACE) || \ + defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(__NR_syz_kvm_setup_cpu) || \ + defined(__NR_syz_init_net_socket) NORETURN PRINTF static void fail(const char* msg, ...) { int e = errno; diff --git a/pkg/host/host_akaros.go b/pkg/host/host_akaros.go index 6c3fbb6d3..91ee49a7d 100644 --- a/pkg/host/host_akaros.go +++ b/pkg/host/host_akaros.go @@ -10,7 +10,7 @@ import ( ) // DetectSupportedSyscalls returns list on supported syscalls on host. -func DetectSupportedSyscalls(target *prog.Target) (map[*prog.Syscall]bool, error) { +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 diff --git a/pkg/host/host_freebsd.go b/pkg/host/host_freebsd.go index d2745e376..39e8655c4 100644 --- a/pkg/host/host_freebsd.go +++ b/pkg/host/host_freebsd.go @@ -8,7 +8,7 @@ import ( ) // DetectSupportedSyscalls returns list on supported syscalls on host. -func DetectSupportedSyscalls(target *prog.Target) (map[*prog.Syscall]bool, error) { +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 diff --git a/pkg/host/host_fuchsia.go b/pkg/host/host_fuchsia.go index 37cf8b847..b145961df 100644 --- a/pkg/host/host_fuchsia.go +++ b/pkg/host/host_fuchsia.go @@ -10,7 +10,7 @@ import ( ) // DetectSupportedSyscalls returns list on supported syscalls on host. -func DetectSupportedSyscalls(target *prog.Target) (map[*prog.Syscall]bool, error) { +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 diff --git a/pkg/host/host_linux.go b/pkg/host/host_linux.go index fd3dc5bb8..57ce3e79e 100644 --- a/pkg/host/host_linux.go +++ b/pkg/host/host_linux.go @@ -17,7 +17,7 @@ import ( ) // DetectSupportedSyscalls returns list on supported syscalls on host. -func DetectSupportedSyscalls(target *prog.Target) (map[*prog.Syscall]bool, error) { +func DetectSupportedSyscalls(target *prog.Target, sandbox string) (map[*prog.Syscall]bool, error) { // There are 3 possible strategies: // 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, @@ -31,23 +31,20 @@ func DetectSupportedSyscalls(target *prog.Target) (map[*prog.Syscall]bool, error kallsyms, _ := ioutil.ReadFile("/proc/kallsyms") supported := make(map[*prog.Syscall]bool) for _, c := range target.Syscalls { - if isSupported(kallsyms, c) { + if isSupported(sandbox, kallsyms, c) { supported[c] = true } } return supported, nil } -func isSupported(kallsyms []byte, c *prog.Syscall) bool { +func isSupported(sandbox string, kallsyms []byte, c *prog.Syscall) bool { if strings.HasPrefix(c.CallName, "syz_") { - return isSupportedSyzkall(c) + return isSupportedSyzkall(sandbox, 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) } @@ -69,7 +66,7 @@ var kallsymsMap = map[string]string{ "umount2": "umount", } -func isSupportedSyzkall(c *prog.Syscall) bool { +func isSupportedSyzkall(sandbox string, c *prog.Syscall) bool { switch c.CallName { case "syz_open_dev": if _, ok := c.Args[0].(*prog.ConstType); ok { @@ -81,7 +78,7 @@ func isSupportedSyzkall(c *prog.Syscall) bool { if !ok { panic("first open arg is not a pointer to string const") } - if syscall.Getuid() != 0 { + if syscall.Getuid() != 0 || sandbox == "setuid" { return false } var check func(dev string) bool @@ -102,15 +99,21 @@ func isSupportedSyzkall(c *prog.Syscall) bool { case "syz_open_pts": return true case "syz_fuse_mount": + if syscall.Getuid() != 0 || sandbox == "setuid" { + return false + } return osutil.IsExist("/dev/fuse") case "syz_fuseblk_mount": - return osutil.IsExist("/dev/fuse") && syscall.Getuid() == 0 + if syscall.Getuid() != 0 || sandbox == "setuid" { + return false + } + return osutil.IsExist("/dev/fuse") 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 + return err == nil case "syz_kvm_setup_cpu": switch c.Name { case "syz_kvm_setup_cpu$x86": @@ -118,6 +121,15 @@ func isSupportedSyzkall(c *prog.Syscall) bool { case "syz_kvm_setup_cpu$arm64": return runtime.GOARCH == "arm64" } + 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 isSupportedSocket(c) } panic("unknown syzkall: " + c.Name) } @@ -125,7 +137,6 @@ func isSupportedSyzkall(c *prog.Syscall) bool { func isSupportedSocket(c *prog.Syscall) bool { af, ok := c.Args[0].(*prog.ConstType) if !ok { - println(c.Name) panic("socket family is not const") } fd, err := syscall.Socket(int(af.Val), 0, 0) @@ -135,18 +146,6 @@ func isSupportedSocket(c *prog.Syscall) bool { return err != syscall.ENOSYS && err != syscall.EAFNOSUPPORT } -func isSupportedOpen(c *prog.Syscall) 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 *prog.Syscall) bool { fname, ok := extractStringConst(c.Args[1]) if !ok { diff --git a/pkg/host/host_linux_test.go b/pkg/host/host_linux_test.go index e32fe09cc..9deda69bc 100644 --- a/pkg/host/host_linux_test.go +++ b/pkg/host/host_linux_test.go @@ -21,7 +21,7 @@ func TestLog(t *testing.T) { t.Fatal(err) } // Dump for manual inspection. - supp, err := DetectSupportedSyscalls(target) + supp, err := DetectSupportedSyscalls(target, "none") if err != nil { t.Skipf("skipping: %v", err) } @@ -54,7 +54,7 @@ func TestSupportedSyscalls(t *testing.T) { if err != nil { t.Fatal(err) } - supp, err := DetectSupportedSyscalls(target) + supp, err := DetectSupportedSyscalls(target, "none") if err != nil { t.Skipf("skipping: %v", err) } diff --git a/pkg/host/host_netbsd.go b/pkg/host/host_netbsd.go index d2745e376..39e8655c4 100644 --- a/pkg/host/host_netbsd.go +++ b/pkg/host/host_netbsd.go @@ -8,7 +8,7 @@ import ( ) // DetectSupportedSyscalls returns list on supported syscalls on host. -func DetectSupportedSyscalls(target *prog.Target) (map[*prog.Syscall]bool, error) { +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 diff --git a/pkg/host/host_windows.go b/pkg/host/host_windows.go index d2745e376..39e8655c4 100644 --- a/pkg/host/host_windows.go +++ b/pkg/host/host_windows.go @@ -8,7 +8,7 @@ import ( ) // DetectSupportedSyscalls returns list on supported syscalls on host. -func DetectSupportedSyscalls(target *prog.Target) (map[*prog.Syscall]bool, error) { +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 |
