aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrey Konovalov <andreyknvl@google.com>2016-10-04 16:12:12 +0200
committerAndrey Konovalov <andreyknvl@google.com>2016-11-29 17:39:38 +0100
commitc5707f5e575f3a4220faede18a51f7a8c7ee1377 (patch)
treed7b264b52b8ce35cd6de3db86dc742a30f980bfc
parent72e9b239f4d6b71e43acee4ca76de04e893400e8 (diff)
executor: emit ethernet traffic
-rw-r--r--csource/common.go145
-rw-r--r--csource/csource.go6
-rw-r--r--executor/common.h147
-rw-r--r--executor/executor.cc4
-rwxr-xr-xextract.sh3
-rw-r--r--host/host.go3
-rw-r--r--ipc/ipc.go4
-rw-r--r--sys/vnet.txt11
-rw-r--r--sysgen/syscallnr.go1
9 files changed, 293 insertions, 31 deletions
diff --git a/csource/common.go b/csource/common.go
index 5cd07cdfe..186678951 100644
--- a/csource/common.go
+++ b/csource/common.go
@@ -3,15 +3,33 @@ package csource
var commonHeader = `
+
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
+
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <linux/capability.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <linux/sched.h>
+#include <net/if_arp.h>
+
+#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
-#include <linux/capability.h>
-#include <linux/sched.h>
#include <pthread.h>
#include <setjmp.h>
#include <signal.h>
@@ -21,15 +39,6 @@ var commonHeader = `
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/mount.h>
-#include <sys/prctl.h>
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <sys/syscall.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/wait.h>
#include <unistd.h>
const int kFailStatus = 67;
@@ -115,6 +124,110 @@ static void install_segv_handler()
__atomic_fetch_sub(&skip_segv, 1, __ATOMIC_SEQ_CST); \
}
+#ifdef __NR_syz_emit_ethernet
+static void vsnprintf_check(char* str, size_t size, const char* format, va_list args)
+{
+ int rv;
+
+ rv = vsnprintf(str, size, format, args);
+ if (rv < 0)
+ fail("tun: snprintf failed");
+ if ((size_t)rv >= size)
+ fail("tun: string '%s...' doesn't fit into buffer", str);
+}
+
+static void snprintf_check(char* str, size_t size, const char* format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ vsnprintf_check(str, size, format, args);
+ va_end(args);
+}
+
+#define COMMAND_MAX_LEN 128
+
+static void execute_command(const char* format, ...)
+{
+ va_list args;
+ char command[COMMAND_MAX_LEN];
+
+ va_start(args, format);
+
+ vsnprintf_check(command, sizeof(command), format, args);
+ if (system(command) < 0)
+ fail("tun: command \"%s\" failed", &command[0]);
+
+ va_end(args);
+}
+
+int tunfd;
+
+#define ADDR_MAX_LEN 32
+
+#define LOCAL_MAC "aa:aa:aa:aa:aa:%02hx"
+#define REMOTE_MAC "bb:bb:bb:bb:bb:%02hx"
+
+#define LOCAL_IPV4 "192.168.%d.170"
+#define REMOTE_IPV4 "192.168.%d.187"
+
+#define LOCAL_IPV6 "fd00::%02hxaa"
+#define REMOTE_IPV6 "fd00::%02hxbb"
+
+static void initialize_tun(uint64_t pid)
+{
+ if (getuid() != 0)
+ return;
+
+ if (pid >= 0xff)
+ fail("tun: no more than 255 executors");
+ int id = pid & 0xff;
+
+ tunfd = open("/dev/net/tun", O_RDWR);
+ if (tunfd == -1)
+ fail("tun: can't open /dev/net/tun");
+
+ char iface[IFNAMSIZ];
+ snprintf_check(iface, sizeof(iface), "syz%d", id);
+
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, iface, IFNAMSIZ);
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+ if (ioctl(tunfd, TUNSETIFF, (void*)&ifr) < 0)
+ fail("tun: ioctl(TUNSETIFF) failed");
+
+ char local_mac[ADDR_MAX_LEN];
+ snprintf_check(local_mac, sizeof(local_mac), LOCAL_MAC, id);
+ char remote_mac[ADDR_MAX_LEN];
+ snprintf_check(remote_mac, sizeof(remote_mac), REMOTE_MAC, id);
+
+ char local_ipv4[ADDR_MAX_LEN];
+ snprintf_check(local_ipv4, sizeof(local_ipv4), LOCAL_IPV4, id);
+ char remote_ipv4[ADDR_MAX_LEN];
+ snprintf_check(remote_ipv4, sizeof(remote_ipv4), REMOTE_IPV4, id);
+
+ char local_ipv6[ADDR_MAX_LEN];
+ snprintf_check(local_ipv6, sizeof(local_ipv6), LOCAL_IPV6, id);
+ char remote_ipv6[ADDR_MAX_LEN];
+ snprintf_check(remote_ipv6, sizeof(remote_ipv6), REMOTE_IPV6, id);
+
+ execute_command("ip link set dev %s address %s", iface, local_mac);
+ execute_command("ip addr add %s/24 dev %s", local_ipv4, iface);
+ execute_command("ip -6 addr add %s/120 dev %s", local_ipv6, iface);
+ execute_command("ip neigh add %s lladdr %s dev %s nud permanent", remote_ipv4, remote_mac, iface);
+ execute_command("ip -6 neigh add %s lladdr %s dev %s nud permanent", remote_ipv6, remote_mac, iface);
+ execute_command("ip link set %s up", iface);
+}
+
+static uintptr_t syz_emit_ethernet(uintptr_t a0, uintptr_t a1)
+{
+ int64_t length = a0;
+ char* data = (char*)a1;
+ return write(tunfd, data, length);
+}
+#endif
+
#ifdef __NR_syz_open_dev
static uintptr_t syz_open_dev(uintptr_t a0, uintptr_t a1, uintptr_t a2)
{
@@ -231,10 +344,14 @@ static uintptr_t execute_syscall(int nr, uintptr_t a0, uintptr_t a1, uintptr_t a
case __NR_syz_fuseblk_mount:
return syz_fuseblk_mount(a0, a1, a2, a3, a4, a5, a6, a7);
#endif
+#ifdef __NR_syz_emit_ethernet
+ case __NR_syz_emit_ethernet:
+ return syz_emit_ethernet(a0, a1);
+#endif
}
}
-static void setup_main_process()
+static void setup_main_process(uint64_t pid)
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
@@ -243,6 +360,10 @@ static void setup_main_process()
syscall(SYS_rt_sigaction, 0x21, &sa, NULL, 8);
install_segv_handler();
+#ifdef __NR_syz_emit_ethernet
+ initialize_tun(pid);
+#endif
+
char tmpdir_template[] = "./syzkaller.XXXXXX";
char* tmpdir = mkdtemp(tmpdir_template);
if (!tmpdir)
diff --git a/csource/csource.go b/csource/csource.go
index 0c4d93ed3..42eca2038 100644
--- a/csource/csource.go
+++ b/csource/csource.go
@@ -59,7 +59,7 @@ func Write(p *prog.Prog, opts Options) ([]byte, error) {
generateTestFunc(w, opts, calls, "loop")
fmt.Fprint(w, "int main()\n{\n")
- fmt.Fprint(w, "\tsetup_main_process();\n")
+ fmt.Fprint(w, "\tsetup_main_process(0);\n")
fmt.Fprintf(w, "\tint pid = do_sandbox_%v();\n", opts.Sandbox)
fmt.Fprint(w, "\tint status = 0;\n")
fmt.Fprint(w, "\twhile (waitpid(pid, &status, __WALL) != pid) {}\n")
@@ -68,7 +68,7 @@ func Write(p *prog.Prog, opts Options) ([]byte, error) {
generateTestFunc(w, opts, calls, "test")
if opts.Procs <= 1 {
fmt.Fprint(w, "int main()\n{\n")
- fmt.Fprint(w, "\tsetup_main_process();\n")
+ fmt.Fprint(w, "\tsetup_main_process(0);\n")
fmt.Fprintf(w, "\tint pid = do_sandbox_%v();\n", opts.Sandbox)
fmt.Fprint(w, "\tint status = 0;\n")
fmt.Fprint(w, "\twhile (waitpid(pid, &status, __WALL) != pid) {}\n")
@@ -78,7 +78,7 @@ func Write(p *prog.Prog, opts Options) ([]byte, error) {
fmt.Fprint(w, "\tint i;")
fmt.Fprintf(w, "\tfor (i = 0; i < %v; i++) {\n", opts.Procs)
fmt.Fprint(w, "\t\tif (fork() == 0) {\n")
- fmt.Fprint(w, "\t\t\tsetup_main_process();\n")
+ fmt.Fprint(w, "\t\t\tsetup_main_process(i);\n")
fmt.Fprintf(w, "\t\t\tdo_sandbox_%v();\n", opts.Sandbox)
fmt.Fprint(w, "\t\t}\n")
fmt.Fprint(w, "\t}\n")
diff --git a/executor/common.h b/executor/common.h
index dd7a0dfe8..3e8f258d4 100644
--- a/executor/common.h
+++ b/executor/common.h
@@ -2,15 +2,33 @@
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
// This file is shared between executor and csource package.
+
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
+
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <linux/capability.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <linux/sched.h>
+#include <net/if_arp.h>
+
+#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
-#include <linux/capability.h>
-#include <linux/sched.h>
#include <pthread.h>
#include <setjmp.h>
#include <signal.h>
@@ -20,22 +38,13 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/mount.h>
-#include <sys/prctl.h>
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <sys/syscall.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/wait.h>
#include <unistd.h>
const int kFailStatus = 67;
const int kErrorStatus = 68;
const int kRetryStatus = 69;
-// logical error (e.g. invalid input program)
+// logical error (e.g. invalid input program), use as an assert() alernative
__attribute__((noreturn)) void fail(const char* msg, ...)
{
int e = errno;
@@ -117,6 +126,110 @@ static void install_segv_handler()
__atomic_fetch_sub(&skip_segv, 1, __ATOMIC_SEQ_CST); \
}
+#ifdef __NR_syz_emit_ethernet
+static void vsnprintf_check(char* str, size_t size, const char* format, va_list args)
+{
+ int rv;
+
+ rv = vsnprintf(str, size, format, args);
+ if (rv < 0)
+ fail("tun: snprintf failed");
+ if ((size_t)rv >= size)
+ fail("tun: string '%s...' doesn't fit into buffer", str);
+}
+
+static void snprintf_check(char* str, size_t size, const char* format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ vsnprintf_check(str, size, format, args);
+ va_end(args);
+}
+
+#define COMMAND_MAX_LEN 128
+
+static void execute_command(const char* format, ...)
+{
+ va_list args;
+ char command[COMMAND_MAX_LEN];
+
+ va_start(args, format);
+
+ vsnprintf_check(command, sizeof(command), format, args);
+ if (system(command) < 0)
+ fail("tun: command \"%s\" failed", &command[0]);
+
+ va_end(args);
+}
+
+int tunfd;
+
+#define ADDR_MAX_LEN 32
+
+#define LOCAL_MAC "aa:aa:aa:aa:aa:%02hx"
+#define REMOTE_MAC "bb:bb:bb:bb:bb:%02hx"
+
+#define LOCAL_IPV4 "192.168.%d.170"
+#define REMOTE_IPV4 "192.168.%d.187"
+
+#define LOCAL_IPV6 "fd00::%02hxaa"
+#define REMOTE_IPV6 "fd00::%02hxbb"
+
+static void initialize_tun(uint64_t pid)
+{
+ if (getuid() != 0)
+ return;
+
+ if (pid >= 0xff)
+ fail("tun: no more than 255 executors");
+ int id = pid & 0xff;
+
+ tunfd = open("/dev/net/tun", O_RDWR);
+ if (tunfd == -1)
+ fail("tun: can't open /dev/net/tun");
+
+ char iface[IFNAMSIZ];
+ snprintf_check(iface, sizeof(iface), "syz%d", id);
+
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, iface, IFNAMSIZ);
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+ if (ioctl(tunfd, TUNSETIFF, (void*)&ifr) < 0)
+ fail("tun: ioctl(TUNSETIFF) failed");
+
+ char local_mac[ADDR_MAX_LEN];
+ snprintf_check(local_mac, sizeof(local_mac), LOCAL_MAC, id);
+ char remote_mac[ADDR_MAX_LEN];
+ snprintf_check(remote_mac, sizeof(remote_mac), REMOTE_MAC, id);
+
+ char local_ipv4[ADDR_MAX_LEN];
+ snprintf_check(local_ipv4, sizeof(local_ipv4), LOCAL_IPV4, id);
+ char remote_ipv4[ADDR_MAX_LEN];
+ snprintf_check(remote_ipv4, sizeof(remote_ipv4), REMOTE_IPV4, id);
+
+ char local_ipv6[ADDR_MAX_LEN];
+ snprintf_check(local_ipv6, sizeof(local_ipv6), LOCAL_IPV6, id);
+ char remote_ipv6[ADDR_MAX_LEN];
+ snprintf_check(remote_ipv6, sizeof(remote_ipv6), REMOTE_IPV6, id);
+
+ execute_command("ip link set dev %s address %s", iface, local_mac);
+ execute_command("ip addr add %s/24 dev %s", local_ipv4, iface);
+ execute_command("ip -6 addr add %s/120 dev %s", local_ipv6, iface);
+ execute_command("ip neigh add %s lladdr %s dev %s nud permanent", remote_ipv4, remote_mac, iface);
+ execute_command("ip -6 neigh add %s lladdr %s dev %s nud permanent", remote_ipv6, remote_mac, iface);
+ execute_command("ip link set %s up", iface);
+}
+
+static uintptr_t syz_emit_ethernet(uintptr_t a0, uintptr_t a1)
+{
+ int64_t length = a0;
+ char* data = (char*)a1;
+ return write(tunfd, data, length);
+}
+#endif // __NR_syz_emit_ethernet
+
#ifdef __NR_syz_open_dev
static uintptr_t syz_open_dev(uintptr_t a0, uintptr_t a1, uintptr_t a2)
{
@@ -241,10 +354,14 @@ static uintptr_t execute_syscall(int nr, uintptr_t a0, uintptr_t a1, uintptr_t a
case __NR_syz_fuseblk_mount:
return syz_fuseblk_mount(a0, a1, a2, a3, a4, a5, a6, a7);
#endif
+#ifdef __NR_syz_emit_ethernet
+ case __NR_syz_emit_ethernet:
+ return syz_emit_ethernet(a0, a1);
+#endif
}
}
-static void setup_main_process()
+static void setup_main_process(uint64_t pid)
{
// Don't need that SIGCANCEL/SIGSETXID glibc stuff.
// SIGCANCEL sent to main thread causes it to exit
@@ -256,6 +373,10 @@ static void setup_main_process()
syscall(SYS_rt_sigaction, 0x21, &sa, NULL, 8);
install_segv_handler();
+#ifdef __NR_syz_emit_ethernet
+ initialize_tun(pid);
+#endif
+
char tmpdir_template[] = "./syzkaller.XXXXXX";
char* tmpdir = mkdtemp(tmpdir_template);
if (!tmpdir)
diff --git a/executor/executor.cc b/executor/executor.cc
index b155c578d..85133fa3d 100644
--- a/executor/executor.cc
+++ b/executor/executor.cc
@@ -161,9 +161,10 @@ int main(int argc, char** argv)
flag_sandbox = sandbox_namespace;
if (!flag_threaded)
flag_collide = false;
+ uint64_t executor_pid = *((uint64_t*)input_data + 1);
cover_open();
- setup_main_process();
+ setup_main_process(executor_pid);
int pid = -1;
switch (flag_sandbox) {
@@ -273,6 +274,7 @@ void execute_one()
retry:
uint64_t* input_pos = (uint64_t*)&input_data[0];
read_input(&input_pos); // flags
+ read_input(&input_pos); // pid
output_pos = (uint32_t*)&output_data[0];
write_output(0); // Number of executed syscalls (updated later).
diff --git a/extract.sh b/extract.sh
index 027f0e623..ac5c23940 100755
--- a/extract.sh
+++ b/extract.sh
@@ -21,7 +21,8 @@ fi
COMMON_FILES="sys/socket.txt sys/tty.txt sys/perf.txt sys/kvm.txt \
sys/key.txt sys/bpf.txt sys/fuse.txt sys/dri.txt sys/kdbus.txt sys/sctp.txt \
sys/sndseq.txt sys/sndtimer.txt sys/sndcontrol.txt sys/input.txt \
- sys/netlink.txt sys/tun.txt sys/random.txt sys/netrom.txt"
+ sys/netlink.txt sys/tun.txt sys/random.txt sys/netrom.txt \
+ sys/vnet.txt"
UPSTREAM_FILES="sys/sys.txt sys/kcm.txt"
ANDROID_FILES=sys/tlk_device.txt
diff --git a/host/host.go b/host/host.go
index a58964e8c..ff4c0c3a8 100644
--- a/host/host.go
+++ b/host/host.go
@@ -89,6 +89,9 @@ func isSupportedSyzkall(c *sys.Call) bool {
case "syz_fuseblk_mount":
_, err := os.Stat("/dev/fuse")
return err == nil && syscall.Getuid() == 0
+ case "syz_emit_ethernet":
+ _, err := os.Stat("/dev/net/tun")
+ return err == nil && syscall.Getuid() == 0
default:
panic("unknown syzkall: " + c.Name)
}
diff --git a/ipc/ipc.go b/ipc/ipc.go
index dbb2a878d..899a8c7c3 100644
--- a/ipc/ipc.go
+++ b/ipc/ipc.go
@@ -16,6 +16,7 @@ import (
"sync/atomic"
"syscall"
"time"
+ "unsafe"
"github.com/google/syzkaller/fileutil"
"github.com/google/syzkaller/prog"
@@ -121,7 +122,8 @@ func MakeEnv(bin string, timeout time.Duration, flags uint64, pid int) (*Env, er
for i := 0; i < 8; i++ {
inmem[i] = byte(flags >> (8 * uint(i)))
}
- inmem = inmem[8:]
+ *(*uint64)(unsafe.Pointer(&inmem[8])) = uint64(pid)
+ inmem = inmem[16:]
env := &Env{
In: inmem,
Out: outmem,
diff --git a/sys/vnet.txt b/sys/vnet.txt
new file mode 100644
index 000000000..e115e701c
--- /dev/null
+++ b/sys/vnet.txt
@@ -0,0 +1,11 @@
+# Copyright 2016 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.
+
+include <linux/types.h>
+include <linux/byteorder/generic.h>
+
+eth_packet {
+ data array[int8, 0:256]
+} [packed]
+
+syz_emit_ethernet(len len[packet], packet ptr[in, eth_packet])
diff --git a/sysgen/syscallnr.go b/sysgen/syscallnr.go
index 81c5009bc..7dc1296fa 100644
--- a/sysgen/syscallnr.go
+++ b/sysgen/syscallnr.go
@@ -28,6 +28,7 @@ var syzkalls = map[string]uint64{
"syz_open_pts": 1000003,
"syz_fuse_mount": 1000004,
"syz_fuseblk_mount": 1000005,
+ "syz_emit_ethernet": 1000006,
}
func generateExecutorSyscalls(syscalls []Syscall, consts map[string]map[string]uint64) {