diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2017-09-22 11:09:53 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2017-09-22 13:10:55 +0200 |
| commit | 913d592f973a0155647473eaa032711fe956f8a5 (patch) | |
| tree | 29b1b2083c00d199cf4d9a30917411d923b49ef4 | |
| parent | c26ea367cfa790e86800ac025638ad50f95b8287 (diff) | |
all: more assorted fuchsia support
33 files changed, 2816 insertions, 2371 deletions
diff --git a/executor/common.h b/executor/common.h index 23cc02bb1..9eb5ea2c5 100644 --- a/executor/common.h +++ b/executor/common.h @@ -3,23 +3,12 @@ // This file is shared between executor and csource package. -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - #include <stdint.h> #include <string.h> -#include <sys/syscall.h> -#include <unistd.h> #if defined(SYZ_EXECUTOR) || defined(SYZ_THREADED) || defined(SYZ_COLLIDE) #include <pthread.h> #include <stdlib.h> #endif -#if defined(SYZ_EXECUTOR) || defined(SYZ_HANDLE_SEGV) -#include <setjmp.h> -#include <signal.h> -#include <string.h> -#endif #if defined(SYZ_EXECUTOR) || defined(SYZ_USE_TMP_DIR) #include <errno.h> #include <stdarg.h> @@ -27,102 +16,42 @@ #include <stdlib.h> #include <sys/stat.h> #endif -#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) -#include <errno.h> +#if defined(SYZ_EXECUTOR) || defined(SYZ_HANDLE_SEGV) +#include <setjmp.h> #include <signal.h> -#include <stdarg.h> -#include <stdio.h> -#include <sys/prctl.h> -#include <sys/time.h> -#include <sys/wait.h> -#include <time.h> -#endif -#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT) && defined(SYZ_USE_TMP_DIR)) -#include <dirent.h> -#include <sys/mount.h> +#include <string.h> #endif -#if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_SANDBOX_NAMESPACE) +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) #include <errno.h> -#include <sched.h> #include <signal.h> #include <stdarg.h> -#include <stdbool.h> #include <stdio.h> -#include <sys/prctl.h> -#include <sys/resource.h> #include <sys/time.h> #include <sys/wait.h> -#endif -#if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_SETUID) -#include <grp.h> -#endif -#if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_NAMESPACE) -#include <fcntl.h> -#include <linux/capability.h> -#include <sys/mman.h> -#include <sys/mount.h> -#include <sys/stat.h> -#endif -#if defined(SYZ_EXECUTOR) || defined(SYZ_TUN_ENABLE) -#include <arpa/inet.h> -#include <errno.h> -#include <fcntl.h> -#include <linux/if.h> -#include <linux/if_ether.h> -#include <linux/if_tun.h> -#include <linux/ip.h> -#include <linux/tcp.h> -#include <net/if_arp.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/ioctl.h> -#include <sys/stat.h> -#endif -#if defined(SYZ_EXECUTOR) || defined(SYZ_FAULT_INJECTION) -#include <errno.h> -#include <fcntl.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stdio.h> -#include <sys/stat.h> +#include <time.h> #endif #if defined(SYZ_EXECUTOR) || defined(SYZ_DEBUG) #include <stdarg.h> #include <stdio.h> #endif -#ifdef __NR_syz_open_dev -#include <fcntl.h> -#include <stdio.h> -#include <sys/stat.h> -#endif -#if defined(__NR_syz_fuse_mount) || defined(__NR_syz_fuseblk_mount) -#include <fcntl.h> -#include <stdio.h> -#include <sys/stat.h> -#include <sys/sysmacros.h> -#endif -#ifdef __NR_syz_open_pts -#include <fcntl.h> -#include <stdio.h> -#include <sys/ioctl.h> -#include <sys/stat.h> -#endif -#ifdef __NR_syz_kvm_setup_cpu -#include <errno.h> -#include <fcntl.h> -#include <linux/kvm.h> -#include <stdarg.h> -#include <stddef.h> -#include <stdio.h> -#include <sys/ioctl.h> -#include <sys/stat.h> + +#if defined(SYZ_EXECUTOR) +typedef long (*syscall_t)(long, long, long, long, long, long, long, long, long); + +struct call_t { + const char* name; + int sys_nr; + syscall_t call; +}; + +// Defined in generated syscalls_OS.h files. +extern call_t syscalls[]; +extern unsigned syscall_count; #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_SETUID) || \ - defined(SYZ_FAULT_INJECTION) || 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_SETUID) || defined(SYZ_FAULT_INJECTION) || defined(__NR_syz_kvm_setup_cpu) const int kFailStatus = 67; const int kRetryStatus = 69; #endif @@ -131,39 +60,9 @@ 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_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) -// One does not simply exit. -// _exit can in fact fail. -// syzkaller did manage to generate a seccomp filter that prohibits exit_group syscall. -// Previously, we get into infinite recursion via segv_handler in such case -// and corrupted output_data, which does matter in our case since it is shared -// with fuzzer process. Loop infinitely instead. Parent will kill us. -// But one does not simply loop either. Compilers are sure that _exit never returns, -// so they remove all code after _exit as dead. Call _exit via volatile indirection. -// And this does not work as well. _exit has own handling of failing exit_group -// in the form of HLT instruction, it will divert control flow from our loop. -// So call the syscall directly. -__attribute__((noreturn)) static void doexit(int status) -{ - volatile unsigned i; - syscall(__NR_exit_group, status); - for (i = 0;; i++) { - } -} -#endif - -#if defined(SYZ_EXECUTOR) -// exit/_exit do not necessary work. -#define exit use_doexit_instead -#define _exit use_doexit_instead -#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_SETUID) || \ - defined(SYZ_FAULT_INJECTION) || 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_SETUID) || defined(SYZ_FAULT_INJECTION) || defined(__NR_syz_kvm_setup_cpu) // logical error (e.g. invalid input program), use as an assert() alernative __attribute__((noreturn)) static void fail(const char* msg, ...) { @@ -265,25 +164,6 @@ static void segv_handler(int sig, siginfo_t* info, void* uctx) } } -static void install_segv_handler() -{ - struct sigaction sa; - - // Don't need that SIGCANCEL/SIGSETXID glibc stuff. - // SIGCANCEL sent to main thread causes it to exit - // without bringing down the whole group. - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = SIG_IGN; - syscall(SYS_rt_sigaction, 0x20, &sa, NULL, 8); - syscall(SYS_rt_sigaction, 0x21, &sa, NULL, 8); - - memset(&sa, 0, sizeof(sa)); - sa.sa_sigaction = segv_handler; - sa.sa_flags = SA_NODEFER | SA_SIGINFO; - sigaction(SIGSEGV, &sa, NULL); - sigaction(SIGBUS, &sa, NULL); -} - #define NONFAILING(...) \ { \ __atomic_fetch_add(&skip_segv, 1, __ATOMIC_SEQ_CST); \ @@ -294,162 +174,6 @@ static void install_segv_handler() } #endif -#if defined(SYZ_EXECUTOR) || defined(SYZ_USE_TMP_DIR) -static void use_temporary_dir() -{ - char tmpdir_template[] = "./syzkaller.XXXXXX"; - char* tmpdir = mkdtemp(tmpdir_template); - if (!tmpdir) - fail("failed to mkdtemp"); - if (chmod(tmpdir, 0777)) - fail("failed to chmod"); - if (chdir(tmpdir)) - fail("failed to chdir"); -} -#endif - -#if defined(SYZ_EXECUTOR) || defined(SYZ_TUN_ENABLE) -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]; - int rv; - - va_start(args, format); - - vsnprintf_check(command, sizeof(command), format, args); - rv = system(command); - if (rv != 0) - fail("tun: command \"%s\" failed with code %d", &command[0], rv); - - va_end(args); -} - -static int tunfd = -1; - -// We just need this to be large enough to hold headers that we parse (ethernet/ip/tcp). -// Rest of the packet (if any) will be silently truncated which is fine. -#define SYZ_TUN_MAX_PACKET_SIZE 1000 - -// sysgen knowns about this constant (maxPids) -#define MAX_PIDS 32 -#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 "172.20.%d.170" -#define REMOTE_IPV4 "172.20.%d.187" - -#define LOCAL_IPV6 "fe80::%02hxaa" -#define REMOTE_IPV6 "fe80::%02hxbb" - -static void initialize_tun(uint64_t pid) -{ - if (pid >= MAX_PIDS) - fail("tun: no more than %d executors", MAX_PIDS); - int id = pid; - - tunfd = open("/dev/net/tun", O_RDWR | O_NONBLOCK); - 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); - - // Disable IPv6 DAD, otherwise the address remains unusable until DAD completes. - execute_command("sysctl -w net.ipv6.conf.%s.accept_dad=0", iface); - - // Disable IPv6 router solicitation to prevent IPv6 spam. - execute_command("sysctl -w net.ipv6.conf.%s.router_solicitations=0", iface); - // There seems to be no way to disable IPv6 MTD to prevent more IPv6 spam. - - 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 dev %s up", iface); -} - -static void setup_tun(uint64_t pid, bool enable_tun) -{ - if (enable_tun) - initialize_tun(pid); -} -#endif - -#if defined(SYZ_EXECUTOR) || (defined(SYZ_TUN_ENABLE) && (defined(__NR_syz_extract_tcp_res) || defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT))) -static int read_tun(char* data, int size) -{ - int rv = read(tunfd, data, size); - if (rv < 0) { - if (errno == EAGAIN) - return -1; - fail("tun: read failed with %d, errno: %d", rv, errno); - } - return rv; -} -#endif - -#if defined(SYZ_EXECUTOR) || (defined(SYZ_DEBUG) && defined(SYZ_TUN_ENABLE) && (defined(__NR_syz_emit_ethernet) || defined(__NR_syz_extract_tcp_res))) -static void debug_dump_data(const char* data, int length) -{ - int i; - for (i = 0; i < length; i++) { - debug("%02hx ", (uint8_t)data[i] & (uint8_t)0xff); - if (i % 16 == 15) - debug("\n"); - } - if (i % 16 != 0) - debug("\n"); -} -#endif - #if defined(SYZ_EXECUTOR) || defined(SYZ_USE_CHECKSUMS) struct csum_inet { uint32_t acc; @@ -482,523 +206,6 @@ static uint16_t csum_inet_digest(struct csum_inet* csum) } #endif -#if defined(SYZ_EXECUTOR) || (defined(__NR_syz_emit_ethernet) && defined(SYZ_TUN_ENABLE)) -static uintptr_t syz_emit_ethernet(uintptr_t a0, uintptr_t a1) -{ - // syz_emit_ethernet(len len[packet], packet ptr[in, eth_packet]) - - if (tunfd < 0) - return (uintptr_t)-1; - - int64_t length = a0; - char* data = (char*)a1; - debug_dump_data(data, length); - return write(tunfd, data, length); -} -#endif - -#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT) && defined(SYZ_TUN_ENABLE)) -static void flush_tun() -{ - char data[SYZ_TUN_MAX_PACKET_SIZE]; - while (read_tun(&data[0], sizeof(data)) != -1) - ; -} -#endif - -#if defined(SYZ_EXECUTOR) || (defined(__NR_syz_extract_tcp_res) && defined(SYZ_TUN_ENABLE)) -#ifndef __ANDROID__ -// Can't include <linux/ipv6.h>, since it causes -// conflicts due to some structs redefinition. -struct ipv6hdr { - __u8 priority : 4, - version : 4; - __u8 flow_lbl[3]; - - __be16 payload_len; - __u8 nexthdr; - __u8 hop_limit; - - struct in6_addr saddr; - struct in6_addr daddr; -}; -#endif - -struct tcp_resources { - int32_t seq; - int32_t ack; -}; - -static uintptr_t syz_extract_tcp_res(uintptr_t a0, uintptr_t a1, uintptr_t a2) -{ - // syz_extract_tcp_res(res ptr[out, tcp_resources], seq_inc int32, ack_inc int32) - - if (tunfd < 0) - return (uintptr_t)-1; - - char data[SYZ_TUN_MAX_PACKET_SIZE]; - int rv = read_tun(&data[0], sizeof(data)); - if (rv == -1) - return (uintptr_t)-1; - size_t length = rv; - debug_dump_data(data, length); - - struct tcphdr* tcphdr; - - if (length < sizeof(struct ethhdr)) - return (uintptr_t)-1; - struct ethhdr* ethhdr = (struct ethhdr*)&data[0]; - - if (ethhdr->h_proto == htons(ETH_P_IP)) { - if (length < sizeof(struct ethhdr) + sizeof(struct iphdr)) - return (uintptr_t)-1; - struct iphdr* iphdr = (struct iphdr*)&data[sizeof(struct ethhdr)]; - if (iphdr->protocol != IPPROTO_TCP) - return (uintptr_t)-1; - if (length < sizeof(struct ethhdr) + iphdr->ihl * 4 + sizeof(struct tcphdr)) - return (uintptr_t)-1; - tcphdr = (struct tcphdr*)&data[sizeof(struct ethhdr) + iphdr->ihl * 4]; - } else { - if (length < sizeof(struct ethhdr) + sizeof(struct ipv6hdr)) - return (uintptr_t)-1; - struct ipv6hdr* ipv6hdr = (struct ipv6hdr*)&data[sizeof(struct ethhdr)]; - // TODO: parse and skip extension headers. - if (ipv6hdr->nexthdr != IPPROTO_TCP) - return (uintptr_t)-1; - if (length < sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + sizeof(struct tcphdr)) - return (uintptr_t)-1; - tcphdr = (struct tcphdr*)&data[sizeof(struct ethhdr) + sizeof(struct ipv6hdr)]; - } - - struct tcp_resources* res = (struct tcp_resources*)a0; - NONFAILING(res->seq = htonl((ntohl(tcphdr->seq) + (uint32_t)a1))); - NONFAILING(res->ack = htonl((ntohl(tcphdr->ack_seq) + (uint32_t)a2))); - - debug("extracted seq: %08x\n", res->seq); - debug("extracted ack: %08x\n", res->ack); - - return 0; -} -#endif - -#ifdef __NR_syz_open_dev -static uintptr_t syz_open_dev(uintptr_t a0, uintptr_t a1, uintptr_t a2) -{ - if (a0 == 0xc || a0 == 0xb) { - // syz_open_dev$char(dev const[0xc], major intptr, minor intptr) fd - // syz_open_dev$block(dev const[0xb], major intptr, minor intptr) fd - char buf[128]; - sprintf(buf, "/dev/%s/%d:%d", a0 == 0xc ? "char" : "block", (uint8_t)a1, (uint8_t)a2); - return open(buf, O_RDWR, 0); - } else { - // syz_open_dev(dev strconst, id intptr, flags flags[open_flags]) fd - char buf[1024]; - char* hash; - NONFAILING(strncpy(buf, (char*)a0, sizeof(buf))); - buf[sizeof(buf) - 1] = 0; - while ((hash = strchr(buf, '#'))) { - *hash = '0' + (char)(a1 % 10); // 10 devices should be enough for everyone. - a1 /= 10; - } - return open(buf, a2, 0); - } -} -#endif - -#ifdef __NR_syz_open_pts -static uintptr_t syz_open_pts(uintptr_t a0, uintptr_t a1) -{ - // syz_openpts(fd fd[tty], flags flags[open_flags]) fd[tty] - int ptyno = 0; - if (ioctl(a0, TIOCGPTN, &ptyno)) - return -1; - char buf[128]; - sprintf(buf, "/dev/pts/%d", ptyno); - return open(buf, a1, 0); -} -#endif - -#ifdef __NR_syz_fuse_mount -static uintptr_t syz_fuse_mount(uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5) -{ - // syz_fuse_mount(target filename, mode flags[fuse_mode], uid uid, gid gid, maxread intptr, flags flags[mount_flags]) fd[fuse] - uint64_t target = a0; - uint64_t mode = a1; - uint64_t uid = a2; - uint64_t gid = a3; - uint64_t maxread = a4; - uint64_t flags = a5; - - int fd = open("/dev/fuse", O_RDWR); - if (fd == -1) - return fd; - char buf[1024]; - sprintf(buf, "fd=%d,user_id=%ld,group_id=%ld,rootmode=0%o", fd, (long)uid, (long)gid, (unsigned)mode & ~3u); - if (maxread != 0) - sprintf(buf + strlen(buf), ",max_read=%ld", (long)maxread); - if (mode & 1) - strcat(buf, ",default_permissions"); - if (mode & 2) - strcat(buf, ",allow_other"); - syscall(SYS_mount, "", target, "fuse", flags, buf); - // Ignore errors, maybe fuzzer can do something useful with fd alone. - return fd; -} -#endif - -#ifdef __NR_syz_fuseblk_mount -static uintptr_t syz_fuseblk_mount(uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7) -{ - // syz_fuseblk_mount(target filename, blkdev filename, mode flags[fuse_mode], uid uid, gid gid, maxread intptr, blksize intptr, flags flags[mount_flags]) fd[fuse] - uint64_t target = a0; - uint64_t blkdev = a1; - uint64_t mode = a2; - uint64_t uid = a3; - uint64_t gid = a4; - uint64_t maxread = a5; - uint64_t blksize = a6; - uint64_t flags = a7; - - int fd = open("/dev/fuse", O_RDWR); - if (fd == -1) - return fd; - if (syscall(SYS_mknodat, AT_FDCWD, blkdev, S_IFBLK, makedev(7, 199))) - return fd; - char buf[256]; - sprintf(buf, "fd=%d,user_id=%ld,group_id=%ld,rootmode=0%o", fd, (long)uid, (long)gid, (unsigned)mode & ~3u); - if (maxread != 0) - sprintf(buf + strlen(buf), ",max_read=%ld", (long)maxread); - if (blksize != 0) - sprintf(buf + strlen(buf), ",blksize=%ld", (long)blksize); - if (mode & 1) - strcat(buf, ",default_permissions"); - if (mode & 2) - strcat(buf, ",allow_other"); - syscall(SYS_mount, blkdev, target, "fuseblk", flags, buf); - // Ignore errors, maybe fuzzer can do something useful with fd alone. - return fd; -} -#endif - -#ifdef __NR_syz_kvm_setup_cpu -#if defined(__x86_64__) -#include "common_kvm_amd64.h" -#elif defined(__aarch64__) -#include "common_kvm_arm64.h" -#else -static uintptr_t syz_kvm_setup_cpu(uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7) -{ - return 0; -} -#endif -#endif // #ifdef __NR_syz_kvm_setup_cpu - -#ifdef SYZ_EXECUTOR -static uintptr_t execute_syscall(int nr, uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7, uintptr_t a8) -{ - switch (nr) { - default: - return syscall(nr, a0, a1, a2, a3, a4, a5); -#ifdef __NR_syz_test - case __NR_syz_test: - return 0; -#endif -#ifdef __NR_syz_open_dev - case __NR_syz_open_dev: - return syz_open_dev(a0, a1, a2); -#endif -#ifdef __NR_syz_open_pts - case __NR_syz_open_pts: - return syz_open_pts(a0, a1); -#endif -#ifdef __NR_syz_fuse_mount - case __NR_syz_fuse_mount: - return syz_fuse_mount(a0, a1, a2, a3, a4, a5); -#endif -#ifdef __NR_syz_fuseblk_mount - 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 -#ifdef __NR_syz_extract_tcp_res - case __NR_syz_extract_tcp_res: - return syz_extract_tcp_res(a0, a1, a2); -#endif -#ifdef __NR_syz_kvm_setup_cpu - case __NR_syz_kvm_setup_cpu: - return syz_kvm_setup_cpu(a0, a1, a2, a3, a4, a5, a6, a7); -#endif - } -} -#endif - -#if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_SANDBOX_NAMESPACE) -static void loop(); - -static void sandbox_common() -{ - prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); - setpgrp(); - setsid(); - - struct rlimit rlim; - rlim.rlim_cur = rlim.rlim_max = 128 << 20; - setrlimit(RLIMIT_AS, &rlim); - rlim.rlim_cur = rlim.rlim_max = 8 << 20; - setrlimit(RLIMIT_MEMLOCK, &rlim); - rlim.rlim_cur = rlim.rlim_max = 1 << 20; - setrlimit(RLIMIT_FSIZE, &rlim); - rlim.rlim_cur = rlim.rlim_max = 1 << 20; - setrlimit(RLIMIT_STACK, &rlim); - rlim.rlim_cur = rlim.rlim_max = 0; - setrlimit(RLIMIT_CORE, &rlim); - - // CLONE_NEWIPC/CLONE_IO cause EINVAL on some systems, so we do them separately of clone. - unshare(CLONE_NEWNS); - unshare(CLONE_NEWIPC); - unshare(CLONE_IO); -} -#endif - -#if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_NONE) -static int do_sandbox_none(int executor_pid, bool enable_tun) -{ - int pid = fork(); - if (pid) - return pid; - - sandbox_common(); -#if defined(SYZ_EXECUTOR) || defined(SYZ_TUN_ENABLE) - setup_tun(executor_pid, enable_tun); -#endif - - loop(); - doexit(1); -} -#endif - -#if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_SETUID) -static int do_sandbox_setuid(int executor_pid, bool enable_tun) -{ - int pid = fork(); - if (pid) - return pid; - - sandbox_common(); -#if defined(SYZ_EXECUTOR) || defined(SYZ_TUN_ENABLE) - setup_tun(executor_pid, enable_tun); -#endif - - const int nobody = 65534; - if (setgroups(0, NULL)) - fail("failed to setgroups"); - if (syscall(SYS_setresgid, nobody, nobody, nobody)) - fail("failed to setresgid"); - if (syscall(SYS_setresuid, nobody, nobody, nobody)) - fail("failed to setresuid"); - - // This is required to open /proc/self/* files. - // Otherwise they are owned by root and we can't open them after setuid. - // See task_dump_owner function in kernel. - prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); - - loop(); - doexit(1); -} -#endif - -#if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_NAMESPACE) || defined(SYZ_FAULT_INJECTION) -static bool write_file(const char* file, const char* what, ...) -{ - char buf[1024]; - va_list args; - va_start(args, what); - vsnprintf(buf, sizeof(buf), what, args); - va_end(args); - buf[sizeof(buf) - 1] = 0; - int len = strlen(buf); - - int fd = open(file, O_WRONLY | O_CLOEXEC); - if (fd == -1) - return false; - if (write(fd, buf, len) != len) { - close(fd); - return false; - } - close(fd); - return true; -} -#endif - -#if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_NAMESPACE) -static int real_uid; -static int real_gid; -static int epid; -static bool etun; -__attribute__((aligned(64 << 10))) static char sandbox_stack[1 << 20]; - -static int namespace_sandbox_proc(void* arg) -{ - sandbox_common(); - - // /proc/self/setgroups is not present on some systems, ignore error. - write_file("/proc/self/setgroups", "deny"); - if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)) - fail("write of /proc/self/uid_map failed"); - if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) - fail("write of /proc/self/gid_map failed"); - -#if defined(SYZ_EXECUTOR) || defined(SYZ_TUN_ENABLE) - // For sandbox namespace we setup tun after initializing uid mapping, - // otherwise ip commands fail. - setup_tun(epid, etun); -#endif - - if (mkdir("./syz-tmp", 0777)) - fail("mkdir(syz-tmp) failed"); - if (mount("", "./syz-tmp", "tmpfs", 0, NULL)) - fail("mount(tmpfs) failed"); - if (mkdir("./syz-tmp/newroot", 0777)) - fail("mkdir failed"); - if (mkdir("./syz-tmp/newroot/dev", 0700)) - fail("mkdir failed"); - if (mount("/dev", "./syz-tmp/newroot/dev", NULL, MS_BIND | MS_REC | MS_PRIVATE, NULL)) - fail("mount(dev) failed"); - if (mkdir("./syz-tmp/newroot/proc", 0700)) - fail("mkdir failed"); - if (mount(NULL, "./syz-tmp/newroot/proc", "proc", 0, NULL)) - fail("mount(proc) failed"); - if (mkdir("./syz-tmp/pivot", 0777)) - fail("mkdir failed"); - if (syscall(SYS_pivot_root, "./syz-tmp", "./syz-tmp/pivot")) { - debug("pivot_root failed"); - if (chdir("./syz-tmp")) - fail("chdir failed"); - } else { - if (chdir("/")) - fail("chdir failed"); - if (umount2("./pivot", MNT_DETACH)) - fail("umount failed"); - } - if (chroot("./newroot")) - fail("chroot failed"); - if (chdir("/")) - fail("chdir failed"); - - // Drop CAP_SYS_PTRACE so that test processes can't attach to parent processes. - // Previously it lead to hangs because the loop process stopped due to SIGSTOP. - // Note that a process can always ptrace its direct children, which is enough - // for testing purposes. - struct __user_cap_header_struct cap_hdr = {}; - struct __user_cap_data_struct cap_data[2] = {}; - cap_hdr.version = _LINUX_CAPABILITY_VERSION_3; - cap_hdr.pid = getpid(); - if (syscall(SYS_capget, &cap_hdr, &cap_data)) - fail("capget failed"); - cap_data[0].effective &= ~(1 << CAP_SYS_PTRACE); - cap_data[0].permitted &= ~(1 << CAP_SYS_PTRACE); - cap_data[0].inheritable &= ~(1 << CAP_SYS_PTRACE); - if (syscall(SYS_capset, &cap_hdr, &cap_data)) - fail("capset failed"); - - loop(); - doexit(1); -} - -static int do_sandbox_namespace(int executor_pid, bool enable_tun) -{ - real_uid = getuid(); - real_gid = getgid(); - epid = executor_pid; - etun = enable_tun; - mprotect(sandbox_stack, 4096, PROT_NONE); // to catch stack underflows - return clone(namespace_sandbox_proc, &sandbox_stack[sizeof(sandbox_stack) - 64], - CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWNET, NULL); -} -#endif - -#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT) && defined(SYZ_USE_TMP_DIR)) -// One does not simply remove a directory. -// There can be mounts, so we need to try to umount. -// Moreover, a mount can be mounted several times, so we need to try to umount in a loop. -// Moreover, after umount a dir can become non-empty again, so we need another loop. -// Moreover, a mount can be re-mounted as read-only and then we will fail to make a dir empty. -static void remove_dir(const char* dir) -{ - DIR* dp; - struct dirent* ep; - int iter = 0; -retry: - dp = opendir(dir); - if (dp == NULL) { - if (errno == EMFILE) { - // This happens when the test process casts prlimit(NOFILE) on us. - // Ideally we somehow prevent test processes from messing with parent processes. - // But full sandboxing is expensive, so let's ignore this error for now. - exitf("opendir(%s) failed due to NOFILE, exiting"); - } - exitf("opendir(%s) failed", dir); - } - while ((ep = readdir(dp))) { - if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0) - continue; - char filename[FILENAME_MAX]; - snprintf(filename, sizeof(filename), "%s/%s", dir, ep->d_name); - struct stat st; - if (lstat(filename, &st)) - exitf("lstat(%s) failed", filename); - if (S_ISDIR(st.st_mode)) { - remove_dir(filename); - continue; - } - int i; - for (i = 0;; i++) { - debug("unlink(%s)\n", filename); - if (unlink(filename) == 0) - break; - if (errno == EROFS) { - debug("ignoring EROFS\n"); - break; - } - if (errno != EBUSY || i > 100) - exitf("unlink(%s) failed", filename); - debug("umount(%s)\n", filename); - if (umount2(filename, MNT_DETACH)) - exitf("umount(%s) failed", filename); - } - } - closedir(dp); - int i; - for (i = 0;; i++) { - debug("rmdir(%s)\n", dir); - if (rmdir(dir) == 0) - break; - if (i < 100) { - if (errno == EROFS) { - debug("ignoring EROFS\n"); - break; - } - if (errno == EBUSY) { - debug("umount(%s)\n", dir); - if (umount2(dir, MNT_DETACH)) - exitf("umount(%s) failed", dir); - continue; - } - if (errno == ENOTEMPTY) { - if (iter < 100) { - iter++; - goto retry; - } - } - } - exitf("rmdir(%s) failed", dir); - } -} -#endif - #if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) static uint64_t current_time_ms() { @@ -1009,80 +216,3 @@ static uint64_t current_time_ms() return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000; } #endif - -#if defined(SYZ_EXECUTOR) || defined(SYZ_FAULT_INJECTION) -static int inject_fault(int nth) -{ - int fd; - char buf[128]; - - sprintf(buf, "/proc/self/task/%d/fail-nth", (int)syscall(SYS_gettid)); - fd = open(buf, O_RDWR); - if (fd == -1) - fail("failed to open /proc/self/task/tid/fail-nth"); - sprintf(buf, "%d", nth + 1); - if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) - fail("failed to write /proc/self/task/tid/fail-nth"); - return fd; -} -#endif - -#if defined(SYZ_REPEAT) -static void test(); - -#if defined(SYZ_WAIT_REPEAT) -void loop() -{ - int iter; - for (iter = 0;; iter++) { -#ifdef SYZ_USE_TMP_DIR - char cwdbuf[256]; - sprintf(cwdbuf, "./%d", iter); - if (mkdir(cwdbuf, 0777)) - fail("failed to mkdir"); -#endif - int pid = fork(); - if (pid < 0) - fail("clone failed"); - if (pid == 0) { - prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); - setpgrp(); -#ifdef SYZ_USE_TMP_DIR - if (chdir(cwdbuf)) - fail("failed to chdir"); -#endif -#ifdef SYZ_TUN_ENABLE - flush_tun(); -#endif - test(); - doexit(0); - } - int status = 0; - uint64_t start = current_time_ms(); - for (;;) { - int res = waitpid(-1, &status, __WALL | WNOHANG); - if (res == pid) - break; - usleep(1000); - if (current_time_ms() - start > 5 * 1000) { - kill(-pid, SIGKILL); - kill(pid, SIGKILL); - while (waitpid(-1, &status, __WALL) != pid) { - } - break; - } - } -#ifdef SYZ_USE_TMP_DIR - remove_dir(cwdbuf); -#endif - } -} -#else -void loop() -{ - while (1) { - test(); - } -} -#endif -#endif diff --git a/executor/common_fuchsia.h b/executor/common_fuchsia.h new file mode 100644 index 000000000..189e7bc3c --- /dev/null +++ b/executor/common_fuchsia.h @@ -0,0 +1,51 @@ +// Copyright 2017 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. + +// This file is shared between executor and csource package. + +#include <zircon/process.h> +#include <zircon/syscalls.h> + +#define doexit exit + +#include "common.h" + +#if defined(SYZ_EXECUTOR) || defined(SYZ_HANDLE_SEGV) +static void install_segv_handler() +{ + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = segv_handler; + sa.sa_flags = SA_NODEFER | SA_SIGINFO; + sigaction(SIGSEGV, &sa, NULL); + sigaction(SIGBUS, &sa, NULL); +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_FAULT_INJECTION) +static int inject_fault(int nth) +{ + return 0; +} + +static int fault_injected(int fail_fd) +{ + return 0; +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_mmap) +long syz_mmap(size_t addr, size_t size) +{ + zx_handle_t mapping = 0; + uintptr_t res = 0; + uintptr_t offset = 16 << 20; + zx_status_t status = zx_vmar_allocate(zx_vmar_root_self(), addr - offset, size, + ZX_VM_FLAG_SPECIFIC | ZX_VM_FLAG_CAN_MAP_READ | ZX_VM_FLAG_CAN_MAP_WRITE, + &mapping, &res); + if (addr != res) + error("zx_vmar_allocate allocated wrong address: %p, want %p", (void*)res, (void*)addr); + return status; +} +#endif diff --git a/executor/common_linux.h b/executor/common_linux.h new file mode 100644 index 000000000..dd7d0ac31 --- /dev/null +++ b/executor/common_linux.h @@ -0,0 +1,877 @@ +// 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. + +// This file is shared between executor and csource package. + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <sys/syscall.h> +#include <unistd.h> +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) +#include <sys/prctl.h> +#endif +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT) && defined(SYZ_USE_TMP_DIR)) +#include <dirent.h> +#include <sys/mount.h> +#endif +#if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_SANDBOX_NAMESPACE) +#include <errno.h> +#include <sched.h> +#include <signal.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <sys/prctl.h> +#include <sys/resource.h> +#include <sys/time.h> +#include <sys/wait.h> +#endif +#if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_SETUID) +#include <grp.h> +#endif +#if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_NAMESPACE) +#include <fcntl.h> +#include <linux/capability.h> +#include <sys/mman.h> +#include <sys/mount.h> +#include <sys/stat.h> +#endif +#if defined(SYZ_EXECUTOR) || defined(SYZ_TUN_ENABLE) +#include <arpa/inet.h> +#include <errno.h> +#include <fcntl.h> +#include <linux/if.h> +#include <linux/if_ether.h> +#include <linux/if_tun.h> +#include <linux/ip.h> +#include <linux/tcp.h> +#include <net/if_arp.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#endif +#if defined(SYZ_EXECUTOR) || defined(SYZ_FAULT_INJECTION) +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <sys/stat.h> +#endif +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_open_dev) +#include <fcntl.h> +#include <stdio.h> +#include <sys/stat.h> +#endif +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_fuse_mount) || defined(__NR_syz_fuseblk_mount) +#include <fcntl.h> +#include <stdio.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#endif +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_open_pts) +#include <fcntl.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#endif +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_kvm_setup_cpu) +#include <errno.h> +#include <fcntl.h> +#include <linux/kvm.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <sys/stat.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) +// One does not simply exit. +// _exit can in fact fail. +// syzkaller did manage to generate a seccomp filter that prohibits exit_group syscall. +// Previously, we get into infinite recursion via segv_handler in such case +// and corrupted output_data, which does matter in our case since it is shared +// with fuzzer process. Loop infinitely instead. Parent will kill us. +// But one does not simply loop either. Compilers are sure that _exit never returns, +// so they remove all code after _exit as dead. Call _exit via volatile indirection. +// And this does not work as well. _exit has own handling of failing exit_group +// in the form of HLT instruction, it will divert control flow from our loop. +// So call the syscall directly. +__attribute__((noreturn)) static void doexit(int status) +{ + volatile unsigned i; + syscall(__NR_exit_group, status); + for (i = 0;; i++) { + } +} +#endif + +#if defined(SYZ_EXECUTOR) +// exit/_exit do not necessary work. +#define exit use_doexit_instead +#define _exit use_doexit_instead +#endif + +#include "common.h" + +#if defined(SYZ_EXECUTOR) || defined(SYZ_HANDLE_SEGV) +static void install_segv_handler() +{ + struct sigaction sa; + + // Don't need that SIGCANCEL/SIGSETXID glibc stuff. + // SIGCANCEL sent to main thread causes it to exit + // without bringing down the whole group. + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_IGN; + syscall(SYS_rt_sigaction, 0x20, &sa, NULL, 8); + syscall(SYS_rt_sigaction, 0x21, &sa, NULL, 8); + + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = segv_handler; + sa.sa_flags = SA_NODEFER | SA_SIGINFO; + sigaction(SIGSEGV, &sa, NULL); + sigaction(SIGBUS, &sa, NULL); +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_USE_TMP_DIR) +static void use_temporary_dir() +{ + char tmpdir_template[] = "./syzkaller.XXXXXX"; + char* tmpdir = mkdtemp(tmpdir_template); + if (!tmpdir) + fail("failed to mkdtemp"); + if (chmod(tmpdir, 0777)) + fail("failed to chmod"); + if (chdir(tmpdir)) + fail("failed to chdir"); +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_TUN_ENABLE) +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]; + int rv; + + va_start(args, format); + + vsnprintf_check(command, sizeof(command), format, args); + rv = system(command); + if (rv != 0) + fail("tun: command \"%s\" failed with code %d", &command[0], rv); + + va_end(args); +} + +static int tunfd = -1; + +// We just need this to be large enough to hold headers that we parse (ethernet/ip/tcp). +// Rest of the packet (if any) will be silently truncated which is fine. +#define SYZ_TUN_MAX_PACKET_SIZE 1000 + +// sysgen knowns about this constant (maxPids) +#define MAX_PIDS 32 +#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 "172.20.%d.170" +#define REMOTE_IPV4 "172.20.%d.187" + +#define LOCAL_IPV6 "fe80::%02hxaa" +#define REMOTE_IPV6 "fe80::%02hxbb" + +static void initialize_tun(uint64_t pid) +{ + if (pid >= MAX_PIDS) + fail("tun: no more than %d executors", MAX_PIDS); + int id = pid; + + tunfd = open("/dev/net/tun", O_RDWR | O_NONBLOCK); + 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); + + // Disable IPv6 DAD, otherwise the address remains unusable until DAD completes. + execute_command("sysctl -w net.ipv6.conf.%s.accept_dad=0", iface); + + // Disable IPv6 router solicitation to prevent IPv6 spam. + execute_command("sysctl -w net.ipv6.conf.%s.router_solicitations=0", iface); + // There seems to be no way to disable IPv6 MTD to prevent more IPv6 spam. + + 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 dev %s up", iface); +} + +static void setup_tun(uint64_t pid, bool enable_tun) +{ + if (enable_tun) + initialize_tun(pid); +} +#endif + +#if defined(SYZ_EXECUTOR) || (defined(SYZ_TUN_ENABLE) && (defined(__NR_syz_extract_tcp_res) || defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT))) +static int read_tun(char* data, int size) +{ + int rv = read(tunfd, data, size); + if (rv < 0) { + if (errno == EAGAIN) + return -1; + fail("tun: read failed with %d, errno: %d", rv, errno); + } + return rv; +} +#endif + +#if defined(SYZ_EXECUTOR) || (defined(SYZ_DEBUG) && defined(SYZ_TUN_ENABLE) && (defined(__NR_syz_emit_ethernet) || defined(__NR_syz_extract_tcp_res))) +static void debug_dump_data(const char* data, int length) +{ + int i; + for (i = 0; i < length; i++) { + debug("%02hx ", (uint8_t)data[i] & (uint8_t)0xff); + if (i % 16 == 15) + debug("\n"); + } + if (i % 16 != 0) + debug("\n"); +} +#endif + +#if defined(SYZ_EXECUTOR) || (defined(__NR_syz_emit_ethernet) && defined(SYZ_TUN_ENABLE)) +static uintptr_t syz_emit_ethernet(uintptr_t a0, uintptr_t a1) +{ + // syz_emit_ethernet(len len[packet], packet ptr[in, eth_packet]) + + if (tunfd < 0) + return (uintptr_t)-1; + + int64_t length = a0; + char* data = (char*)a1; + debug_dump_data(data, length); + return write(tunfd, data, length); +} +#endif + +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT) && defined(SYZ_TUN_ENABLE)) +static void flush_tun() +{ + char data[SYZ_TUN_MAX_PACKET_SIZE]; + while (read_tun(&data[0], sizeof(data)) != -1) + ; +} +#endif + +#if defined(SYZ_EXECUTOR) || (defined(__NR_syz_extract_tcp_res) && defined(SYZ_TUN_ENABLE)) +#ifndef __ANDROID__ +// Can't include <linux/ipv6.h>, since it causes +// conflicts due to some structs redefinition. +struct ipv6hdr { + __u8 priority : 4, + version : 4; + __u8 flow_lbl[3]; + + __be16 payload_len; + __u8 nexthdr; + __u8 hop_limit; + + struct in6_addr saddr; + struct in6_addr daddr; +}; +#endif + +struct tcp_resources { + int32_t seq; + int32_t ack; +}; + +static uintptr_t syz_extract_tcp_res(uintptr_t a0, uintptr_t a1, uintptr_t a2) +{ + // syz_extract_tcp_res(res ptr[out, tcp_resources], seq_inc int32, ack_inc int32) + + if (tunfd < 0) + return (uintptr_t)-1; + + char data[SYZ_TUN_MAX_PACKET_SIZE]; + int rv = read_tun(&data[0], sizeof(data)); + if (rv == -1) + return (uintptr_t)-1; + size_t length = rv; + debug_dump_data(data, length); + + struct tcphdr* tcphdr; + + if (length < sizeof(struct ethhdr)) + return (uintptr_t)-1; + struct ethhdr* ethhdr = (struct ethhdr*)&data[0]; + + if (ethhdr->h_proto == htons(ETH_P_IP)) { + if (length < sizeof(struct ethhdr) + sizeof(struct iphdr)) + return (uintptr_t)-1; + struct iphdr* iphdr = (struct iphdr*)&data[sizeof(struct ethhdr)]; + if (iphdr->protocol != IPPROTO_TCP) + return (uintptr_t)-1; + if (length < sizeof(struct ethhdr) + iphdr->ihl * 4 + sizeof(struct tcphdr)) + return (uintptr_t)-1; + tcphdr = (struct tcphdr*)&data[sizeof(struct ethhdr) + iphdr->ihl * 4]; + } else { + if (length < sizeof(struct ethhdr) + sizeof(struct ipv6hdr)) + return (uintptr_t)-1; + struct ipv6hdr* ipv6hdr = (struct ipv6hdr*)&data[sizeof(struct ethhdr)]; + // TODO: parse and skip extension headers. + if (ipv6hdr->nexthdr != IPPROTO_TCP) + return (uintptr_t)-1; + if (length < sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + sizeof(struct tcphdr)) + return (uintptr_t)-1; + tcphdr = (struct tcphdr*)&data[sizeof(struct ethhdr) + sizeof(struct ipv6hdr)]; + } + + struct tcp_resources* res = (struct tcp_resources*)a0; + NONFAILING(res->seq = htonl((ntohl(tcphdr->seq) + (uint32_t)a1))); + NONFAILING(res->ack = htonl((ntohl(tcphdr->ack_seq) + (uint32_t)a2))); + + debug("extracted seq: %08x\n", res->seq); + debug("extracted ack: %08x\n", res->ack); + + return 0; +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_open_dev) +static uintptr_t syz_open_dev(uintptr_t a0, uintptr_t a1, uintptr_t a2) +{ + if (a0 == 0xc || a0 == 0xb) { + // syz_open_dev$char(dev const[0xc], major intptr, minor intptr) fd + // syz_open_dev$block(dev const[0xb], major intptr, minor intptr) fd + char buf[128]; + sprintf(buf, "/dev/%s/%d:%d", a0 == 0xc ? "char" : "block", (uint8_t)a1, (uint8_t)a2); + return open(buf, O_RDWR, 0); + } else { + // syz_open_dev(dev strconst, id intptr, flags flags[open_flags]) fd + char buf[1024]; + char* hash; + NONFAILING(strncpy(buf, (char*)a0, sizeof(buf))); + buf[sizeof(buf) - 1] = 0; + while ((hash = strchr(buf, '#'))) { + *hash = '0' + (char)(a1 % 10); // 10 devices should be enough for everyone. + a1 /= 10; + } + return open(buf, a2, 0); + } +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_open_pts) +static uintptr_t syz_open_pts(uintptr_t a0, uintptr_t a1) +{ + // syz_openpts(fd fd[tty], flags flags[open_flags]) fd[tty] + int ptyno = 0; + if (ioctl(a0, TIOCGPTN, &ptyno)) + return -1; + char buf[128]; + sprintf(buf, "/dev/pts/%d", ptyno); + return open(buf, a1, 0); +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_fuse_mount) +static uintptr_t syz_fuse_mount(uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5) +{ + // syz_fuse_mount(target filename, mode flags[fuse_mode], uid uid, gid gid, maxread intptr, flags flags[mount_flags]) fd[fuse] + uint64_t target = a0; + uint64_t mode = a1; + uint64_t uid = a2; + uint64_t gid = a3; + uint64_t maxread = a4; + uint64_t flags = a5; + + int fd = open("/dev/fuse", O_RDWR); + if (fd == -1) + return fd; + char buf[1024]; + sprintf(buf, "fd=%d,user_id=%ld,group_id=%ld,rootmode=0%o", fd, (long)uid, (long)gid, (unsigned)mode & ~3u); + if (maxread != 0) + sprintf(buf + strlen(buf), ",max_read=%ld", (long)maxread); + if (mode & 1) + strcat(buf, ",default_permissions"); + if (mode & 2) + strcat(buf, ",allow_other"); + syscall(SYS_mount, "", target, "fuse", flags, buf); + // Ignore errors, maybe fuzzer can do something useful with fd alone. + return fd; +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_fuseblk_mount) +static uintptr_t syz_fuseblk_mount(uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7) +{ + // syz_fuseblk_mount(target filename, blkdev filename, mode flags[fuse_mode], uid uid, gid gid, maxread intptr, blksize intptr, flags flags[mount_flags]) fd[fuse] + uint64_t target = a0; + uint64_t blkdev = a1; + uint64_t mode = a2; + uint64_t uid = a3; + uint64_t gid = a4; + uint64_t maxread = a5; + uint64_t blksize = a6; + uint64_t flags = a7; + + int fd = open("/dev/fuse", O_RDWR); + if (fd == -1) + return fd; + if (syscall(SYS_mknodat, AT_FDCWD, blkdev, S_IFBLK, makedev(7, 199))) + return fd; + char buf[256]; + sprintf(buf, "fd=%d,user_id=%ld,group_id=%ld,rootmode=0%o", fd, (long)uid, (long)gid, (unsigned)mode & ~3u); + if (maxread != 0) + sprintf(buf + strlen(buf), ",max_read=%ld", (long)maxread); + if (blksize != 0) + sprintf(buf + strlen(buf), ",blksize=%ld", (long)blksize); + if (mode & 1) + strcat(buf, ",default_permissions"); + if (mode & 2) + strcat(buf, ",allow_other"); + syscall(SYS_mount, blkdev, target, "fuseblk", flags, buf); + // Ignore errors, maybe fuzzer can do something useful with fd alone. + return fd; +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_kvm_setup_cpu) +#if defined(__x86_64__) +#include "common_kvm_amd64.h" +#elif defined(__aarch64__) +#include "common_kvm_arm64.h" +#else +static uintptr_t syz_kvm_setup_cpu(uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7) +{ + return 0; +} +#endif +#endif // #ifdef __NR_syz_kvm_setup_cpu + +#if defined(SYZ_EXECUTOR) +// TODO(dvyukov): syz_test call should be moved to a "test" target. +static uintptr_t syz_test() +{ + return 0; +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_NONE) || defined(SYZ_SANDBOX_SETUID) || defined(SYZ_SANDBOX_NAMESPACE) +static void loop(); + +static void sandbox_common() +{ + prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); + setpgrp(); + setsid(); + + struct rlimit rlim; + rlim.rlim_cur = rlim.rlim_max = 128 << 20; + setrlimit(RLIMIT_AS, &rlim); + rlim.rlim_cur = rlim.rlim_max = 8 << 20; + setrlimit(RLIMIT_MEMLOCK, &rlim); + rlim.rlim_cur = rlim.rlim_max = 1 << 20; + setrlimit(RLIMIT_FSIZE, &rlim); + rlim.rlim_cur = rlim.rlim_max = 1 << 20; + setrlimit(RLIMIT_STACK, &rlim); + rlim.rlim_cur = rlim.rlim_max = 0; + setrlimit(RLIMIT_CORE, &rlim); + + // CLONE_NEWIPC/CLONE_IO cause EINVAL on some systems, so we do them separately of clone. + unshare(CLONE_NEWNS); + unshare(CLONE_NEWIPC); + unshare(CLONE_IO); +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_NONE) +static int do_sandbox_none(int executor_pid, bool enable_tun) +{ + int pid = fork(); + if (pid) + return pid; + + sandbox_common(); +#if defined(SYZ_EXECUTOR) || defined(SYZ_TUN_ENABLE) + setup_tun(executor_pid, enable_tun); +#endif + + loop(); + doexit(1); +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_SETUID) +static int do_sandbox_setuid(int executor_pid, bool enable_tun) +{ + int pid = fork(); + if (pid) + return pid; + + sandbox_common(); +#if defined(SYZ_EXECUTOR) || defined(SYZ_TUN_ENABLE) + setup_tun(executor_pid, enable_tun); +#endif + + const int nobody = 65534; + if (setgroups(0, NULL)) + fail("failed to setgroups"); + if (syscall(SYS_setresgid, nobody, nobody, nobody)) + fail("failed to setresgid"); + if (syscall(SYS_setresuid, nobody, nobody, nobody)) + fail("failed to setresuid"); + + // This is required to open /proc/self/* files. + // Otherwise they are owned by root and we can't open them after setuid. + // See task_dump_owner function in kernel. + prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); + + loop(); + doexit(1); +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_NAMESPACE) || defined(SYZ_FAULT_INJECTION) +static bool write_file(const char* file, const char* what, ...) +{ + char buf[1024]; + va_list args; + va_start(args, what); + vsnprintf(buf, sizeof(buf), what, args); + va_end(args); + buf[sizeof(buf) - 1] = 0; + int len = strlen(buf); + + int fd = open(file, O_WRONLY | O_CLOEXEC); + if (fd == -1) + return false; + if (write(fd, buf, len) != len) { + close(fd); + return false; + } + close(fd); + return true; +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_NAMESPACE) +static int real_uid; +static int real_gid; +static int epid; +static bool etun; +__attribute__((aligned(64 << 10))) static char sandbox_stack[1 << 20]; + +static int namespace_sandbox_proc(void* arg) +{ + sandbox_common(); + + // /proc/self/setgroups is not present on some systems, ignore error. + write_file("/proc/self/setgroups", "deny"); + if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)) + fail("write of /proc/self/uid_map failed"); + if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) + fail("write of /proc/self/gid_map failed"); + +#if defined(SYZ_EXECUTOR) || defined(SYZ_TUN_ENABLE) + // For sandbox namespace we setup tun after initializing uid mapping, + // otherwise ip commands fail. + setup_tun(epid, etun); +#endif + + if (mkdir("./syz-tmp", 0777)) + fail("mkdir(syz-tmp) failed"); + if (mount("", "./syz-tmp", "tmpfs", 0, NULL)) + fail("mount(tmpfs) failed"); + if (mkdir("./syz-tmp/newroot", 0777)) + fail("mkdir failed"); + if (mkdir("./syz-tmp/newroot/dev", 0700)) + fail("mkdir failed"); + if (mount("/dev", "./syz-tmp/newroot/dev", NULL, MS_BIND | MS_REC | MS_PRIVATE, NULL)) + fail("mount(dev) failed"); + if (mkdir("./syz-tmp/newroot/proc", 0700)) + fail("mkdir failed"); + if (mount(NULL, "./syz-tmp/newroot/proc", "proc", 0, NULL)) + fail("mount(proc) failed"); + if (mkdir("./syz-tmp/pivot", 0777)) + fail("mkdir failed"); + if (syscall(SYS_pivot_root, "./syz-tmp", "./syz-tmp/pivot")) { + debug("pivot_root failed"); + if (chdir("./syz-tmp")) + fail("chdir failed"); + } else { + if (chdir("/")) + fail("chdir failed"); + if (umount2("./pivot", MNT_DETACH)) + fail("umount failed"); + } + if (chroot("./newroot")) + fail("chroot failed"); + if (chdir("/")) + fail("chdir failed"); + + // Drop CAP_SYS_PTRACE so that test processes can't attach to parent processes. + // Previously it lead to hangs because the loop process stopped due to SIGSTOP. + // Note that a process can always ptrace its direct children, which is enough + // for testing purposes. + struct __user_cap_header_struct cap_hdr = {}; + struct __user_cap_data_struct cap_data[2] = {}; + cap_hdr.version = _LINUX_CAPABILITY_VERSION_3; + cap_hdr.pid = getpid(); + if (syscall(SYS_capget, &cap_hdr, &cap_data)) + fail("capget failed"); + cap_data[0].effective &= ~(1 << CAP_SYS_PTRACE); + cap_data[0].permitted &= ~(1 << CAP_SYS_PTRACE); + cap_data[0].inheritable &= ~(1 << CAP_SYS_PTRACE); + if (syscall(SYS_capset, &cap_hdr, &cap_data)) + fail("capset failed"); + + loop(); + doexit(1); +} + +static int do_sandbox_namespace(int executor_pid, bool enable_tun) +{ + real_uid = getuid(); + real_gid = getgid(); + epid = executor_pid; + etun = enable_tun; + mprotect(sandbox_stack, 4096, PROT_NONE); // to catch stack underflows + return clone(namespace_sandbox_proc, &sandbox_stack[sizeof(sandbox_stack) - 64], + CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWNET, NULL); +} +#endif + +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT) && defined(SYZ_USE_TMP_DIR)) +// One does not simply remove a directory. +// There can be mounts, so we need to try to umount. +// Moreover, a mount can be mounted several times, so we need to try to umount in a loop. +// Moreover, after umount a dir can become non-empty again, so we need another loop. +// Moreover, a mount can be re-mounted as read-only and then we will fail to make a dir empty. +static void remove_dir(const char* dir) +{ + DIR* dp; + struct dirent* ep; + int iter = 0; +retry: + dp = opendir(dir); + if (dp == NULL) { + if (errno == EMFILE) { + // This happens when the test process casts prlimit(NOFILE) on us. + // Ideally we somehow prevent test processes from messing with parent processes. + // But full sandboxing is expensive, so let's ignore this error for now. + exitf("opendir(%s) failed due to NOFILE, exiting"); + } + exitf("opendir(%s) failed", dir); + } + while ((ep = readdir(dp))) { + if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0) + continue; + char filename[FILENAME_MAX]; + snprintf(filename, sizeof(filename), "%s/%s", dir, ep->d_name); + struct stat st; + if (lstat(filename, &st)) + exitf("lstat(%s) failed", filename); + if (S_ISDIR(st.st_mode)) { + remove_dir(filename); + continue; + } + int i; + for (i = 0;; i++) { + debug("unlink(%s)\n", filename); + if (unlink(filename) == 0) + break; + if (errno == EROFS) { + debug("ignoring EROFS\n"); + break; + } + if (errno != EBUSY || i > 100) + exitf("unlink(%s) failed", filename); + debug("umount(%s)\n", filename); + if (umount2(filename, MNT_DETACH)) + exitf("umount(%s) failed", filename); + } + } + closedir(dp); + int i; + for (i = 0;; i++) { + debug("rmdir(%s)\n", dir); + if (rmdir(dir) == 0) + break; + if (i < 100) { + if (errno == EROFS) { + debug("ignoring EROFS\n"); + break; + } + if (errno == EBUSY) { + debug("umount(%s)\n", dir); + if (umount2(dir, MNT_DETACH)) + exitf("umount(%s) failed", dir); + continue; + } + if (errno == ENOTEMPTY) { + if (iter < 100) { + iter++; + goto retry; + } + } + } + exitf("rmdir(%s) failed", dir); + } +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_FAULT_INJECTION) +static int inject_fault(int nth) +{ + int fd; + char buf[128]; + + sprintf(buf, "/proc/self/task/%d/fail-nth", (int)syscall(SYS_gettid)); + fd = open(buf, O_RDWR); + if (fd == -1) + fail("failed to open /proc/self/task/tid/fail-nth"); + sprintf(buf, "%d", nth + 1); + if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) + fail("failed to write /proc/self/task/tid/fail-nth"); + return fd; +} +#endif + +#if defined(SYZ_EXECUTOR) +static int fault_injected(int fail_fd) +{ + char buf[16]; + int n = read(fail_fd, buf, sizeof(buf) - 1); + if (n <= 0) + fail("failed to read /proc/self/task/tid/fail-nth"); + int res = n == 2 && buf[0] == '0' && buf[1] == '\n'; + buf[0] = '0'; + if (write(fail_fd, buf, 1) != 1) + fail("failed to write /proc/self/task/tid/fail-nth"); + close(fail_fd); + return res; +} +#endif + +#if defined(SYZ_REPEAT) +static void test(); + +#if defined(SYZ_WAIT_REPEAT) +void loop() +{ + int iter; + for (iter = 0;; iter++) { +#ifdef SYZ_USE_TMP_DIR + char cwdbuf[256]; + sprintf(cwdbuf, "./%d", iter); + if (mkdir(cwdbuf, 0777)) + fail("failed to mkdir"); +#endif + int pid = fork(); + if (pid < 0) + fail("clone failed"); + if (pid == 0) { + prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); + setpgrp(); +#ifdef SYZ_USE_TMP_DIR + if (chdir(cwdbuf)) + fail("failed to chdir"); +#endif +#ifdef SYZ_TUN_ENABLE + flush_tun(); +#endif + test(); + doexit(0); + } + int status = 0; + uint64_t start = current_time_ms(); + for (;;) { + int res = waitpid(-1, &status, __WALL | WNOHANG); + if (res == pid) + break; + usleep(1000); + if (current_time_ms() - start > 5 * 1000) { + kill(-pid, SIGKILL); + kill(pid, SIGKILL); + while (waitpid(-1, &status, __WALL) != pid) { + } + break; + } + } +#ifdef SYZ_USE_TMP_DIR + remove_dir(cwdbuf); +#endif + } +} +#else +void loop() +{ + while (1) { + test(); + } +} +#endif +#endif diff --git a/executor/executor.h b/executor/executor.h new file mode 100644 index 000000000..08112ec5d --- /dev/null +++ b/executor/executor.h @@ -0,0 +1,684 @@ +// Copyright 2017 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 <algorithm> +#include <errno.h> +#include <pthread.h> +#include <signal.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#ifndef GIT_REVISION +#define GIT_REVISION "unknown" +#endif + +const int kMaxInput = 2 << 20; +const int kMaxOutput = 16 << 20; +const int kMaxArgs = 9; +const int kMaxThreads = 16; +const int kMaxCommands = 16 << 10; + +const uint64_t instr_eof = -1; +const uint64_t instr_copyin = -2; +const uint64_t instr_copyout = -3; + +const uint64_t arg_const = 0; +const uint64_t arg_result = 1; +const uint64_t arg_data = 2; +const uint64_t arg_csum = 3; + +enum sandbox_type { + sandbox_none, + sandbox_setuid, + sandbox_namespace, +}; + +bool flag_cover; +bool flag_threaded; +bool flag_collide; +bool flag_sandbox_privs; +sandbox_type flag_sandbox; +bool flag_enable_tun; +bool flag_enable_fault_injection; + +bool flag_collect_cover; +bool flag_dedup_cover; + +// If true, then executor should write the comparisons data to fuzzer. +bool flag_collect_comps; + +// Inject fault into flag_fault_nth-th operation in flag_fault_call-th syscall. +bool flag_inject_fault; +int flag_fault_call; +int flag_fault_nth; + +int running; +uint32_t completed; +bool collide; + +// We use the default value instead of results of failed syscalls. +// -1 is an invalid fd and an invalid address and deterministic, +// so good enough for our purposes. +const uint64_t default_value = -1; + +// Checksum kinds. +const uint64_t arg_csum_inet = 0; + +// Checksum chunk kinds. +const uint64_t arg_csum_chunk_data = 0; +const uint64_t arg_csum_chunk_const = 1; + +struct thread_t { + bool created; + int id; + pthread_t th; + // TODO(dvyukov): this assumes 64-bit kernel. This must be "kernel long" somehow. + uint64_t* cover_data; + // Pointer to the size of coverage (stored as first word of memory). + uint64_t* cover_size_ptr; + + event_t ready; + event_t done; + uint64_t* copyout_pos; + bool handled; + int call_n; + int call_index; + int call_num; + int num_args; + long args[kMaxArgs]; + long res; + uint32_t reserrno; + uint64_t cover_size; + bool fault_injected; + int cover_fd; +}; + +thread_t threads[kMaxThreads]; + +struct res_t { + bool executed; + uint64_t val; +}; + +res_t results[kMaxCommands]; + +enum { + KCOV_CMP_CONST = 1, + KCOV_CMP_SIZE1 = 0, + KCOV_CMP_SIZE2 = 2, + KCOV_CMP_SIZE4 = 4, + KCOV_CMP_SIZE8 = 6, + KCOV_CMP_SIZE_MASK = 6, +}; + +struct kcov_comparison_t { + uint64_t type; + uint64_t arg1; + uint64_t arg2; + + // Writes the structure using the write_one function for each field. + // Inspired by write_output() function. + void write(uint32_t* (*write_one)(uint32_t)); + bool operator==(const struct kcov_comparison_t& other) const; + bool operator<(const struct kcov_comparison_t& other) const; +}; + +long execute_syscall(call_t* c, long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8); +thread_t* schedule_call(int n, int call_index, int call_num, uint64_t num_args, uint64_t* args, uint64_t* pos); +void handle_completion(thread_t* th); +void execute_call(thread_t* th); +void thread_create(thread_t* th, int id); +void* worker_thread(void* arg); +void event_init(event_t* ev); +void event_set(event_t* ev); +void event_reset(event_t* ev); +void event_wait(event_t* ev); +bool event_isset(event_t* ev); +bool event_timedwait(event_t* ev, uint64_t timeout_ms); +uint32_t* write_output(uint32_t v); +void write_completed(uint32_t completed); +uint64_t read_input(uint64_t** input_posp, bool peek = false); +uint64_t read_arg(uint64_t** input_posp); +uint64_t read_result(uint64_t** input_posp); +void copyin(char* addr, uint64_t val, uint64_t size, uint64_t bf_off, uint64_t bf_len); +uint64_t copyout(char* addr, uint64_t size); +void cover_open(); +void cover_enable(thread_t* th); +void cover_reset(thread_t* th); +uint64_t read_cover_size(thread_t* th); +static uint32_t hash(uint32_t a); +static bool dedup(uint32_t sig); + +void execute_one(uint64_t* input_data) +{ +retry: + uint64_t* input_pos = input_data; + write_output(0); // Number of executed syscalls (updated later). + + if (!collide && !flag_threaded) + cover_enable(&threads[0]); + + int call_index = 0; + for (int n = 0;; n++) { + uint64_t call_num = read_input(&input_pos); + if (call_num == instr_eof) + break; + if (call_num == instr_copyin) { + char* addr = (char*)read_input(&input_pos); + uint64_t typ = read_input(&input_pos); + uint64_t size = read_input(&input_pos); + debug("copyin to %p\n", addr); + switch (typ) { + case arg_const: { + uint64_t arg = read_input(&input_pos); + uint64_t bf_off = read_input(&input_pos); + uint64_t bf_len = read_input(&input_pos); + copyin(addr, arg, size, bf_off, bf_len); + break; + } + case arg_result: { + uint64_t val = read_result(&input_pos); + copyin(addr, val, size, 0, 0); + break; + } + case arg_data: { + NONFAILING(memcpy(addr, input_pos, size)); + // Read out the data. + for (uint64_t i = 0; i < (size + 7) / 8; i++) + read_input(&input_pos); + break; + } + case arg_csum: { + debug("checksum found at %llx\n", addr); + char* csum_addr = addr; + uint64_t csum_size = size; + uint64_t csum_kind = read_input(&input_pos); + switch (csum_kind) { + case arg_csum_inet: { + if (csum_size != 2) { + fail("inet checksum must be 2 bytes, not %lu", size); + } + debug("calculating checksum for %llx\n", csum_addr); + struct csum_inet csum; + csum_inet_init(&csum); + uint64_t chunks_num = read_input(&input_pos); + uint64_t chunk; + for (chunk = 0; chunk < chunks_num; chunk++) { + uint64_t chunk_kind = read_input(&input_pos); + uint64_t chunk_value = read_input(&input_pos); + uint64_t chunk_size = read_input(&input_pos); + switch (chunk_kind) { + case arg_csum_chunk_data: + debug("#%d: data chunk, addr: %llx, size: %llu\n", chunk, chunk_value, chunk_size); + NONFAILING(csum_inet_update(&csum, (const uint8_t*)chunk_value, chunk_size)); + break; + case arg_csum_chunk_const: + if (chunk_size != 2 && chunk_size != 4 && chunk_size != 8) { + fail("bad checksum const chunk size %lld\n", chunk_size); + } + // Here we assume that const values come to us big endian. + debug("#%d: const chunk, value: %llx, size: %llu\n", chunk, chunk_value, chunk_size); + csum_inet_update(&csum, (const uint8_t*)&chunk_value, chunk_size); + break; + default: + fail("bad checksum chunk kind %lu", chunk_kind); + } + } + int16_t csum_value = csum_inet_digest(&csum); + debug("writing inet checksum %hx to %llx\n", csum_value, csum_addr); + NONFAILING(copyin(csum_addr, csum_value, 2, 0, 0)); + break; + } + default: + fail("bad checksum kind %lu", csum_kind); + } + break; + } + default: + fail("bad argument type %lu", typ); + } + continue; + } + if (call_num == instr_copyout) { + read_input(&input_pos); // addr + read_input(&input_pos); // size + // The copyout will happen when/if the call completes. + continue; + } + + // Normal syscall. + if (call_num >= syscall_count) + fail("invalid command number %lu", call_num); + uint64_t num_args = read_input(&input_pos); + if (num_args > kMaxArgs) + fail("command has bad number of arguments %lu", num_args); + uint64_t args[kMaxArgs] = {}; + for (uint64_t i = 0; i < num_args; i++) + args[i] = read_arg(&input_pos); + for (uint64_t i = num_args; i < 6; i++) + args[i] = 0; + thread_t* th = schedule_call(n, call_index++, call_num, num_args, args, input_pos); + + if (collide && (call_index % 2) == 0) { + // Don't wait for every other call. + // We already have results from the previous execution. + } else if (flag_threaded) { + // Wait for call completion. + // Note: sys knows about this 20ms timeout when it generates + // timespec/timeval values. + const uint64_t timeout_ms = flag_debug ? 500 : 20; + if (event_timedwait(&th->done, timeout_ms)) + handle_completion(th); + // Check if any of previous calls have completed. + // Give them some additional time, because they could have been + // just unblocked by the current call. + if (running < 0) + fail("running = %d", running); + if (running > 0) { + bool last = read_input(&input_pos, true) == instr_eof; + usleep(last ? 1000 : 100); + for (int i = 0; i < kMaxThreads; i++) { + th = &threads[i]; + if (!th->handled && event_isset(&th->done)) + handle_completion(th); + } + } + } else { + // Execute directly. + if (th != &threads[0]) + fail("using non-main thread in non-thread mode"); + execute_call(th); + handle_completion(th); + } + } + + if (flag_collide && !flag_inject_fault && !collide) { + debug("enabling collider\n"); + collide = true; + goto retry; + } +} + +thread_t* schedule_call(int n, int call_index, int call_num, uint64_t num_args, uint64_t* args, uint64_t* pos) +{ + // Find a spare thread to execute the call. + int i; + for (i = 0; i < kMaxThreads; i++) { + thread_t* th = &threads[i]; + if (!th->created) + thread_create(th, i); + if (event_isset(&th->done)) { + if (!th->handled) + handle_completion(th); + break; + } + } + if (i == kMaxThreads) + exitf("out of threads"); + thread_t* th = &threads[i]; + debug("scheduling call %d [%s] on thread %d\n", call_index, syscalls[call_num].name, th->id); + if (event_isset(&th->ready) || !event_isset(&th->done) || !th->handled) + fail("bad thread state in schedule: ready=%d done=%d handled=%d", + event_isset(&th->ready), event_isset(&th->done), th->handled); + th->copyout_pos = pos; + event_reset(&th->done); + th->handled = false; + th->call_n = n; + th->call_index = call_index; + th->call_num = call_num; + th->num_args = num_args; + for (int i = 0; i < kMaxArgs; i++) + th->args[i] = args[i]; + event_set(&th->ready); + running++; + return th; +} + +void handle_completion(thread_t* th) +{ + debug("completion of call %d [%s] on thread %d\n", th->call_index, syscalls[th->call_num].name, th->id); + if (event_isset(&th->ready) || !event_isset(&th->done) || th->handled) + fail("bad thread state in completion: ready=%d done=%d handled=%d", + event_isset(&th->ready), event_isset(&th->done), th->handled); + if (th->res != (long)-1) { + if (th->call_n >= kMaxCommands) + fail("result idx %ld overflows kMaxCommands", th->call_n); + results[th->call_n].executed = true; + results[th->call_n].val = th->res; + for (bool done = false; !done;) { + th->call_n++; + uint64_t call_num = read_input(&th->copyout_pos); + switch (call_num) { + case instr_copyout: { + char* addr = (char*)read_input(&th->copyout_pos); + uint64_t size = read_input(&th->copyout_pos); + uint64_t val = copyout(addr, size); + if (th->call_n >= kMaxCommands) + fail("result idx %ld overflows kMaxCommands", th->call_n); + results[th->call_n].executed = true; + results[th->call_n].val = val; + debug("copyout from %p\n", addr); + break; + } + default: + done = true; + break; + } + } + } + if (!collide) { + write_output(th->call_index); + write_output(th->call_num); + uint32_t reserrno = th->res != (uint32_t)-1 ? 0 : th->reserrno; + write_output(reserrno); + write_output(th->fault_injected); + uint32_t* signal_count_pos = write_output(0); // filled in later + uint32_t* cover_count_pos = write_output(0); // filled in later + uint32_t* comps_count_pos = write_output(0); // filled in later + uint32_t nsig = 0, cover_size = 0, comps_size = 0; + + if (flag_collect_comps) { + // Collect only the comparisons + comps_size = th->cover_size; + kcov_comparison_t* start = (kcov_comparison_t*)th->cover_data; + kcov_comparison_t* end = start + comps_size; + std::sort(start, end); + comps_size = std::unique(start, end) - start; + for (uint32_t i = 0; i < comps_size; ++i) + start[i].write(write_output); + } else { + // Write out feedback signals. + // Currently it is code edges computed as xor of + // two subsequent basic block PCs. + uint32_t prev = 0; + for (uint32_t i = 0; i < th->cover_size; i++) { + uint32_t pc = (uint32_t)th->cover_data[i]; + uint32_t sig = pc ^ prev; + prev = hash(pc); + if (dedup(sig)) + continue; + write_output(sig); + nsig++; + } + if (flag_collect_cover) { + // Write out real coverage (basic block PCs). + cover_size = th->cover_size; + if (flag_dedup_cover) { + uint64_t* start = (uint64_t*)th->cover_data; + uint64_t* end = start + cover_size; + std::sort(start, end); + cover_size = std::unique(start, end) - start; + } + // Truncate PCs to uint32_t assuming that they fit into 32-bits. + // True for x86_64 and arm64 without KASLR. + for (uint32_t i = 0; i < cover_size; i++) + write_output((uint32_t)th->cover_data[i]); + } + } + // Write out real coverage (basic block PCs). + *cover_count_pos = cover_size; + // Write out number of comparisons + *comps_count_pos = comps_size; + // Write out number of signals + *signal_count_pos = nsig; + debug("out #%u: index=%u num=%u errno=%d sig=%u cover=%u comps=%u\n", + completed, th->call_index, th->call_num, reserrno, nsig, + cover_size, comps_size); + completed++; + write_completed(completed); + } + th->handled = true; + running--; +} + +void thread_create(thread_t* th, int id) +{ + th->created = true; + th->id = id; + th->handled = true; + event_init(&th->ready); + event_init(&th->done); + event_set(&th->done); + if (flag_threaded) { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, 128 << 10); + if (pthread_create(&th->th, &attr, worker_thread, th)) + exitf("pthread_create failed"); + pthread_attr_destroy(&attr); + } +} + +void* worker_thread(void* arg) +{ + thread_t* th = (thread_t*)arg; + + cover_enable(th); + for (;;) { + event_wait(&th->ready); + execute_call(th); + } + return 0; +} + +void execute_call(thread_t* th) +{ + event_reset(&th->ready); + call_t* call = &syscalls[th->call_num]; + debug("#%d: %s(", th->id, call->name); + for (int i = 0; i < th->num_args; i++) { + if (i != 0) + debug(", "); + debug("0x%lx", th->args[i]); + } + debug(")\n"); + + int fail_fd = -1; + if (flag_inject_fault && th->call_index == flag_fault_call) { + if (collide) + fail("both collide and fault injection are enabled"); + debug("injecting fault into %d-th operation\n", flag_fault_nth); + fail_fd = inject_fault(flag_fault_nth); + } + + cover_reset(th); + th->res = execute_syscall(call, th->args[0], th->args[1], th->args[2], + th->args[3], th->args[4], th->args[5], + th->args[6], th->args[7], th->args[8]); + th->reserrno = errno; + th->cover_size = read_cover_size(th); + th->fault_injected = false; + + if (flag_inject_fault && th->call_index == flag_fault_call) { + th->fault_injected = fault_injected(fail_fd); + debug("fault injected: %d\n", th->fault_injected); + } + + if (th->res == (uint32_t)-1) + debug("#%d: %s = errno(%d)\n", th->id, call->name, th->reserrno); + else + debug("#%d: %s = 0x%lx\n", th->id, call->name, th->res); + event_set(&th->done); +} + +static uint32_t hash(uint32_t a) +{ + a = (a ^ 61) ^ (a >> 16); + a = a + (a << 3); + a = a ^ (a >> 4); + a = a * 0x27d4eb2d; + a = a ^ (a >> 15); + return a; +} + +const uint32_t dedup_table_size = 8 << 10; +uint32_t dedup_table[dedup_table_size]; + +// Poorman's best-effort hashmap-based deduplication. +// The hashmap is global which means that we deduplicate across different calls. +// This is OK because we are interested only in new signals. +static bool dedup(uint32_t sig) +{ + for (uint32_t i = 0; i < 4; i++) { + uint32_t pos = (sig + i) % dedup_table_size; + if (dedup_table[pos] == sig) + return true; + if (dedup_table[pos] == 0) { + dedup_table[pos] = sig; + return false; + } + } + dedup_table[sig % dedup_table_size] = sig; + return false; +} + +void copyin(char* addr, uint64_t val, uint64_t size, uint64_t bf_off, uint64_t bf_len) +{ + NONFAILING(switch (size) { + case 1: + STORE_BY_BITMASK(uint8_t, addr, val, bf_off, bf_len); + break; + case 2: + STORE_BY_BITMASK(uint16_t, addr, val, bf_off, bf_len); + break; + case 4: + STORE_BY_BITMASK(uint32_t, addr, val, bf_off, bf_len); + break; + case 8: + STORE_BY_BITMASK(uint64_t, addr, val, bf_off, bf_len); + break; + default: + fail("copyin: bad argument size %lu", size); + }); +} + +uint64_t copyout(char* addr, uint64_t size) +{ + uint64_t res = default_value; + NONFAILING(switch (size) { + case 1: + res = *(uint8_t*)addr; + break; + case 2: + res = *(uint16_t*)addr; + break; + case 4: + res = *(uint32_t*)addr; + break; + case 8: + res = *(uint64_t*)addr; + break; + default: + fail("copyout: bad argument size %lu", size); + }); + return res; +} + +uint64_t read_arg(uint64_t** input_posp) +{ + uint64_t typ = read_input(input_posp); + uint64_t size = read_input(input_posp); + (void)size; + uint64_t arg = 0; + switch (typ) { + case arg_const: { + arg = read_input(input_posp); + // Bitfields can't be args of a normal syscall, so just ignore them. + read_input(input_posp); // bit field offset + read_input(input_posp); // bit field length + break; + } + case arg_result: { + arg = read_result(input_posp); + break; + } + default: + fail("bad argument type %lu", typ); + } + return arg; +} + +uint64_t read_result(uint64_t** input_posp) +{ + uint64_t idx = read_input(input_posp); + uint64_t op_div = read_input(input_posp); + uint64_t op_add = read_input(input_posp); + if (idx >= kMaxCommands) + fail("command refers to bad result %ld", idx); + uint64_t arg = default_value; + if (results[idx].executed) { + arg = results[idx].val; + if (op_div != 0) + arg = arg / op_div; + arg += op_add; + } + return arg; +} + +uint64_t read_input(uint64_t** input_posp, bool peek) +{ + uint64_t* input_pos = *input_posp; + //if ((char*)input_pos >= input_data + kMaxInput) + // fail("input command overflows input"); + if (!peek) + *input_posp = input_pos + 1; + return *input_pos; +} + +void kcov_comparison_t::write(uint32_t* (*write_one)(uint32_t)) +{ + // Write order: type arg1 arg2. + write_one((uint32_t)type); + + // KCOV converts all arguments of size x first to uintx_t and then to + // uint64_t. We want to properly extend signed values, e.g we want + // int8_t c = 0xfe to be represented as 0xfffffffffffffffe. + // Note that uint8_t c = 0xfe will be represented the same way. + // This is ok because during hints processing we will anyways try + // the value 0x00000000000000fe. + switch (type & KCOV_CMP_SIZE_MASK) { + case KCOV_CMP_SIZE1: + arg1 = (uint64_t)(int64_t)(int8_t)arg1; + arg2 = (uint64_t)(int64_t)(int8_t)arg2; + break; + case KCOV_CMP_SIZE2: + arg1 = (uint64_t)(int64_t)(int16_t)arg1; + arg2 = (uint64_t)(int64_t)(int16_t)arg2; + break; + case KCOV_CMP_SIZE4: + arg1 = (uint64_t)(int64_t)(int32_t)arg1; + arg2 = (uint64_t)(int64_t)(int32_t)arg2; + break; + } + bool is_size_8 = (type & KCOV_CMP_SIZE_MASK) == KCOV_CMP_SIZE8; + if (!is_size_8) { + write_one((uint32_t)arg1); + write_one((uint32_t)arg2); + return; + } + // If we have 64 bits arguments then write them in Little-endian. + write_one((uint32_t)(arg1 & 0xFFFFFFFF)); + write_one((uint32_t)(arg1 >> 32)); + write_one((uint32_t)(arg2 & 0xFFFFFFFF)); + write_one((uint32_t)(arg2 >> 32)); +} + +bool kcov_comparison_t::operator==(const struct kcov_comparison_t& other) const +{ + return type == other.type && arg1 == other.arg1 && arg2 == other.arg2; +} + +bool kcov_comparison_t::operator<(const struct kcov_comparison_t& other) const +{ + if (type != other.type) + return type < other.type; + if (arg1 != other.arg1) + return arg1 < other.arg1; + return arg2 < other.arg2; +} diff --git a/executor/executor_fuchsia.cc b/executor/executor_fuchsia.cc index 3a214e4ac..4ff293350 100644 --- a/executor/executor_fuchsia.cc +++ b/executor/executor_fuchsia.cc @@ -3,10 +3,145 @@ // +build +#define SYZ_EXECUTOR +#include "common_fuchsia.h" + +struct event_t { + pthread_mutex_t mu; + pthread_cond_t cv; + bool state; +}; + +#include "executor.h" + #include "syscalls_fuchsia.h" -int main() +char input_data[kMaxInput]; +uint32_t output; + +int main(int argc, char** argv) +{ + if (argc == 2 && strcmp(argv[1], "version") == 0) { + puts("linux " GOARCH " " SYZ_REVISION " " GIT_REVISION); + return 0; + } + + int pos = 0; + for (;;) { + int rv = read(0, input_data + pos, sizeof(input_data) - pos); + if (rv < 0) + fail("read failed"); + if (rv == 0) + break; + pos += rv; + } + if (pos < 24) + fail("truncated input"); + + uint64_t flags = *(uint64_t*)input_data; + flag_debug = flags & (1 << 0); + flag_threaded = flags & (1 << 2); + flag_collide = flags & (1 << 3); + if (!flag_threaded) + flag_collide = false; + uint64_t executor_pid = *((uint64_t*)input_data + 2); + debug("input %d, threaded=%d collide=%d pid=%llu\n", + pos, flag_threaded, flag_collide, executor_pid); + + execute_one(((uint64_t*)input_data) + 3); + return 0; +} + +long execute_syscall(call_t* c, long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) +{ + debug("%s = %p\n", c->name, c->call); + long res = c->call(a0, a1, a2, a3, a4, a5, a6, a7, a8); + debug("%s = %ld\n", c->name, res); + return res; +} + +void cover_open() +{ +} + +void cover_enable(thread_t* th) +{ +} + +void cover_reset(thread_t* th) +{ +} + +uint64_t read_cover_size(thread_t* th) { - (void)syscalls; return 0; } + +uint32_t* write_output(uint32_t v) +{ + return &output; +} + +void write_completed(uint32_t completed) +{ +} + +void event_init(event_t* ev) +{ + if (pthread_mutex_init(&ev->mu, 0)) + fail("pthread_mutex_init failed"); + if (pthread_cond_init(&ev->cv, 0)) + fail("pthread_cond_init failed"); + ev->state = false; +} + +void event_reset(event_t* ev) +{ + ev->state = false; +} + +void event_set(event_t* ev) +{ + pthread_mutex_lock(&ev->mu); + if (ev->state) + fail("event already set"); + ev->state = true; + pthread_mutex_unlock(&ev->mu); + pthread_cond_broadcast(&ev->cv); +} + +void event_wait(event_t* ev) +{ + pthread_mutex_lock(&ev->mu); + while (!ev->state) + pthread_cond_wait(&ev->cv, &ev->mu); + pthread_mutex_unlock(&ev->mu); +} + +bool event_isset(event_t* ev) +{ + pthread_mutex_lock(&ev->mu); + bool res = ev->state; + pthread_mutex_unlock(&ev->mu); + return res; +} + +bool event_timedwait(event_t* ev, uint64_t timeout_ms) +{ + pthread_mutex_lock(&ev->mu); + uint64_t start = current_time_ms(); + for (;;) { + if (ev->state) + break; + uint64_t now = current_time_ms(); + if (now - start > timeout_ms) + break; + timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = (timeout_ms - (now - start)) * 1000 * 1000; + pthread_cond_timedwait(&ev->cv, &ev->mu, &ts); + } + bool res = ev->state; + pthread_mutex_unlock(&ev->mu); + return res; +} diff --git a/executor/executor_linux.cc b/executor/executor_linux.cc index d428fc1ea..85a0b29c8 100644 --- a/executor/executor_linux.cc +++ b/executor/executor_linux.cc @@ -3,19 +3,9 @@ // +build -#include <algorithm> -#include <errno.h> #include <fcntl.h> #include <limits.h> #include <linux/futex.h> -#include <pthread.h> -#include <setjmp.h> -#include <signal.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> #include <sys/ioctl.h> #include <sys/prctl.h> #include <sys/stat.h> @@ -23,13 +13,17 @@ #include <sys/time.h> #include <sys/types.h> #include <sys/wait.h> -#include <time.h> #include <unistd.h> -#include "syscalls_linux.h" +struct event_t { + int state; +}; #define SYZ_EXECUTOR -#include "common.h" +#include "common_linux.h" +#include "executor.h" + +#include "syscalls_linux.h" #define KCOV_INIT_TRACE _IOR('c', 1, unsigned long long) #define KCOV_INIT_CMP _IOR('c', 2, unsigned long long) @@ -43,192 +37,12 @@ const int kInFd = 3; const int kOutFd = 4; const int kInPipeFd = 5; const int kOutPipeFd = 6; -const int kMaxInput = 2 << 20; -const int kMaxOutput = 16 << 20; -const int kMaxArgs = 9; -const int kMaxThreads = 16; -const int kMaxCommands = 16 << 10; const int kCoverSize = 64 << 10; const int kPageSize = 4 << 10; -const uint64_t instr_eof = -1; -const uint64_t instr_copyin = -2; -const uint64_t instr_copyout = -3; - -const uint64_t arg_const = 0; -const uint64_t arg_result = 1; -const uint64_t arg_data = 2; -const uint64_t arg_csum = 3; - -// We use the default value instead of results of failed syscalls. -// -1 is an invalid fd and an invalid address and deterministic, -// so good enough for our purposes. -const uint64_t default_value = -1; - -enum sandbox_type { - sandbox_none, - sandbox_setuid, - sandbox_namespace, -}; - -bool flag_cover; -bool flag_threaded; -bool flag_collide; -bool flag_sandbox_privs; -sandbox_type flag_sandbox; -bool flag_enable_tun; -bool flag_enable_fault_injection; - -bool flag_collect_cover; -bool flag_dedup_cover; - -// If true, then executor should write the comparisons data to fuzzer. -bool flag_collect_comps; - -// Inject fault into flag_fault_nth-th operation in flag_fault_call-th syscall. -bool flag_inject_fault; -int flag_fault_call; -int flag_fault_nth; - __attribute__((aligned(64 << 10))) char input_data[kMaxInput]; uint32_t* output_data; uint32_t* output_pos; -uint32_t completed; -int running; -bool collide; - -struct res_t { - bool executed; - uint64_t val; -}; - -res_t results[kMaxCommands]; - -enum { - KCOV_CMP_CONST = 1, - KCOV_CMP_SIZE1 = 0, - KCOV_CMP_SIZE2 = 2, - KCOV_CMP_SIZE4 = 4, - KCOV_CMP_SIZE8 = 6, - KCOV_CMP_SIZE_MASK = 6, -}; - -struct kcov_comparison_t { - uint64_t type; - uint64_t arg1; - uint64_t arg2; - - bool operator==(const struct kcov_comparison_t& other) const - { - return type == other.type && arg1 == other.arg1 && arg2 == other.arg2; - } - - bool operator<(const struct kcov_comparison_t& other) const - { - if (type != other.type) - return type < other.type; - if (arg1 != other.arg1) - return arg1 < other.arg1; - return arg2 < other.arg2; - } - - // Writes the structure using the write_one function for each field. - // Inspired by write_output() function. - void write(uint32_t* (*write_one)(uint32_t)) - { - // Write order: type arg1 arg2. - write_one((uint32_t)type); - - // KCOV converts all arguments of size x first to uintx_t and then to - // uint64_t. We want to properly extend signed values, e.g we want - // int8_t c = 0xfe to be represented as 0xfffffffffffffffe. - // Note that uint8_t c = 0xfe will be represented the same way. - // This is ok because during hints processing we will anyways try - // the value 0x00000000000000fe. - switch (type & KCOV_CMP_SIZE_MASK) { - case KCOV_CMP_SIZE1: - arg1 = (uint64_t)(int64_t)(int8_t)arg1; - arg2 = (uint64_t)(int64_t)(int8_t)arg2; - break; - case KCOV_CMP_SIZE2: - arg1 = (uint64_t)(int64_t)(int16_t)arg1; - arg2 = (uint64_t)(int64_t)(int16_t)arg2; - break; - case KCOV_CMP_SIZE4: - arg1 = (uint64_t)(int64_t)(int32_t)arg1; - arg2 = (uint64_t)(int64_t)(int32_t)arg2; - break; - } - bool is_size_8 = (type & KCOV_CMP_SIZE_MASK) == KCOV_CMP_SIZE8; - if (!is_size_8) { - write_one((uint32_t)arg1); - write_one((uint32_t)arg2); - return; - } - // If we have 64 bits arguments then write them in Little-endian. - write_one((uint32_t)(arg1 & 0xFFFFFFFF)); - write_one((uint32_t)(arg1 >> 32)); - write_one((uint32_t)(arg2 & 0xFFFFFFFF)); - write_one((uint32_t)(arg2 >> 32)); - } -}; - -struct thread_t { - bool created; - int id; - pthread_t th; - // TODO(dvyukov): this assumes 64-bit kernel. This must be "kernel long" somehow. - uint64_t* cover_data; - // Pointer to the size of coverage (stored as first word of memory). - uint64_t* cover_size_ptr; - - uint64_t* copyout_pos; - int ready; - int done; - bool handled; - int call_n; - int call_index; - int call_num; - int num_args; - uintptr_t args[kMaxArgs]; - uintptr_t res; - uint32_t reserrno; - uint64_t cover_size; - bool fault_injected; - int cover_fd; -}; - -thread_t threads[kMaxThreads]; - -// Checksum kinds. -const uint64_t arg_csum_inet = 0; - -// Checksum chunk kinds. -const uint64_t arg_csum_chunk_data = 0; -const uint64_t arg_csum_chunk_const = 1; - -void execute_one(); -uint64_t read_input(uint64_t** input_posp, bool peek = false); -uint64_t read_arg(uint64_t** input_posp); -uint64_t read_result(uint64_t** input_posp); -uint32_t* write_output(uint32_t v); -void copyin(char* addr, uint64_t val, uint64_t size, uint64_t bf_off, uint64_t bf_len); -uint64_t copyout(char* addr, uint64_t size); -thread_t* schedule_call(int n, int call_index, int call_num, uint64_t num_args, uint64_t* args, uint64_t* pos); -void execute_call(thread_t* th); -void handle_completion(thread_t* th); -void thread_create(thread_t* th, int id); -void* worker_thread(void* arg); -void cover_open(); -void cover_enable(thread_t* th); -void cover_reset(thread_t* th); -uint64_t read_cover_size(thread_t* th); -static uint32_t hash(uint32_t a); -static bool dedup(uint32_t sig); - -#ifndef GIT_REVISION -#define GIT_REVISION "unknown" -#endif int main(int argc, char** argv) { @@ -373,7 +187,9 @@ void loop() // isolate consequently executing programs. flush_tun(); } - execute_one(); + uint64_t* input_pos = ((uint64_t*)&input_data[0]) + 2; // skip flags and pid + output_pos = output_data; + execute_one(input_pos); debug("worker exiting\n"); doexit(0); } @@ -437,380 +253,11 @@ void loop() } } -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 = output_data; - write_output(0); // Number of executed syscalls (updated later). - - if (!collide && !flag_threaded) - cover_enable(&threads[0]); - - int call_index = 0; - for (int n = 0;; n++) { - uint64_t call_num = read_input(&input_pos); - if (call_num == instr_eof) - break; - if (call_num == instr_copyin) { - char* addr = (char*)read_input(&input_pos); - uint64_t typ = read_input(&input_pos); - uint64_t size = read_input(&input_pos); - debug("copyin to %p\n", addr); - switch (typ) { - case arg_const: { - uint64_t arg = read_input(&input_pos); - uint64_t bf_off = read_input(&input_pos); - uint64_t bf_len = read_input(&input_pos); - copyin(addr, arg, size, bf_off, bf_len); - break; - } - case arg_result: { - uint64_t val = read_result(&input_pos); - copyin(addr, val, size, 0, 0); - break; - } - case arg_data: { - NONFAILING(memcpy(addr, input_pos, size)); - // Read out the data. - for (uint64_t i = 0; i < (size + 7) / 8; i++) - read_input(&input_pos); - break; - } - case arg_csum: { - debug("checksum found at %llx\n", addr); - char* csum_addr = addr; - uint64_t csum_size = size; - uint64_t csum_kind = read_input(&input_pos); - switch (csum_kind) { - case arg_csum_inet: { - if (csum_size != 2) { - fail("inet checksum must be 2 bytes, not %lu", size); - } - debug("calculating checksum for %llx\n", csum_addr); - struct csum_inet csum; - csum_inet_init(&csum); - uint64_t chunks_num = read_input(&input_pos); - uint64_t chunk; - for (chunk = 0; chunk < chunks_num; chunk++) { - uint64_t chunk_kind = read_input(&input_pos); - uint64_t chunk_value = read_input(&input_pos); - uint64_t chunk_size = read_input(&input_pos); - switch (chunk_kind) { - case arg_csum_chunk_data: - debug("#%d: data chunk, addr: %llx, size: %llu\n", chunk, chunk_value, chunk_size); - NONFAILING(csum_inet_update(&csum, (const uint8_t*)chunk_value, chunk_size)); - break; - case arg_csum_chunk_const: - if (chunk_size != 2 && chunk_size != 4 && chunk_size != 8) { - fail("bad checksum const chunk size %lld\n", chunk_size); - } - // Here we assume that const values come to us big endian. - debug("#%d: const chunk, value: %llx, size: %llu\n", chunk, chunk_value, chunk_size); - csum_inet_update(&csum, (const uint8_t*)&chunk_value, chunk_size); - break; - default: - fail("bad checksum chunk kind %lu", chunk_kind); - } - } - int16_t csum_value = csum_inet_digest(&csum); - debug("writing inet checksum %hx to %llx\n", csum_value, csum_addr); - NONFAILING(copyin(csum_addr, csum_value, 2, 0, 0)); - break; - } - default: - fail("bad checksum kind %lu", csum_kind); - } - break; - } - default: - fail("bad argument type %lu", typ); - } - continue; - } - if (call_num == instr_copyout) { - read_input(&input_pos); // addr - read_input(&input_pos); // size - // The copyout will happen when/if the call completes. - continue; - } - - // Normal syscall. - if (call_num >= sizeof(syscalls) / sizeof(syscalls[0])) - fail("invalid command number %lu", call_num); - uint64_t num_args = read_input(&input_pos); - if (num_args > kMaxArgs) - fail("command has bad number of arguments %lu", num_args); - uint64_t args[kMaxArgs] = {}; - for (uint64_t i = 0; i < num_args; i++) - args[i] = read_arg(&input_pos); - for (uint64_t i = num_args; i < 6; i++) - args[i] = 0; - thread_t* th = schedule_call(n, call_index++, call_num, num_args, args, input_pos); - - if (collide && (call_index % 2) == 0) { - // Don't wait for every other call. - // We already have results from the previous execution. - } else if (flag_threaded) { - // Wait for call completion. - uint64_t start = current_time_ms(); - uint64_t now = start; - // Note: sys knows about this 20ms timeout when it generates - // timespec/timeval values. - const uint64_t timeout_ms = flag_debug ? 500 : 20; - for (;;) { - timespec ts = {}; - ts.tv_sec = 0; - ts.tv_nsec = (timeout_ms - (now - start)) * 1000 * 1000; - syscall(SYS_futex, &th->done, FUTEX_WAIT, 0, &ts); - if (__atomic_load_n(&th->done, __ATOMIC_RELAXED)) - break; - now = current_time_ms(); - if (now - start > 20) - break; - } - if (__atomic_load_n(&th->done, __ATOMIC_ACQUIRE)) - handle_completion(th); - // Check if any of previous calls have completed. - // Give them some additional time, because they could have been - // just unblocked by the current call. - if (running < 0) - fail("running = %d", running); - if (running > 0) { - bool last = read_input(&input_pos, true) == instr_eof; - usleep(last ? 1000 : 100); - for (int i = 0; i < kMaxThreads; i++) { - th = &threads[i]; - if (__atomic_load_n(&th->done, __ATOMIC_ACQUIRE) && !th->handled) - handle_completion(th); - } - } - } else { - // Execute directly. - if (th != &threads[0]) - fail("using non-main thread in non-thread mode"); - execute_call(th); - handle_completion(th); - } - } - - if (flag_collide && !flag_inject_fault && !collide) { - debug("enabling collider\n"); - collide = true; - goto retry; - } -} - -thread_t* schedule_call(int n, int call_index, int call_num, uint64_t num_args, uint64_t* args, uint64_t* pos) -{ - // Find a spare thread to execute the call. - int i; - for (i = 0; i < kMaxThreads; i++) { - thread_t* th = &threads[i]; - if (!th->created) - thread_create(th, i); - if (__atomic_load_n(&th->done, __ATOMIC_ACQUIRE)) { - if (!th->handled) - handle_completion(th); - break; - } - } - if (i == kMaxThreads) - exitf("out of threads"); - thread_t* th = &threads[i]; - debug("scheduling call %d [%s] on thread %d\n", call_index, syscalls[call_num].name, th->id); - if (th->ready || !th->done || !th->handled) - fail("bad thread state in schedule: ready=%d done=%d handled=%d", th->ready, th->done, th->handled); - th->copyout_pos = pos; - th->done = false; - th->handled = false; - th->call_n = n; - th->call_index = call_index; - th->call_num = call_num; - th->num_args = num_args; - for (int i = 0; i < kMaxArgs; i++) - th->args[i] = args[i]; - __atomic_store_n(&th->ready, 1, __ATOMIC_RELEASE); - syscall(SYS_futex, &th->ready, FUTEX_WAKE); - running++; - return th; -} - -void handle_completion(thread_t* th) -{ - debug("completion of call %d [%s] on thread %d\n", th->call_index, syscalls[th->call_num].name, th->id); - if (th->ready || !th->done || th->handled) - fail("bad thread state in completion: ready=%d done=%d handled=%d", - th->ready, th->done, th->handled); - if (th->res != (uintptr_t)-1) { - if (th->call_n >= kMaxCommands) - fail("result idx %ld overflows kMaxCommands", th->call_n); - results[th->call_n].executed = true; - results[th->call_n].val = th->res; - for (bool done = false; !done;) { - th->call_n++; - uint64_t call_num = read_input(&th->copyout_pos); - switch (call_num) { - case instr_copyout: { - char* addr = (char*)read_input(&th->copyout_pos); - uint64_t size = read_input(&th->copyout_pos); - uint64_t val = copyout(addr, size); - if (th->call_n >= kMaxCommands) - fail("result idx %ld overflows kMaxCommands", th->call_n); - results[th->call_n].executed = true; - results[th->call_n].val = val; - debug("copyout from %p\n", addr); - break; - } - default: - done = true; - break; - } - } - } - if (!collide) { - write_output(th->call_index); - write_output(th->call_num); - uint32_t reserrno = th->res != (uint32_t)-1 ? 0 : th->reserrno; - write_output(reserrno); - write_output(th->fault_injected); - uint32_t* signal_count_pos = write_output(0); // filled in later - uint32_t* cover_count_pos = write_output(0); // filled in later - uint32_t* comps_count_pos = write_output(0); // filled in later - uint32_t nsig = 0, cover_size = 0, comps_size = 0; - - if (flag_collect_comps) { - // Collect only the comparisons - comps_size = th->cover_size; - kcov_comparison_t* start = (kcov_comparison_t*)th->cover_data; - kcov_comparison_t* end = start + comps_size; - std::sort(start, end); - comps_size = std::unique(start, end) - start; - for (uint32_t i = 0; i < comps_size; ++i) - start[i].write(write_output); - } else { - // Write out feedback signals. - // Currently it is code edges computed as xor of - // two subsequent basic block PCs. - uint32_t prev = 0; - for (uint32_t i = 0; i < th->cover_size; i++) { - uint32_t pc = (uint32_t)th->cover_data[i]; - uint32_t sig = pc ^ prev; - prev = hash(pc); - if (dedup(sig)) - continue; - write_output(sig); - nsig++; - } - if (flag_collect_cover) { - // Write out real coverage (basic block PCs). - cover_size = th->cover_size; - if (flag_dedup_cover) { - uint64_t* start = (uint64_t*)th->cover_data; - uint64_t* end = start + cover_size; - std::sort(start, end); - cover_size = std::unique(start, end) - start; - } - // Truncate PCs to uint32_t assuming that they fit into 32-bits. - // True for x86_64 and arm64 without KASLR. - for (uint32_t i = 0; i < cover_size; i++) - write_output((uint32_t)th->cover_data[i]); - } - } - // Write out real coverage (basic block PCs). - *cover_count_pos = cover_size; - // Write out number of comparisons - *comps_count_pos = comps_size; - // Write out number of signals - *signal_count_pos = nsig; - debug("out #%u: index=%u num=%u errno=%d sig=%u cover=%u comps=%u\n", - completed, th->call_index, th->call_num, reserrno, nsig, - cover_size, comps_size); - completed++; - __atomic_store_n(output_data, completed, __ATOMIC_RELEASE); - } - th->handled = true; - running--; -} - -void thread_create(thread_t* th, int id) -{ - th->created = true; - th->id = id; - th->done = true; - th->handled = true; - if (flag_threaded) { - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setstacksize(&attr, 128 << 10); - if (pthread_create(&th->th, &attr, worker_thread, th)) - exitf("pthread_create failed"); - pthread_attr_destroy(&attr); - } -} - -void* worker_thread(void* arg) +long execute_syscall(call_t* c, long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { - thread_t* th = (thread_t*)arg; - - cover_enable(th); - for (;;) { - while (!__atomic_load_n(&th->ready, __ATOMIC_ACQUIRE)) - syscall(SYS_futex, &th->ready, FUTEX_WAIT, 0, 0); - execute_call(th); - } - return 0; -} - -void execute_call(thread_t* th) -{ - th->ready = false; - call_t* call = &syscalls[th->call_num]; - debug("#%d: %s(", th->id, call->name); - for (int i = 0; i < th->num_args; i++) { - if (i != 0) - debug(", "); - debug("0x%lx", th->args[i]); - } - debug(")\n"); - - int fail_fd = -1; - if (flag_inject_fault && th->call_index == flag_fault_call) { - if (collide) - fail("both collide and fault injection are enabled"); - debug("injecting fault into %d-th operation\n", flag_fault_nth); - fail_fd = inject_fault(flag_fault_nth); - } - - cover_reset(th); - th->res = execute_syscall(call->sys_nr, th->args[0], th->args[1], - th->args[2], th->args[3], th->args[4], th->args[5], - th->args[6], th->args[7], th->args[8]); - th->reserrno = errno; - th->cover_size = read_cover_size(th); - th->fault_injected = false; - - if (flag_inject_fault && th->call_index == flag_fault_call) { - char buf[16]; - int n = read(fail_fd, buf, sizeof(buf) - 1); - if (n <= 0) - fail("failed to read /proc/self/task/tid/fail-nth"); - th->fault_injected = n == 2 && buf[0] == '0' && buf[1] == '\n'; - buf[0] = '0'; - if (write(fail_fd, buf, 1) != 1) - fail("failed to write /proc/self/task/tid/fail-nth"); - close(fail_fd); - debug("fault injected: %d\n", th->fault_injected); - } - - if (th->res == (uint32_t)-1) - debug("#%d: %s = errno(%d)\n", th->id, call->name, th->reserrno); - else - debug("#%d: %s = 0x%lx\n", th->id, call->name, th->res); - __atomic_store_n(&th->done, 1, __ATOMIC_RELEASE); - syscall(SYS_futex, &th->done, FUTEX_WAKE); + if (c->call) + return c->call(a0, a1, a2, a3, a4, a5, a6, a7, a8); + return syscall(c->sys_nr, a0, a1, a2, a3, a4, a5); } void cover_open() @@ -866,136 +313,63 @@ uint64_t read_cover_size(thread_t* th) return n; } -static uint32_t hash(uint32_t a) +uint32_t* write_output(uint32_t v) { - a = (a ^ 61) ^ (a >> 16); - a = a + (a << 3); - a = a ^ (a >> 4); - a = a * 0x27d4eb2d; - a = a ^ (a >> 15); - return a; + if (collide) + return 0; + if (output_pos < output_data || (char*)output_pos >= (char*)output_data + kMaxOutput) + fail("output overflow"); + *output_pos = v; + return output_pos++; } -const uint32_t dedup_table_size = 8 << 10; -uint32_t dedup_table[dedup_table_size]; - -// Poorman's best-effort hashmap-based deduplication. -// The hashmap is global which means that we deduplicate across different calls. -// This is OK because we are interested only in new signals. -static bool dedup(uint32_t sig) +void write_completed(uint32_t completed) { - for (uint32_t i = 0; i < 4; i++) { - uint32_t pos = (sig + i) % dedup_table_size; - if (dedup_table[pos] == sig) - return true; - if (dedup_table[pos] == 0) { - dedup_table[pos] = sig; - return false; - } - } - dedup_table[sig % dedup_table_size] = sig; - return false; + __atomic_store_n(output_data, completed, __ATOMIC_RELEASE); } -void copyin(char* addr, uint64_t val, uint64_t size, uint64_t bf_off, uint64_t bf_len) +void event_init(event_t* ev) { - NONFAILING(switch (size) { - case 1: - STORE_BY_BITMASK(uint8_t, addr, val, bf_off, bf_len); - break; - case 2: - STORE_BY_BITMASK(uint16_t, addr, val, bf_off, bf_len); - break; - case 4: - STORE_BY_BITMASK(uint32_t, addr, val, bf_off, bf_len); - break; - case 8: - STORE_BY_BITMASK(uint64_t, addr, val, bf_off, bf_len); - break; - default: - fail("copyin: bad argument size %lu", size); - }); + ev->state = 0; } -uint64_t copyout(char* addr, uint64_t size) +void event_reset(event_t* ev) { - uint64_t res = default_value; - NONFAILING(switch (size) { - case 1: - res = *(uint8_t*)addr; - break; - case 2: - res = *(uint16_t*)addr; - break; - case 4: - res = *(uint32_t*)addr; - break; - case 8: - res = *(uint64_t*)addr; - break; - default: - fail("copyout: bad argument size %lu", size); - }); - return res; + ev->state = 0; } -uint64_t read_arg(uint64_t** input_posp) +void event_set(event_t* ev) { - uint64_t typ = read_input(input_posp); - uint64_t size = read_input(input_posp); - (void)size; - uint64_t arg = 0; - switch (typ) { - case arg_const: { - arg = read_input(input_posp); - // Bitfields can't be args of a normal syscall, so just ignore them. - read_input(input_posp); // bit field offset - read_input(input_posp); // bit field length - break; - } - case arg_result: { - arg = read_result(input_posp); - break; - } - default: - fail("bad argument type %lu", typ); - } - return arg; + if (ev->state) + fail("event already set"); + __atomic_store_n(&ev->state, 1, __ATOMIC_RELEASE); + syscall(SYS_futex, &ev->state, FUTEX_WAKE); } -uint64_t read_result(uint64_t** input_posp) +void event_wait(event_t* ev) { - uint64_t idx = read_input(input_posp); - uint64_t op_div = read_input(input_posp); - uint64_t op_add = read_input(input_posp); - if (idx >= kMaxCommands) - fail("command refers to bad result %ld", idx); - uint64_t arg = default_value; - if (results[idx].executed) { - arg = results[idx].val; - if (op_div != 0) - arg = arg / op_div; - arg += op_add; - } - return arg; + while (!__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE)) + syscall(SYS_futex, &ev->state, FUTEX_WAIT, 0, 0); } -uint64_t read_input(uint64_t** input_posp, bool peek) +bool event_isset(event_t* ev) { - uint64_t* input_pos = *input_posp; - if ((char*)input_pos >= input_data + kMaxInput) - fail("input command overflows input"); - if (!peek) - *input_posp = input_pos + 1; - return *input_pos; + return __atomic_load_n(&ev->state, __ATOMIC_ACQUIRE); } -uint32_t* write_output(uint32_t v) +bool event_timedwait(event_t* ev, uint64_t timeout_ms) { - if (collide) - return 0; - if (output_pos < output_data || (char*)output_pos >= (char*)output_data + kMaxOutput) - fail("output overflow"); - *output_pos = v; - return output_pos++; + uint64_t start = current_time_ms(); + uint64_t now = start; + for (;;) { + timespec ts = {}; + ts.tv_sec = 0; + ts.tv_nsec = (timeout_ms - (now - start)) * 1000 * 1000; + syscall(SYS_futex, &ev->state, FUTEX_WAIT, 0, &ts); + if (__atomic_load_n(&ev->state, __ATOMIC_RELAXED)) + return true; + now = current_time_ms(); + if (now - start > timeout_ms) + return false; + } } diff --git a/executor/syscalls_fuchsia.h b/executor/syscalls_fuchsia.h index 6919aec79..f64ef023c 100644 --- a/executor/syscalls_fuchsia.h +++ b/executor/syscalls_fuchsia.h @@ -1,26 +1,33 @@ // AUTOGENERATED FILE -struct call_t { - const char* name; - int sys_nr; -}; - #if defined(__x86_64__) || 0 #define GOARCH "amd64" -#define SYZ_REVISION "d65b9adb4853817be6f471df44fc8347ebf6dfc6" +#define SYZ_REVISION "640bc9ab193d2488d41793c10c5a44bab3ad33f9" +#define __NR_syz_mmap 1000000 -static call_t syscalls[] = { - {"mx_time_get", 0}, +unsigned syscall_count = 5; +call_t syscalls[] = { + {"syz_mmap", 1000000, (syscall_t)syz_mmap}, + {"zx_handle_close", 0, (syscall_t)zx_handle_close}, + {"zx_handle_duplicate", 0, (syscall_t)zx_handle_duplicate}, + {"zx_handle_replace", 0, (syscall_t)zx_handle_replace}, + {"zx_time_get", 0, (syscall_t)zx_time_get}, }; #endif #if defined(__aarch64__) || 0 #define GOARCH "arm64" -#define SYZ_REVISION "bd2655e6d85f1fecceb1648649e1ad5adda81dc8" +#define SYZ_REVISION "8c364809f3ccf951f9c9ac974a817dfaf8d1713c" +#define __NR_syz_mmap 1000000 -static call_t syscalls[] = { - {"mx_time_get", 0}, +unsigned syscall_count = 5; +call_t syscalls[] = { + {"syz_mmap", 1000000, (syscall_t)syz_mmap}, + {"zx_handle_close", 0, (syscall_t)zx_handle_close}, + {"zx_handle_duplicate", 0, (syscall_t)zx_handle_duplicate}, + {"zx_handle_replace", 0, (syscall_t)zx_handle_replace}, + {"zx_time_get", 0, (syscall_t)zx_time_get}, }; #endif diff --git a/executor/syscalls_linux.h b/executor/syscalls_linux.h index 48ea44e1c..b30807290 100644 --- a/executor/syscalls_linux.h +++ b/executor/syscalls_linux.h @@ -1,10 +1,5 @@ // AUTOGENERATED FILE -struct call_t { - const char* name; - int sys_nr; -}; - #if defined(__i386__) || 0 #define GOARCH "386" #define SYZ_REVISION "7b2f1949d48094cf3369932d0743db166049457b" @@ -17,7 +12,8 @@ struct call_t { #define __NR_syz_open_pts 1000006 #define __NR_syz_test 1000007 -static call_t syscalls[] = { +unsigned syscall_count = 1481; +call_t syscalls[] = { {"accept4", 364}, {"accept4$ax25", 364}, {"accept4$inet", 364}, @@ -1351,109 +1347,109 @@ static call_t syscalls[] = { {"sysfs$3", 135}, {"sysinfo", 116}, {"syslog", 103}, - {"syz_emit_ethernet", 1000000}, - {"syz_extract_tcp_res", 1000001}, - {"syz_extract_tcp_res$synack", 1000001}, - {"syz_fuse_mount", 1000002}, - {"syz_fuseblk_mount", 1000003}, - {"syz_kvm_setup_cpu$arm64", 1000004}, - {"syz_kvm_setup_cpu$x86", 1000004}, - {"syz_open_dev$admmidi", 1000005}, - {"syz_open_dev$adsp", 1000005}, - {"syz_open_dev$amidi", 1000005}, - {"syz_open_dev$audion", 1000005}, - {"syz_open_dev$dmmidi", 1000005}, - {"syz_open_dev$dri", 1000005}, - {"syz_open_dev$dricontrol", 1000005}, - {"syz_open_dev$drirender", 1000005}, - {"syz_open_dev$dspn", 1000005}, - {"syz_open_dev$evdev", 1000005}, - {"syz_open_dev$floppy", 1000005}, - {"syz_open_dev$ircomm", 1000005}, - {"syz_open_dev$loop", 1000005}, - {"syz_open_dev$mice", 1000005}, - {"syz_open_dev$midi", 1000005}, - {"syz_open_dev$mouse", 1000005}, - {"syz_open_dev$random", 1000005}, - {"syz_open_dev$sg", 1000005}, - {"syz_open_dev$sndctrl", 1000005}, - {"syz_open_dev$sndhw", 1000005}, - {"syz_open_dev$sndmidi", 1000005}, - {"syz_open_dev$sndpcmc", 1000005}, - {"syz_open_dev$sndpcmp", 1000005}, - {"syz_open_dev$sndseq", 1000005}, - {"syz_open_dev$sndtimer", 1000005}, - {"syz_open_dev$tlk_device", 1000005}, - {"syz_open_dev$tun", 1000005}, - {"syz_open_dev$urandom", 1000005}, - {"syz_open_dev$usb", 1000005}, - {"syz_open_dev$usbmon", 1000005}, - {"syz_open_dev$vcsa", 1000005}, - {"syz_open_dev$vcsn", 1000005}, - {"syz_open_pts", 1000006}, - {"syz_test", 1000007}, - {"syz_test$align0", 1000007}, - {"syz_test$align1", 1000007}, - {"syz_test$align2", 1000007}, - {"syz_test$align3", 1000007}, - {"syz_test$align4", 1000007}, - {"syz_test$align5", 1000007}, - {"syz_test$align6", 1000007}, - {"syz_test$array0", 1000007}, - {"syz_test$array1", 1000007}, - {"syz_test$array2", 1000007}, - {"syz_test$bf0", 1000007}, - {"syz_test$bf1", 1000007}, - {"syz_test$csum_encode", 1000007}, - {"syz_test$csum_ipv4", 1000007}, - {"syz_test$csum_ipv4_tcp", 1000007}, - {"syz_test$csum_ipv4_udp", 1000007}, - {"syz_test$csum_ipv6_icmp", 1000007}, - {"syz_test$csum_ipv6_tcp", 1000007}, - {"syz_test$csum_ipv6_udp", 1000007}, - {"syz_test$end0", 1000007}, - {"syz_test$end1", 1000007}, - {"syz_test$int", 1000007}, - {"syz_test$length0", 1000007}, - {"syz_test$length1", 1000007}, - {"syz_test$length10", 1000007}, - {"syz_test$length11", 1000007}, - {"syz_test$length12", 1000007}, - {"syz_test$length13", 1000007}, - {"syz_test$length14", 1000007}, - {"syz_test$length15", 1000007}, - {"syz_test$length16", 1000007}, - {"syz_test$length17", 1000007}, - {"syz_test$length18", 1000007}, - {"syz_test$length19", 1000007}, - {"syz_test$length2", 1000007}, - {"syz_test$length20", 1000007}, - {"syz_test$length3", 1000007}, - {"syz_test$length4", 1000007}, - {"syz_test$length5", 1000007}, - {"syz_test$length6", 1000007}, - {"syz_test$length7", 1000007}, - {"syz_test$length8", 1000007}, - {"syz_test$length9", 1000007}, - {"syz_test$missing_resource", 1000007}, - {"syz_test$opt0", 1000007}, - {"syz_test$opt1", 1000007}, - {"syz_test$opt2", 1000007}, - {"syz_test$recur0", 1000007}, - {"syz_test$recur1", 1000007}, - {"syz_test$recur2", 1000007}, - {"syz_test$regression0", 1000007}, - {"syz_test$res0", 1000007}, - {"syz_test$res1", 1000007}, - {"syz_test$struct", 1000007}, - {"syz_test$text_x86_16", 1000007}, - {"syz_test$text_x86_32", 1000007}, - {"syz_test$text_x86_64", 1000007}, - {"syz_test$text_x86_real", 1000007}, - {"syz_test$union0", 1000007}, - {"syz_test$union1", 1000007}, - {"syz_test$union2", 1000007}, - {"syz_test$vma0", 1000007}, + {"syz_emit_ethernet", 1000000, (syscall_t)syz_emit_ethernet}, + {"syz_extract_tcp_res", 1000001, (syscall_t)syz_extract_tcp_res}, + {"syz_extract_tcp_res$synack", 1000001, (syscall_t)syz_extract_tcp_res}, + {"syz_fuse_mount", 1000002, (syscall_t)syz_fuse_mount}, + {"syz_fuseblk_mount", 1000003, (syscall_t)syz_fuseblk_mount}, + {"syz_kvm_setup_cpu$arm64", 1000004, (syscall_t)syz_kvm_setup_cpu}, + {"syz_kvm_setup_cpu$x86", 1000004, (syscall_t)syz_kvm_setup_cpu}, + {"syz_open_dev$admmidi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$adsp", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$amidi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$audion", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$dmmidi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$dri", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$dricontrol", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$drirender", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$dspn", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$evdev", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$floppy", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$ircomm", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$loop", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$mice", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$midi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$mouse", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$random", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sg", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndctrl", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndhw", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndmidi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndpcmc", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndpcmp", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndseq", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndtimer", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$tlk_device", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$tun", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$urandom", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$usb", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$usbmon", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$vcsa", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$vcsn", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_pts", 1000006, (syscall_t)syz_open_pts}, + {"syz_test", 1000007, (syscall_t)syz_test}, + {"syz_test$align0", 1000007, (syscall_t)syz_test}, + {"syz_test$align1", 1000007, (syscall_t)syz_test}, + {"syz_test$align2", 1000007, (syscall_t)syz_test}, + {"syz_test$align3", 1000007, (syscall_t)syz_test}, + {"syz_test$align4", 1000007, (syscall_t)syz_test}, + {"syz_test$align5", 1000007, (syscall_t)syz_test}, + {"syz_test$align6", 1000007, (syscall_t)syz_test}, + {"syz_test$array0", 1000007, (syscall_t)syz_test}, + {"syz_test$array1", 1000007, (syscall_t)syz_test}, + {"syz_test$array2", 1000007, (syscall_t)syz_test}, + {"syz_test$bf0", 1000007, (syscall_t)syz_test}, + {"syz_test$bf1", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_encode", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv4", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv4_tcp", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv4_udp", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv6_icmp", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv6_tcp", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv6_udp", 1000007, (syscall_t)syz_test}, + {"syz_test$end0", 1000007, (syscall_t)syz_test}, + {"syz_test$end1", 1000007, (syscall_t)syz_test}, + {"syz_test$int", 1000007, (syscall_t)syz_test}, + {"syz_test$length0", 1000007, (syscall_t)syz_test}, + {"syz_test$length1", 1000007, (syscall_t)syz_test}, + {"syz_test$length10", 1000007, (syscall_t)syz_test}, + {"syz_test$length11", 1000007, (syscall_t)syz_test}, + {"syz_test$length12", 1000007, (syscall_t)syz_test}, + {"syz_test$length13", 1000007, (syscall_t)syz_test}, + {"syz_test$length14", 1000007, (syscall_t)syz_test}, + {"syz_test$length15", 1000007, (syscall_t)syz_test}, + {"syz_test$length16", 1000007, (syscall_t)syz_test}, + {"syz_test$length17", 1000007, (syscall_t)syz_test}, + {"syz_test$length18", 1000007, (syscall_t)syz_test}, + {"syz_test$length19", 1000007, (syscall_t)syz_test}, + {"syz_test$length2", 1000007, (syscall_t)syz_test}, + {"syz_test$length20", 1000007, (syscall_t)syz_test}, + {"syz_test$length3", 1000007, (syscall_t)syz_test}, + {"syz_test$length4", 1000007, (syscall_t)syz_test}, + {"syz_test$length5", 1000007, (syscall_t)syz_test}, + {"syz_test$length6", 1000007, (syscall_t)syz_test}, + {"syz_test$length7", 1000007, (syscall_t)syz_test}, + {"syz_test$length8", 1000007, (syscall_t)syz_test}, + {"syz_test$length9", 1000007, (syscall_t)syz_test}, + {"syz_test$missing_resource", 1000007, (syscall_t)syz_test}, + {"syz_test$opt0", 1000007, (syscall_t)syz_test}, + {"syz_test$opt1", 1000007, (syscall_t)syz_test}, + {"syz_test$opt2", 1000007, (syscall_t)syz_test}, + {"syz_test$recur0", 1000007, (syscall_t)syz_test}, + {"syz_test$recur1", 1000007, (syscall_t)syz_test}, + {"syz_test$recur2", 1000007, (syscall_t)syz_test}, + {"syz_test$regression0", 1000007, (syscall_t)syz_test}, + {"syz_test$res0", 1000007, (syscall_t)syz_test}, + {"syz_test$res1", 1000007, (syscall_t)syz_test}, + {"syz_test$struct", 1000007, (syscall_t)syz_test}, + {"syz_test$text_x86_16", 1000007, (syscall_t)syz_test}, + {"syz_test$text_x86_32", 1000007, (syscall_t)syz_test}, + {"syz_test$text_x86_64", 1000007, (syscall_t)syz_test}, + {"syz_test$text_x86_real", 1000007, (syscall_t)syz_test}, + {"syz_test$union0", 1000007, (syscall_t)syz_test}, + {"syz_test$union1", 1000007, (syscall_t)syz_test}, + {"syz_test$union2", 1000007, (syscall_t)syz_test}, + {"syz_test$vma0", 1000007, (syscall_t)syz_test}, {"tee", 315}, {"tgkill", 270}, {"time", 13}, @@ -1515,7 +1511,8 @@ static call_t syscalls[] = { #define __NR_syz_open_pts 1000006 #define __NR_syz_test 1000007 -static call_t syscalls[] = { +unsigned syscall_count = 1542; +call_t syscalls[] = { {"accept", 43}, {"accept$alg", 43}, {"accept$ax25", 43}, @@ -2910,109 +2907,109 @@ static call_t syscalls[] = { {"sysfs$3", 139}, {"sysinfo", 99}, {"syslog", 103}, - {"syz_emit_ethernet", 1000000}, - {"syz_extract_tcp_res", 1000001}, - {"syz_extract_tcp_res$synack", 1000001}, - {"syz_fuse_mount", 1000002}, - {"syz_fuseblk_mount", 1000003}, - {"syz_kvm_setup_cpu$arm64", 1000004}, - {"syz_kvm_setup_cpu$x86", 1000004}, - {"syz_open_dev$admmidi", 1000005}, - {"syz_open_dev$adsp", 1000005}, - {"syz_open_dev$amidi", 1000005}, - {"syz_open_dev$audion", 1000005}, - {"syz_open_dev$dmmidi", 1000005}, - {"syz_open_dev$dri", 1000005}, - {"syz_open_dev$dricontrol", 1000005}, - {"syz_open_dev$drirender", 1000005}, - {"syz_open_dev$dspn", 1000005}, - {"syz_open_dev$evdev", 1000005}, - {"syz_open_dev$floppy", 1000005}, - {"syz_open_dev$ircomm", 1000005}, - {"syz_open_dev$loop", 1000005}, - {"syz_open_dev$mice", 1000005}, - {"syz_open_dev$midi", 1000005}, - {"syz_open_dev$mouse", 1000005}, - {"syz_open_dev$random", 1000005}, - {"syz_open_dev$sg", 1000005}, - {"syz_open_dev$sndctrl", 1000005}, - {"syz_open_dev$sndhw", 1000005}, - {"syz_open_dev$sndmidi", 1000005}, - {"syz_open_dev$sndpcmc", 1000005}, - {"syz_open_dev$sndpcmp", 1000005}, - {"syz_open_dev$sndseq", 1000005}, - {"syz_open_dev$sndtimer", 1000005}, - {"syz_open_dev$tlk_device", 1000005}, - {"syz_open_dev$tun", 1000005}, - {"syz_open_dev$urandom", 1000005}, - {"syz_open_dev$usb", 1000005}, - {"syz_open_dev$usbmon", 1000005}, - {"syz_open_dev$vcsa", 1000005}, - {"syz_open_dev$vcsn", 1000005}, - {"syz_open_pts", 1000006}, - {"syz_test", 1000007}, - {"syz_test$align0", 1000007}, - {"syz_test$align1", 1000007}, - {"syz_test$align2", 1000007}, - {"syz_test$align3", 1000007}, - {"syz_test$align4", 1000007}, - {"syz_test$align5", 1000007}, - {"syz_test$align6", 1000007}, - {"syz_test$array0", 1000007}, - {"syz_test$array1", 1000007}, - {"syz_test$array2", 1000007}, - {"syz_test$bf0", 1000007}, - {"syz_test$bf1", 1000007}, - {"syz_test$csum_encode", 1000007}, - {"syz_test$csum_ipv4", 1000007}, - {"syz_test$csum_ipv4_tcp", 1000007}, - {"syz_test$csum_ipv4_udp", 1000007}, - {"syz_test$csum_ipv6_icmp", 1000007}, - {"syz_test$csum_ipv6_tcp", 1000007}, - {"syz_test$csum_ipv6_udp", 1000007}, - {"syz_test$end0", 1000007}, - {"syz_test$end1", 1000007}, - {"syz_test$int", 1000007}, - {"syz_test$length0", 1000007}, - {"syz_test$length1", 1000007}, - {"syz_test$length10", 1000007}, - {"syz_test$length11", 1000007}, - {"syz_test$length12", 1000007}, - {"syz_test$length13", 1000007}, - {"syz_test$length14", 1000007}, - {"syz_test$length15", 1000007}, - {"syz_test$length16", 1000007}, - {"syz_test$length17", 1000007}, - {"syz_test$length18", 1000007}, - {"syz_test$length19", 1000007}, - {"syz_test$length2", 1000007}, - {"syz_test$length20", 1000007}, - {"syz_test$length3", 1000007}, - {"syz_test$length4", 1000007}, - {"syz_test$length5", 1000007}, - {"syz_test$length6", 1000007}, - {"syz_test$length7", 1000007}, - {"syz_test$length8", 1000007}, - {"syz_test$length9", 1000007}, - {"syz_test$missing_resource", 1000007}, - {"syz_test$opt0", 1000007}, - {"syz_test$opt1", 1000007}, - {"syz_test$opt2", 1000007}, - {"syz_test$recur0", 1000007}, - {"syz_test$recur1", 1000007}, - {"syz_test$recur2", 1000007}, - {"syz_test$regression0", 1000007}, - {"syz_test$res0", 1000007}, - {"syz_test$res1", 1000007}, - {"syz_test$struct", 1000007}, - {"syz_test$text_x86_16", 1000007}, - {"syz_test$text_x86_32", 1000007}, - {"syz_test$text_x86_64", 1000007}, - {"syz_test$text_x86_real", 1000007}, - {"syz_test$union0", 1000007}, - {"syz_test$union1", 1000007}, - {"syz_test$union2", 1000007}, - {"syz_test$vma0", 1000007}, + {"syz_emit_ethernet", 1000000, (syscall_t)syz_emit_ethernet}, + {"syz_extract_tcp_res", 1000001, (syscall_t)syz_extract_tcp_res}, + {"syz_extract_tcp_res$synack", 1000001, (syscall_t)syz_extract_tcp_res}, + {"syz_fuse_mount", 1000002, (syscall_t)syz_fuse_mount}, + {"syz_fuseblk_mount", 1000003, (syscall_t)syz_fuseblk_mount}, + {"syz_kvm_setup_cpu$arm64", 1000004, (syscall_t)syz_kvm_setup_cpu}, + {"syz_kvm_setup_cpu$x86", 1000004, (syscall_t)syz_kvm_setup_cpu}, + {"syz_open_dev$admmidi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$adsp", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$amidi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$audion", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$dmmidi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$dri", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$dricontrol", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$drirender", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$dspn", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$evdev", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$floppy", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$ircomm", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$loop", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$mice", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$midi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$mouse", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$random", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sg", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndctrl", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndhw", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndmidi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndpcmc", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndpcmp", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndseq", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndtimer", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$tlk_device", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$tun", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$urandom", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$usb", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$usbmon", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$vcsa", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$vcsn", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_pts", 1000006, (syscall_t)syz_open_pts}, + {"syz_test", 1000007, (syscall_t)syz_test}, + {"syz_test$align0", 1000007, (syscall_t)syz_test}, + {"syz_test$align1", 1000007, (syscall_t)syz_test}, + {"syz_test$align2", 1000007, (syscall_t)syz_test}, + {"syz_test$align3", 1000007, (syscall_t)syz_test}, + {"syz_test$align4", 1000007, (syscall_t)syz_test}, + {"syz_test$align5", 1000007, (syscall_t)syz_test}, + {"syz_test$align6", 1000007, (syscall_t)syz_test}, + {"syz_test$array0", 1000007, (syscall_t)syz_test}, + {"syz_test$array1", 1000007, (syscall_t)syz_test}, + {"syz_test$array2", 1000007, (syscall_t)syz_test}, + {"syz_test$bf0", 1000007, (syscall_t)syz_test}, + {"syz_test$bf1", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_encode", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv4", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv4_tcp", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv4_udp", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv6_icmp", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv6_tcp", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv6_udp", 1000007, (syscall_t)syz_test}, + {"syz_test$end0", 1000007, (syscall_t)syz_test}, + {"syz_test$end1", 1000007, (syscall_t)syz_test}, + {"syz_test$int", 1000007, (syscall_t)syz_test}, + {"syz_test$length0", 1000007, (syscall_t)syz_test}, + {"syz_test$length1", 1000007, (syscall_t)syz_test}, + {"syz_test$length10", 1000007, (syscall_t)syz_test}, + {"syz_test$length11", 1000007, (syscall_t)syz_test}, + {"syz_test$length12", 1000007, (syscall_t)syz_test}, + {"syz_test$length13", 1000007, (syscall_t)syz_test}, + {"syz_test$length14", 1000007, (syscall_t)syz_test}, + {"syz_test$length15", 1000007, (syscall_t)syz_test}, + {"syz_test$length16", 1000007, (syscall_t)syz_test}, + {"syz_test$length17", 1000007, (syscall_t)syz_test}, + {"syz_test$length18", 1000007, (syscall_t)syz_test}, + {"syz_test$length19", 1000007, (syscall_t)syz_test}, + {"syz_test$length2", 1000007, (syscall_t)syz_test}, + {"syz_test$length20", 1000007, (syscall_t)syz_test}, + {"syz_test$length3", 1000007, (syscall_t)syz_test}, + {"syz_test$length4", 1000007, (syscall_t)syz_test}, + {"syz_test$length5", 1000007, (syscall_t)syz_test}, + {"syz_test$length6", 1000007, (syscall_t)syz_test}, + {"syz_test$length7", 1000007, (syscall_t)syz_test}, + {"syz_test$length8", 1000007, (syscall_t)syz_test}, + {"syz_test$length9", 1000007, (syscall_t)syz_test}, + {"syz_test$missing_resource", 1000007, (syscall_t)syz_test}, + {"syz_test$opt0", 1000007, (syscall_t)syz_test}, + {"syz_test$opt1", 1000007, (syscall_t)syz_test}, + {"syz_test$opt2", 1000007, (syscall_t)syz_test}, + {"syz_test$recur0", 1000007, (syscall_t)syz_test}, + {"syz_test$recur1", 1000007, (syscall_t)syz_test}, + {"syz_test$recur2", 1000007, (syscall_t)syz_test}, + {"syz_test$regression0", 1000007, (syscall_t)syz_test}, + {"syz_test$res0", 1000007, (syscall_t)syz_test}, + {"syz_test$res1", 1000007, (syscall_t)syz_test}, + {"syz_test$struct", 1000007, (syscall_t)syz_test}, + {"syz_test$text_x86_16", 1000007, (syscall_t)syz_test}, + {"syz_test$text_x86_32", 1000007, (syscall_t)syz_test}, + {"syz_test$text_x86_64", 1000007, (syscall_t)syz_test}, + {"syz_test$text_x86_real", 1000007, (syscall_t)syz_test}, + {"syz_test$union0", 1000007, (syscall_t)syz_test}, + {"syz_test$union1", 1000007, (syscall_t)syz_test}, + {"syz_test$union2", 1000007, (syscall_t)syz_test}, + {"syz_test$vma0", 1000007, (syscall_t)syz_test}, {"tee", 276}, {"tgkill", 234}, {"time", 201}, @@ -3074,7 +3071,8 @@ static call_t syscalls[] = { #define __NR_syz_open_pts 1000006 #define __NR_syz_test 1000007 -static call_t syscalls[] = { +unsigned syscall_count = 1495; +call_t syscalls[] = { {"accept", 9437469}, {"accept$alg", 9437469}, {"accept$ax25", 9437469}, @@ -4422,109 +4420,109 @@ static call_t syscalls[] = { {"sysfs$3", 9437319}, {"sysinfo", 9437300}, {"syslog", 9437287}, - {"syz_emit_ethernet", 1000000}, - {"syz_extract_tcp_res", 1000001}, - {"syz_extract_tcp_res$synack", 1000001}, - {"syz_fuse_mount", 1000002}, - {"syz_fuseblk_mount", 1000003}, - {"syz_kvm_setup_cpu$arm64", 1000004}, - {"syz_kvm_setup_cpu$x86", 1000004}, - {"syz_open_dev$admmidi", 1000005}, - {"syz_open_dev$adsp", 1000005}, - {"syz_open_dev$amidi", 1000005}, - {"syz_open_dev$audion", 1000005}, - {"syz_open_dev$dmmidi", 1000005}, - {"syz_open_dev$dri", 1000005}, - {"syz_open_dev$dricontrol", 1000005}, - {"syz_open_dev$drirender", 1000005}, - {"syz_open_dev$dspn", 1000005}, - {"syz_open_dev$evdev", 1000005}, - {"syz_open_dev$floppy", 1000005}, - {"syz_open_dev$ircomm", 1000005}, - {"syz_open_dev$loop", 1000005}, - {"syz_open_dev$mice", 1000005}, - {"syz_open_dev$midi", 1000005}, - {"syz_open_dev$mouse", 1000005}, - {"syz_open_dev$random", 1000005}, - {"syz_open_dev$sg", 1000005}, - {"syz_open_dev$sndctrl", 1000005}, - {"syz_open_dev$sndhw", 1000005}, - {"syz_open_dev$sndmidi", 1000005}, - {"syz_open_dev$sndpcmc", 1000005}, - {"syz_open_dev$sndpcmp", 1000005}, - {"syz_open_dev$sndseq", 1000005}, - {"syz_open_dev$sndtimer", 1000005}, - {"syz_open_dev$tlk_device", 1000005}, - {"syz_open_dev$tun", 1000005}, - {"syz_open_dev$urandom", 1000005}, - {"syz_open_dev$usb", 1000005}, - {"syz_open_dev$usbmon", 1000005}, - {"syz_open_dev$vcsa", 1000005}, - {"syz_open_dev$vcsn", 1000005}, - {"syz_open_pts", 1000006}, - {"syz_test", 1000007}, - {"syz_test$align0", 1000007}, - {"syz_test$align1", 1000007}, - {"syz_test$align2", 1000007}, - {"syz_test$align3", 1000007}, - {"syz_test$align4", 1000007}, - {"syz_test$align5", 1000007}, - {"syz_test$align6", 1000007}, - {"syz_test$array0", 1000007}, - {"syz_test$array1", 1000007}, - {"syz_test$array2", 1000007}, - {"syz_test$bf0", 1000007}, - {"syz_test$bf1", 1000007}, - {"syz_test$csum_encode", 1000007}, - {"syz_test$csum_ipv4", 1000007}, - {"syz_test$csum_ipv4_tcp", 1000007}, - {"syz_test$csum_ipv4_udp", 1000007}, - {"syz_test$csum_ipv6_icmp", 1000007}, - {"syz_test$csum_ipv6_tcp", 1000007}, - {"syz_test$csum_ipv6_udp", 1000007}, - {"syz_test$end0", 1000007}, - {"syz_test$end1", 1000007}, - {"syz_test$int", 1000007}, - {"syz_test$length0", 1000007}, - {"syz_test$length1", 1000007}, - {"syz_test$length10", 1000007}, - {"syz_test$length11", 1000007}, - {"syz_test$length12", 1000007}, - {"syz_test$length13", 1000007}, - {"syz_test$length14", 1000007}, - {"syz_test$length15", 1000007}, - {"syz_test$length16", 1000007}, - {"syz_test$length17", 1000007}, - {"syz_test$length18", 1000007}, - {"syz_test$length19", 1000007}, - {"syz_test$length2", 1000007}, - {"syz_test$length20", 1000007}, - {"syz_test$length3", 1000007}, - {"syz_test$length4", 1000007}, - {"syz_test$length5", 1000007}, - {"syz_test$length6", 1000007}, - {"syz_test$length7", 1000007}, - {"syz_test$length8", 1000007}, - {"syz_test$length9", 1000007}, - {"syz_test$missing_resource", 1000007}, - {"syz_test$opt0", 1000007}, - {"syz_test$opt1", 1000007}, - {"syz_test$opt2", 1000007}, - {"syz_test$recur0", 1000007}, - {"syz_test$recur1", 1000007}, - {"syz_test$recur2", 1000007}, - {"syz_test$regression0", 1000007}, - {"syz_test$res0", 1000007}, - {"syz_test$res1", 1000007}, - {"syz_test$struct", 1000007}, - {"syz_test$text_x86_16", 1000007}, - {"syz_test$text_x86_32", 1000007}, - {"syz_test$text_x86_64", 1000007}, - {"syz_test$text_x86_real", 1000007}, - {"syz_test$union0", 1000007}, - {"syz_test$union1", 1000007}, - {"syz_test$union2", 1000007}, - {"syz_test$vma0", 1000007}, + {"syz_emit_ethernet", 1000000, (syscall_t)syz_emit_ethernet}, + {"syz_extract_tcp_res", 1000001, (syscall_t)syz_extract_tcp_res}, + {"syz_extract_tcp_res$synack", 1000001, (syscall_t)syz_extract_tcp_res}, + {"syz_fuse_mount", 1000002, (syscall_t)syz_fuse_mount}, + {"syz_fuseblk_mount", 1000003, (syscall_t)syz_fuseblk_mount}, + {"syz_kvm_setup_cpu$arm64", 1000004, (syscall_t)syz_kvm_setup_cpu}, + {"syz_kvm_setup_cpu$x86", 1000004, (syscall_t)syz_kvm_setup_cpu}, + {"syz_open_dev$admmidi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$adsp", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$amidi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$audion", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$dmmidi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$dri", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$dricontrol", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$drirender", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$dspn", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$evdev", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$floppy", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$ircomm", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$loop", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$mice", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$midi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$mouse", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$random", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sg", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndctrl", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndhw", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndmidi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndpcmc", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndpcmp", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndseq", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndtimer", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$tlk_device", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$tun", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$urandom", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$usb", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$usbmon", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$vcsa", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$vcsn", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_pts", 1000006, (syscall_t)syz_open_pts}, + {"syz_test", 1000007, (syscall_t)syz_test}, + {"syz_test$align0", 1000007, (syscall_t)syz_test}, + {"syz_test$align1", 1000007, (syscall_t)syz_test}, + {"syz_test$align2", 1000007, (syscall_t)syz_test}, + {"syz_test$align3", 1000007, (syscall_t)syz_test}, + {"syz_test$align4", 1000007, (syscall_t)syz_test}, + {"syz_test$align5", 1000007, (syscall_t)syz_test}, + {"syz_test$align6", 1000007, (syscall_t)syz_test}, + {"syz_test$array0", 1000007, (syscall_t)syz_test}, + {"syz_test$array1", 1000007, (syscall_t)syz_test}, + {"syz_test$array2", 1000007, (syscall_t)syz_test}, + {"syz_test$bf0", 1000007, (syscall_t)syz_test}, + {"syz_test$bf1", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_encode", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv4", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv4_tcp", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv4_udp", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv6_icmp", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv6_tcp", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv6_udp", 1000007, (syscall_t)syz_test}, + {"syz_test$end0", 1000007, (syscall_t)syz_test}, + {"syz_test$end1", 1000007, (syscall_t)syz_test}, + {"syz_test$int", 1000007, (syscall_t)syz_test}, + {"syz_test$length0", 1000007, (syscall_t)syz_test}, + {"syz_test$length1", 1000007, (syscall_t)syz_test}, + {"syz_test$length10", 1000007, (syscall_t)syz_test}, + {"syz_test$length11", 1000007, (syscall_t)syz_test}, + {"syz_test$length12", 1000007, (syscall_t)syz_test}, + {"syz_test$length13", 1000007, (syscall_t)syz_test}, + {"syz_test$length14", 1000007, (syscall_t)syz_test}, + {"syz_test$length15", 1000007, (syscall_t)syz_test}, + {"syz_test$length16", 1000007, (syscall_t)syz_test}, + {"syz_test$length17", 1000007, (syscall_t)syz_test}, + {"syz_test$length18", 1000007, (syscall_t)syz_test}, + {"syz_test$length19", 1000007, (syscall_t)syz_test}, + {"syz_test$length2", 1000007, (syscall_t)syz_test}, + {"syz_test$length20", 1000007, (syscall_t)syz_test}, + {"syz_test$length3", 1000007, (syscall_t)syz_test}, + {"syz_test$length4", 1000007, (syscall_t)syz_test}, + {"syz_test$length5", 1000007, (syscall_t)syz_test}, + {"syz_test$length6", 1000007, (syscall_t)syz_test}, + {"syz_test$length7", 1000007, (syscall_t)syz_test}, + {"syz_test$length8", 1000007, (syscall_t)syz_test}, + {"syz_test$length9", 1000007, (syscall_t)syz_test}, + {"syz_test$missing_resource", 1000007, (syscall_t)syz_test}, + {"syz_test$opt0", 1000007, (syscall_t)syz_test}, + {"syz_test$opt1", 1000007, (syscall_t)syz_test}, + {"syz_test$opt2", 1000007, (syscall_t)syz_test}, + {"syz_test$recur0", 1000007, (syscall_t)syz_test}, + {"syz_test$recur1", 1000007, (syscall_t)syz_test}, + {"syz_test$recur2", 1000007, (syscall_t)syz_test}, + {"syz_test$regression0", 1000007, (syscall_t)syz_test}, + {"syz_test$res0", 1000007, (syscall_t)syz_test}, + {"syz_test$res1", 1000007, (syscall_t)syz_test}, + {"syz_test$struct", 1000007, (syscall_t)syz_test}, + {"syz_test$text_x86_16", 1000007, (syscall_t)syz_test}, + {"syz_test$text_x86_32", 1000007, (syscall_t)syz_test}, + {"syz_test$text_x86_64", 1000007, (syscall_t)syz_test}, + {"syz_test$text_x86_real", 1000007, (syscall_t)syz_test}, + {"syz_test$union0", 1000007, (syscall_t)syz_test}, + {"syz_test$union1", 1000007, (syscall_t)syz_test}, + {"syz_test$union2", 1000007, (syscall_t)syz_test}, + {"syz_test$vma0", 1000007, (syscall_t)syz_test}, {"tee", 9437526}, {"tgkill", 9437452}, {"time", 9437197}, @@ -4586,7 +4584,8 @@ static call_t syscalls[] = { #define __NR_syz_open_pts 1000006 #define __NR_syz_test 1000007 -static call_t syscalls[] = { +unsigned syscall_count = 1470; +call_t syscalls[] = { {"accept", 202}, {"accept$alg", 202}, {"accept$ax25", 202}, @@ -5915,109 +5914,109 @@ static call_t syscalls[] = { {"syncfs", 267}, {"sysinfo", 179}, {"syslog", 116}, - {"syz_emit_ethernet", 1000000}, - {"syz_extract_tcp_res", 1000001}, - {"syz_extract_tcp_res$synack", 1000001}, - {"syz_fuse_mount", 1000002}, - {"syz_fuseblk_mount", 1000003}, - {"syz_kvm_setup_cpu$arm64", 1000004}, - {"syz_kvm_setup_cpu$x86", 1000004}, - {"syz_open_dev$admmidi", 1000005}, - {"syz_open_dev$adsp", 1000005}, - {"syz_open_dev$amidi", 1000005}, - {"syz_open_dev$audion", 1000005}, - {"syz_open_dev$dmmidi", 1000005}, - {"syz_open_dev$dri", 1000005}, - {"syz_open_dev$dricontrol", 1000005}, - {"syz_open_dev$drirender", 1000005}, - {"syz_open_dev$dspn", 1000005}, - {"syz_open_dev$evdev", 1000005}, - {"syz_open_dev$floppy", 1000005}, - {"syz_open_dev$ircomm", 1000005}, - {"syz_open_dev$loop", 1000005}, - {"syz_open_dev$mice", 1000005}, - {"syz_open_dev$midi", 1000005}, - {"syz_open_dev$mouse", 1000005}, - {"syz_open_dev$random", 1000005}, - {"syz_open_dev$sg", 1000005}, - {"syz_open_dev$sndctrl", 1000005}, - {"syz_open_dev$sndhw", 1000005}, - {"syz_open_dev$sndmidi", 1000005}, - {"syz_open_dev$sndpcmc", 1000005}, - {"syz_open_dev$sndpcmp", 1000005}, - {"syz_open_dev$sndseq", 1000005}, - {"syz_open_dev$sndtimer", 1000005}, - {"syz_open_dev$tlk_device", 1000005}, - {"syz_open_dev$tun", 1000005}, - {"syz_open_dev$urandom", 1000005}, - {"syz_open_dev$usb", 1000005}, - {"syz_open_dev$usbmon", 1000005}, - {"syz_open_dev$vcsa", 1000005}, - {"syz_open_dev$vcsn", 1000005}, - {"syz_open_pts", 1000006}, - {"syz_test", 1000007}, - {"syz_test$align0", 1000007}, - {"syz_test$align1", 1000007}, - {"syz_test$align2", 1000007}, - {"syz_test$align3", 1000007}, - {"syz_test$align4", 1000007}, - {"syz_test$align5", 1000007}, - {"syz_test$align6", 1000007}, - {"syz_test$array0", 1000007}, - {"syz_test$array1", 1000007}, - {"syz_test$array2", 1000007}, - {"syz_test$bf0", 1000007}, - {"syz_test$bf1", 1000007}, - {"syz_test$csum_encode", 1000007}, - {"syz_test$csum_ipv4", 1000007}, - {"syz_test$csum_ipv4_tcp", 1000007}, - {"syz_test$csum_ipv4_udp", 1000007}, - {"syz_test$csum_ipv6_icmp", 1000007}, - {"syz_test$csum_ipv6_tcp", 1000007}, - {"syz_test$csum_ipv6_udp", 1000007}, - {"syz_test$end0", 1000007}, - {"syz_test$end1", 1000007}, - {"syz_test$int", 1000007}, - {"syz_test$length0", 1000007}, - {"syz_test$length1", 1000007}, - {"syz_test$length10", 1000007}, - {"syz_test$length11", 1000007}, - {"syz_test$length12", 1000007}, - {"syz_test$length13", 1000007}, - {"syz_test$length14", 1000007}, - {"syz_test$length15", 1000007}, - {"syz_test$length16", 1000007}, - {"syz_test$length17", 1000007}, - {"syz_test$length18", 1000007}, - {"syz_test$length19", 1000007}, - {"syz_test$length2", 1000007}, - {"syz_test$length20", 1000007}, - {"syz_test$length3", 1000007}, - {"syz_test$length4", 1000007}, - {"syz_test$length5", 1000007}, - {"syz_test$length6", 1000007}, - {"syz_test$length7", 1000007}, - {"syz_test$length8", 1000007}, - {"syz_test$length9", 1000007}, - {"syz_test$missing_resource", 1000007}, - {"syz_test$opt0", 1000007}, - {"syz_test$opt1", 1000007}, - {"syz_test$opt2", 1000007}, - {"syz_test$recur0", 1000007}, - {"syz_test$recur1", 1000007}, - {"syz_test$recur2", 1000007}, - {"syz_test$regression0", 1000007}, - {"syz_test$res0", 1000007}, - {"syz_test$res1", 1000007}, - {"syz_test$struct", 1000007}, - {"syz_test$text_x86_16", 1000007}, - {"syz_test$text_x86_32", 1000007}, - {"syz_test$text_x86_64", 1000007}, - {"syz_test$text_x86_real", 1000007}, - {"syz_test$union0", 1000007}, - {"syz_test$union1", 1000007}, - {"syz_test$union2", 1000007}, - {"syz_test$vma0", 1000007}, + {"syz_emit_ethernet", 1000000, (syscall_t)syz_emit_ethernet}, + {"syz_extract_tcp_res", 1000001, (syscall_t)syz_extract_tcp_res}, + {"syz_extract_tcp_res$synack", 1000001, (syscall_t)syz_extract_tcp_res}, + {"syz_fuse_mount", 1000002, (syscall_t)syz_fuse_mount}, + {"syz_fuseblk_mount", 1000003, (syscall_t)syz_fuseblk_mount}, + {"syz_kvm_setup_cpu$arm64", 1000004, (syscall_t)syz_kvm_setup_cpu}, + {"syz_kvm_setup_cpu$x86", 1000004, (syscall_t)syz_kvm_setup_cpu}, + {"syz_open_dev$admmidi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$adsp", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$amidi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$audion", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$dmmidi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$dri", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$dricontrol", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$drirender", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$dspn", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$evdev", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$floppy", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$ircomm", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$loop", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$mice", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$midi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$mouse", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$random", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sg", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndctrl", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndhw", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndmidi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndpcmc", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndpcmp", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndseq", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndtimer", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$tlk_device", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$tun", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$urandom", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$usb", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$usbmon", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$vcsa", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$vcsn", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_pts", 1000006, (syscall_t)syz_open_pts}, + {"syz_test", 1000007, (syscall_t)syz_test}, + {"syz_test$align0", 1000007, (syscall_t)syz_test}, + {"syz_test$align1", 1000007, (syscall_t)syz_test}, + {"syz_test$align2", 1000007, (syscall_t)syz_test}, + {"syz_test$align3", 1000007, (syscall_t)syz_test}, + {"syz_test$align4", 1000007, (syscall_t)syz_test}, + {"syz_test$align5", 1000007, (syscall_t)syz_test}, + {"syz_test$align6", 1000007, (syscall_t)syz_test}, + {"syz_test$array0", 1000007, (syscall_t)syz_test}, + {"syz_test$array1", 1000007, (syscall_t)syz_test}, + {"syz_test$array2", 1000007, (syscall_t)syz_test}, + {"syz_test$bf0", 1000007, (syscall_t)syz_test}, + {"syz_test$bf1", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_encode", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv4", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv4_tcp", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv4_udp", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv6_icmp", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv6_tcp", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv6_udp", 1000007, (syscall_t)syz_test}, + {"syz_test$end0", 1000007, (syscall_t)syz_test}, + {"syz_test$end1", 1000007, (syscall_t)syz_test}, + {"syz_test$int", 1000007, (syscall_t)syz_test}, + {"syz_test$length0", 1000007, (syscall_t)syz_test}, + {"syz_test$length1", 1000007, (syscall_t)syz_test}, + {"syz_test$length10", 1000007, (syscall_t)syz_test}, + {"syz_test$length11", 1000007, (syscall_t)syz_test}, + {"syz_test$length12", 1000007, (syscall_t)syz_test}, + {"syz_test$length13", 1000007, (syscall_t)syz_test}, + {"syz_test$length14", 1000007, (syscall_t)syz_test}, + {"syz_test$length15", 1000007, (syscall_t)syz_test}, + {"syz_test$length16", 1000007, (syscall_t)syz_test}, + {"syz_test$length17", 1000007, (syscall_t)syz_test}, + {"syz_test$length18", 1000007, (syscall_t)syz_test}, + {"syz_test$length19", 1000007, (syscall_t)syz_test}, + {"syz_test$length2", 1000007, (syscall_t)syz_test}, + {"syz_test$length20", 1000007, (syscall_t)syz_test}, + {"syz_test$length3", 1000007, (syscall_t)syz_test}, + {"syz_test$length4", 1000007, (syscall_t)syz_test}, + {"syz_test$length5", 1000007, (syscall_t)syz_test}, + {"syz_test$length6", 1000007, (syscall_t)syz_test}, + {"syz_test$length7", 1000007, (syscall_t)syz_test}, + {"syz_test$length8", 1000007, (syscall_t)syz_test}, + {"syz_test$length9", 1000007, (syscall_t)syz_test}, + {"syz_test$missing_resource", 1000007, (syscall_t)syz_test}, + {"syz_test$opt0", 1000007, (syscall_t)syz_test}, + {"syz_test$opt1", 1000007, (syscall_t)syz_test}, + {"syz_test$opt2", 1000007, (syscall_t)syz_test}, + {"syz_test$recur0", 1000007, (syscall_t)syz_test}, + {"syz_test$recur1", 1000007, (syscall_t)syz_test}, + {"syz_test$recur2", 1000007, (syscall_t)syz_test}, + {"syz_test$regression0", 1000007, (syscall_t)syz_test}, + {"syz_test$res0", 1000007, (syscall_t)syz_test}, + {"syz_test$res1", 1000007, (syscall_t)syz_test}, + {"syz_test$struct", 1000007, (syscall_t)syz_test}, + {"syz_test$text_x86_16", 1000007, (syscall_t)syz_test}, + {"syz_test$text_x86_32", 1000007, (syscall_t)syz_test}, + {"syz_test$text_x86_64", 1000007, (syscall_t)syz_test}, + {"syz_test$text_x86_real", 1000007, (syscall_t)syz_test}, + {"syz_test$union0", 1000007, (syscall_t)syz_test}, + {"syz_test$union1", 1000007, (syscall_t)syz_test}, + {"syz_test$union2", 1000007, (syscall_t)syz_test}, + {"syz_test$vma0", 1000007, (syscall_t)syz_test}, {"tee", 77}, {"tgkill", 131}, {"timer_create", 107}, @@ -6073,7 +6072,8 @@ static call_t syscalls[] = { #define __NR_syz_open_pts 1000006 #define __NR_syz_test 1000007 -static call_t syscalls[] = { +unsigned syscall_count = 1451; +call_t syscalls[] = { {"accept", 330}, {"accept$alg", 330}, {"accept$ax25", 330}, @@ -7377,109 +7377,109 @@ static call_t syscalls[] = { {"sysfs$3", 135}, {"sysinfo", 116}, {"syslog", 103}, - {"syz_emit_ethernet", 1000000}, - {"syz_extract_tcp_res", 1000001}, - {"syz_extract_tcp_res$synack", 1000001}, - {"syz_fuse_mount", 1000002}, - {"syz_fuseblk_mount", 1000003}, - {"syz_kvm_setup_cpu$arm64", 1000004}, - {"syz_kvm_setup_cpu$x86", 1000004}, - {"syz_open_dev$admmidi", 1000005}, - {"syz_open_dev$adsp", 1000005}, - {"syz_open_dev$amidi", 1000005}, - {"syz_open_dev$audion", 1000005}, - {"syz_open_dev$dmmidi", 1000005}, - {"syz_open_dev$dri", 1000005}, - {"syz_open_dev$dricontrol", 1000005}, - {"syz_open_dev$drirender", 1000005}, - {"syz_open_dev$dspn", 1000005}, - {"syz_open_dev$evdev", 1000005}, - {"syz_open_dev$floppy", 1000005}, - {"syz_open_dev$ircomm", 1000005}, - {"syz_open_dev$loop", 1000005}, - {"syz_open_dev$mice", 1000005}, - {"syz_open_dev$midi", 1000005}, - {"syz_open_dev$mouse", 1000005}, - {"syz_open_dev$random", 1000005}, - {"syz_open_dev$sg", 1000005}, - {"syz_open_dev$sndctrl", 1000005}, - {"syz_open_dev$sndhw", 1000005}, - {"syz_open_dev$sndmidi", 1000005}, - {"syz_open_dev$sndpcmc", 1000005}, - {"syz_open_dev$sndpcmp", 1000005}, - {"syz_open_dev$sndseq", 1000005}, - {"syz_open_dev$sndtimer", 1000005}, - {"syz_open_dev$tlk_device", 1000005}, - {"syz_open_dev$tun", 1000005}, - {"syz_open_dev$urandom", 1000005}, - {"syz_open_dev$usb", 1000005}, - {"syz_open_dev$usbmon", 1000005}, - {"syz_open_dev$vcsa", 1000005}, - {"syz_open_dev$vcsn", 1000005}, - {"syz_open_pts", 1000006}, - {"syz_test", 1000007}, - {"syz_test$align0", 1000007}, - {"syz_test$align1", 1000007}, - {"syz_test$align2", 1000007}, - {"syz_test$align3", 1000007}, - {"syz_test$align4", 1000007}, - {"syz_test$align5", 1000007}, - {"syz_test$align6", 1000007}, - {"syz_test$array0", 1000007}, - {"syz_test$array1", 1000007}, - {"syz_test$array2", 1000007}, - {"syz_test$bf0", 1000007}, - {"syz_test$bf1", 1000007}, - {"syz_test$csum_encode", 1000007}, - {"syz_test$csum_ipv4", 1000007}, - {"syz_test$csum_ipv4_tcp", 1000007}, - {"syz_test$csum_ipv4_udp", 1000007}, - {"syz_test$csum_ipv6_icmp", 1000007}, - {"syz_test$csum_ipv6_tcp", 1000007}, - {"syz_test$csum_ipv6_udp", 1000007}, - {"syz_test$end0", 1000007}, - {"syz_test$end1", 1000007}, - {"syz_test$int", 1000007}, - {"syz_test$length0", 1000007}, - {"syz_test$length1", 1000007}, - {"syz_test$length10", 1000007}, - {"syz_test$length11", 1000007}, - {"syz_test$length12", 1000007}, - {"syz_test$length13", 1000007}, - {"syz_test$length14", 1000007}, - {"syz_test$length15", 1000007}, - {"syz_test$length16", 1000007}, - {"syz_test$length17", 1000007}, - {"syz_test$length18", 1000007}, - {"syz_test$length19", 1000007}, - {"syz_test$length2", 1000007}, - {"syz_test$length20", 1000007}, - {"syz_test$length3", 1000007}, - {"syz_test$length4", 1000007}, - {"syz_test$length5", 1000007}, - {"syz_test$length6", 1000007}, - {"syz_test$length7", 1000007}, - {"syz_test$length8", 1000007}, - {"syz_test$length9", 1000007}, - {"syz_test$missing_resource", 1000007}, - {"syz_test$opt0", 1000007}, - {"syz_test$opt1", 1000007}, - {"syz_test$opt2", 1000007}, - {"syz_test$recur0", 1000007}, - {"syz_test$recur1", 1000007}, - {"syz_test$recur2", 1000007}, - {"syz_test$regression0", 1000007}, - {"syz_test$res0", 1000007}, - {"syz_test$res1", 1000007}, - {"syz_test$struct", 1000007}, - {"syz_test$text_x86_16", 1000007}, - {"syz_test$text_x86_32", 1000007}, - {"syz_test$text_x86_64", 1000007}, - {"syz_test$text_x86_real", 1000007}, - {"syz_test$union0", 1000007}, - {"syz_test$union1", 1000007}, - {"syz_test$union2", 1000007}, - {"syz_test$vma0", 1000007}, + {"syz_emit_ethernet", 1000000, (syscall_t)syz_emit_ethernet}, + {"syz_extract_tcp_res", 1000001, (syscall_t)syz_extract_tcp_res}, + {"syz_extract_tcp_res$synack", 1000001, (syscall_t)syz_extract_tcp_res}, + {"syz_fuse_mount", 1000002, (syscall_t)syz_fuse_mount}, + {"syz_fuseblk_mount", 1000003, (syscall_t)syz_fuseblk_mount}, + {"syz_kvm_setup_cpu$arm64", 1000004, (syscall_t)syz_kvm_setup_cpu}, + {"syz_kvm_setup_cpu$x86", 1000004, (syscall_t)syz_kvm_setup_cpu}, + {"syz_open_dev$admmidi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$adsp", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$amidi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$audion", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$dmmidi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$dri", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$dricontrol", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$drirender", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$dspn", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$evdev", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$floppy", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$ircomm", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$loop", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$mice", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$midi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$mouse", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$random", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sg", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndctrl", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndhw", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndmidi", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndpcmc", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndpcmp", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndseq", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$sndtimer", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$tlk_device", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$tun", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$urandom", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$usb", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$usbmon", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$vcsa", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_dev$vcsn", 1000005, (syscall_t)syz_open_dev}, + {"syz_open_pts", 1000006, (syscall_t)syz_open_pts}, + {"syz_test", 1000007, (syscall_t)syz_test}, + {"syz_test$align0", 1000007, (syscall_t)syz_test}, + {"syz_test$align1", 1000007, (syscall_t)syz_test}, + {"syz_test$align2", 1000007, (syscall_t)syz_test}, + {"syz_test$align3", 1000007, (syscall_t)syz_test}, + {"syz_test$align4", 1000007, (syscall_t)syz_test}, + {"syz_test$align5", 1000007, (syscall_t)syz_test}, + {"syz_test$align6", 1000007, (syscall_t)syz_test}, + {"syz_test$array0", 1000007, (syscall_t)syz_test}, + {"syz_test$array1", 1000007, (syscall_t)syz_test}, + {"syz_test$array2", 1000007, (syscall_t)syz_test}, + {"syz_test$bf0", 1000007, (syscall_t)syz_test}, + {"syz_test$bf1", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_encode", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv4", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv4_tcp", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv4_udp", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv6_icmp", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv6_tcp", 1000007, (syscall_t)syz_test}, + {"syz_test$csum_ipv6_udp", 1000007, (syscall_t)syz_test}, + {"syz_test$end0", 1000007, (syscall_t)syz_test}, + {"syz_test$end1", 1000007, (syscall_t)syz_test}, + {"syz_test$int", 1000007, (syscall_t)syz_test}, + {"syz_test$length0", 1000007, (syscall_t)syz_test}, + {"syz_test$length1", 1000007, (syscall_t)syz_test}, + {"syz_test$length10", 1000007, (syscall_t)syz_test}, + {"syz_test$length11", 1000007, (syscall_t)syz_test}, + {"syz_test$length12", 1000007, (syscall_t)syz_test}, + {"syz_test$length13", 1000007, (syscall_t)syz_test}, + {"syz_test$length14", 1000007, (syscall_t)syz_test}, + {"syz_test$length15", 1000007, (syscall_t)syz_test}, + {"syz_test$length16", 1000007, (syscall_t)syz_test}, + {"syz_test$length17", 1000007, (syscall_t)syz_test}, + {"syz_test$length18", 1000007, (syscall_t)syz_test}, + {"syz_test$length19", 1000007, (syscall_t)syz_test}, + {"syz_test$length2", 1000007, (syscall_t)syz_test}, + {"syz_test$length20", 1000007, (syscall_t)syz_test}, + {"syz_test$length3", 1000007, (syscall_t)syz_test}, + {"syz_test$length4", 1000007, (syscall_t)syz_test}, + {"syz_test$length5", 1000007, (syscall_t)syz_test}, + {"syz_test$length6", 1000007, (syscall_t)syz_test}, + {"syz_test$length7", 1000007, (syscall_t)syz_test}, + {"syz_test$length8", 1000007, (syscall_t)syz_test}, + {"syz_test$length9", 1000007, (syscall_t)syz_test}, + {"syz_test$missing_resource", 1000007, (syscall_t)syz_test}, + {"syz_test$opt0", 1000007, (syscall_t)syz_test}, + {"syz_test$opt1", 1000007, (syscall_t)syz_test}, + {"syz_test$opt2", 1000007, (syscall_t)syz_test}, + {"syz_test$recur0", 1000007, (syscall_t)syz_test}, + {"syz_test$recur1", 1000007, (syscall_t)syz_test}, + {"syz_test$recur2", 1000007, (syscall_t)syz_test}, + {"syz_test$regression0", 1000007, (syscall_t)syz_test}, + {"syz_test$res0", 1000007, (syscall_t)syz_test}, + {"syz_test$res1", 1000007, (syscall_t)syz_test}, + {"syz_test$struct", 1000007, (syscall_t)syz_test}, + {"syz_test$text_x86_16", 1000007, (syscall_t)syz_test}, + {"syz_test$text_x86_32", 1000007, (syscall_t)syz_test}, + {"syz_test$text_x86_64", 1000007, (syscall_t)syz_test}, + {"syz_test$text_x86_real", 1000007, (syscall_t)syz_test}, + {"syz_test$union0", 1000007, (syscall_t)syz_test}, + {"syz_test$union1", 1000007, (syscall_t)syz_test}, + {"syz_test$union2", 1000007, (syscall_t)syz_test}, + {"syz_test$vma0", 1000007, (syscall_t)syz_test}, {"tee", 284}, {"tgkill", 250}, {"time", 13}, diff --git a/executor/test_executor_linux.cc b/executor/test_executor_linux.cc index 8bbd56910..5ba8d91ca 100644 --- a/executor/test_executor_linux.cc +++ b/executor/test_executor_linux.cc @@ -1,10 +1,10 @@ // Copyright 2017 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 "syscalls_linux.h" - #define SYZ_EXECUTOR -#include "common.h" +#include "common_linux.h" + +#include "syscalls_linux.h" #include <sys/utsname.h> diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index d54ac4678..d250eda68 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -13,6 +13,7 @@ import ( "github.com/google/syzkaller/pkg/ast" "github.com/google/syzkaller/prog" + "github.com/google/syzkaller/sys/targets" ) // Overview of compilation process: @@ -41,14 +42,15 @@ type Prog struct { } // Compile compiles sys description. -func Compile(desc *ast.Description, consts map[string]uint64, ptrSize uint64, eh ast.ErrorHandler) *Prog { +func Compile(desc *ast.Description, consts map[string]uint64, target *targets.Target, eh ast.ErrorHandler) *Prog { if eh == nil { eh = ast.LoggingHandler } comp := &compiler{ desc: ast.Clone(desc), + target: target, eh: eh, - ptrSize: ptrSize, + ptrSize: target.PtrSize, unsupported: make(map[string]bool), resources: make(map[string]*ast.Resource), structs: make(map[string]*ast.Struct), @@ -79,6 +81,7 @@ func Compile(desc *ast.Description, consts map[string]uint64, ptrSize uint64, eh type compiler struct { desc *ast.Description + target *targets.Target eh ast.ErrorHandler errors int warnings []warn diff --git a/pkg/compiler/compiler_test.go b/pkg/compiler/compiler_test.go index d82dd4775..be803bae9 100644 --- a/pkg/compiler/compiler_test.go +++ b/pkg/compiler/compiler_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/google/syzkaller/pkg/ast" + "github.com/google/syzkaller/sys/targets" ) func TestCompileAll(t *testing.T) { @@ -24,7 +25,7 @@ func TestCompileAll(t *testing.T) { if consts == nil { t.Fatalf("reading consts failed") } - prog := Compile(desc, consts, 8, eh) + prog := Compile(desc, consts, targets.List["linux"]["amd64"], eh) if prog == nil { t.Fatalf("compilation failed") } @@ -47,7 +48,7 @@ func TestErrors(t *testing.T) { t.Fatalf("parsing failed") } ExtractConsts(desc, em.ErrorHandler) - Compile(desc, consts, 8, em.ErrorHandler) + Compile(desc, consts, targets.List["linux"]["amd64"], em.ErrorHandler) em.Check(t) }) } @@ -67,7 +68,7 @@ func TestFuzz(t *testing.T) { for _, data := range inputs { desc := ast.Parse([]byte(data), "", eh) if desc != nil { - Compile(desc, consts, 8, eh) + Compile(desc, consts, targets.List["linux"]["amd64"], eh) } } } @@ -94,7 +95,7 @@ s2 { if desc == nil { t.Fatal("failed to parse") } - p := Compile(desc, map[string]uint64{"__NR_foo": 1}, 8, nil) + p := Compile(desc, map[string]uint64{"__NR_foo": 1}, targets.List["linux"]["amd64"], nil) if p == nil { t.Fatal("failed to compile") } diff --git a/pkg/compiler/consts.go b/pkg/compiler/consts.go index f64f4ed7f..4cb68e773 100644 --- a/pkg/compiler/consts.go +++ b/pkg/compiler/consts.go @@ -14,6 +14,7 @@ import ( "strings" "github.com/google/syzkaller/pkg/ast" + "github.com/google/syzkaller/sys/targets" ) type ConstInfo struct { @@ -125,6 +126,10 @@ func (comp *compiler) assignSyscallNumbers(consts map[string]uint64) { top = append(top, decl) continue } + if !targets.OSList[comp.target.OS].SyscallNumbers { + top = append(top, decl) + continue + } // Lookup in consts. str := "__NR_" + c.CallName nr, ok := consts[str] diff --git a/pkg/csource/common.go b/pkg/csource/common.go index fc5fd8782..588a0cd45 100644 --- a/pkg/csource/common.go +++ b/pkg/csource/common.go @@ -1,4 +1,4 @@ -// AUTOGENERATED FROM executor/common.h +// AUTOGENERATED FROM executor/common_linux.h package csource var commonHeader = ` @@ -8,35 +8,10 @@ var commonHeader = ` #define _GNU_SOURCE #endif -#include <stdint.h> -#include <string.h> #include <sys/syscall.h> #include <unistd.h> -#if defined(SYZ_EXECUTOR) || defined(SYZ_THREADED) || defined(SYZ_COLLIDE) -#include <pthread.h> -#include <stdlib.h> -#endif -#if defined(SYZ_EXECUTOR) || defined(SYZ_HANDLE_SEGV) -#include <setjmp.h> -#include <signal.h> -#include <string.h> -#endif -#if defined(SYZ_EXECUTOR) || defined(SYZ_USE_TMP_DIR) -#include <errno.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/stat.h> -#endif #if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) -#include <errno.h> -#include <signal.h> -#include <stdarg.h> -#include <stdio.h> #include <sys/prctl.h> -#include <sys/time.h> -#include <sys/wait.h> -#include <time.h> #endif #if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT) && defined(SYZ_USE_TMP_DIR)) #include <dirent.h> @@ -89,28 +64,24 @@ var commonHeader = ` #include <stdio.h> #include <sys/stat.h> #endif -#if defined(SYZ_EXECUTOR) || defined(SYZ_DEBUG) -#include <stdarg.h> -#include <stdio.h> -#endif -#ifdef __NR_syz_open_dev +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_open_dev) #include <fcntl.h> #include <stdio.h> #include <sys/stat.h> #endif -#if defined(__NR_syz_fuse_mount) || defined(__NR_syz_fuseblk_mount) +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_fuse_mount) || defined(__NR_syz_fuseblk_mount) #include <fcntl.h> #include <stdio.h> #include <sys/stat.h> #include <sys/sysmacros.h> #endif -#ifdef __NR_syz_open_pts +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_open_pts) #include <fcntl.h> #include <stdio.h> #include <sys/ioctl.h> #include <sys/stat.h> #endif -#ifdef __NR_syz_kvm_setup_cpu +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_kvm_setup_cpu) #include <errno.h> #include <fcntl.h> #include <linux/kvm.h> @@ -121,21 +92,10 @@ var commonHeader = ` #include <sys/stat.h> #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_SETUID) || \ - defined(SYZ_FAULT_INJECTION) || defined(__NR_syz_kvm_setup_cpu) -const int kFailStatus = 67; -const int kRetryStatus = 69; -#endif - -#if defined(SYZ_EXECUTOR) -const int kErrorStatus = 68; -#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) +#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) __attribute__((noreturn)) static void doexit(int status) { volatile unsigned i; @@ -150,9 +110,67 @@ __attribute__((noreturn)) static void doexit(int status) #define _exit use_doexit_instead #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_SETUID) || \ - defined(SYZ_FAULT_INJECTION) || defined(__NR_syz_kvm_setup_cpu) + + +#include <stdint.h> +#include <string.h> +#if defined(SYZ_EXECUTOR) || defined(SYZ_THREADED) || defined(SYZ_COLLIDE) +#include <pthread.h> +#include <stdlib.h> +#endif +#if defined(SYZ_EXECUTOR) || defined(SYZ_USE_TMP_DIR) +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#endif +#if defined(SYZ_EXECUTOR) || defined(SYZ_HANDLE_SEGV) +#include <setjmp.h> +#include <signal.h> +#include <string.h> +#endif +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) +#include <errno.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <sys/time.h> +#include <sys/wait.h> +#include <time.h> +#endif +#if defined(SYZ_EXECUTOR) || defined(SYZ_DEBUG) +#include <stdarg.h> +#include <stdio.h> +#endif + +#if defined(SYZ_EXECUTOR) +typedef long (*syscall_t)(long, long, long, long, long, long, long, long, long); + +struct call_t { + const char* name; + int sys_nr; + syscall_t call; +}; + +extern call_t syscalls[]; +extern unsigned syscall_count; +#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_SETUID) || defined(SYZ_FAULT_INJECTION) || defined(__NR_syz_kvm_setup_cpu) +const int kFailStatus = 67; +const int kRetryStatus = 69; +#endif + +#if defined(SYZ_EXECUTOR) +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_SETUID) || defined(SYZ_FAULT_INJECTION) || defined(__NR_syz_kvm_setup_cpu) __attribute__((noreturn)) static void fail(const char* msg, ...) { int e = errno; @@ -243,6 +261,60 @@ static void segv_handler(int sig, siginfo_t* info, void* uctx) } } +#define NONFAILING(...) \ + { \ + __atomic_fetch_add(&skip_segv, 1, __ATOMIC_SEQ_CST); \ + if (_setjmp(segv_env) == 0) { \ + __VA_ARGS__; \ + } \ + __atomic_fetch_sub(&skip_segv, 1, __ATOMIC_SEQ_CST); \ + } +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_USE_CHECKSUMS) +struct csum_inet { + uint32_t acc; +}; + +static void csum_inet_init(struct csum_inet* csum) +{ + csum->acc = 0; +} + +static void csum_inet_update(struct csum_inet* csum, const uint8_t* data, size_t length) +{ + if (length == 0) + return; + + size_t i; + for (i = 0; i < length - 1; i += 2) + csum->acc += *(uint16_t*)&data[i]; + + if (length & 1) + csum->acc += (uint16_t)data[length - 1]; + + while (csum->acc > 0xffff) + csum->acc = (csum->acc & 0xffff) + (csum->acc >> 16); +} + +static uint16_t csum_inet_digest(struct csum_inet* csum) +{ + return ~csum->acc; +} +#endif + +#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) +static uint64_t current_time_ms() +{ + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts)) + fail("clock_gettime failed"); + return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000; +} +#endif + +#if defined(SYZ_EXECUTOR) || defined(SYZ_HANDLE_SEGV) static void install_segv_handler() { struct sigaction sa; @@ -258,15 +330,6 @@ static void install_segv_handler() sigaction(SIGSEGV, &sa, NULL); sigaction(SIGBUS, &sa, NULL); } - -#define NONFAILING(...) \ - { \ - __atomic_fetch_add(&skip_segv, 1, __ATOMIC_SEQ_CST); \ - if (_setjmp(segv_env) == 0) { \ - __VA_ARGS__; \ - } \ - __atomic_fetch_sub(&skip_segv, 1, __ATOMIC_SEQ_CST); \ - } #endif #if defined(SYZ_EXECUTOR) || defined(SYZ_USE_TMP_DIR) @@ -419,38 +482,6 @@ static void debug_dump_data(const char* data, int length) } #endif -#if defined(SYZ_EXECUTOR) || defined(SYZ_USE_CHECKSUMS) -struct csum_inet { - uint32_t acc; -}; - -static void csum_inet_init(struct csum_inet* csum) -{ - csum->acc = 0; -} - -static void csum_inet_update(struct csum_inet* csum, const uint8_t* data, size_t length) -{ - if (length == 0) - return; - - size_t i; - for (i = 0; i < length - 1; i += 2) - csum->acc += *(uint16_t*)&data[i]; - - if (length & 1) - csum->acc += (uint16_t)data[length - 1]; - - while (csum->acc > 0xffff) - csum->acc = (csum->acc & 0xffff) + (csum->acc >> 16); -} - -static uint16_t csum_inet_digest(struct csum_inet* csum) -{ - return ~csum->acc; -} -#endif - #if defined(SYZ_EXECUTOR) || (defined(__NR_syz_emit_ethernet) && defined(SYZ_TUN_ENABLE)) static uintptr_t syz_emit_ethernet(uintptr_t a0, uintptr_t a1) { @@ -545,7 +576,7 @@ static uintptr_t syz_extract_tcp_res(uintptr_t a0, uintptr_t a1, uintptr_t a2) } #endif -#ifdef __NR_syz_open_dev +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_open_dev) static uintptr_t syz_open_dev(uintptr_t a0, uintptr_t a1, uintptr_t a2) { if (a0 == 0xc || a0 == 0xb) { @@ -566,7 +597,7 @@ static uintptr_t syz_open_dev(uintptr_t a0, uintptr_t a1, uintptr_t a2) } #endif -#ifdef __NR_syz_open_pts +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_open_pts) static uintptr_t syz_open_pts(uintptr_t a0, uintptr_t a1) { int ptyno = 0; @@ -578,7 +609,7 @@ static uintptr_t syz_open_pts(uintptr_t a0, uintptr_t a1) } #endif -#ifdef __NR_syz_fuse_mount +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_fuse_mount) static uintptr_t syz_fuse_mount(uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5) { uint64_t target = a0; @@ -604,7 +635,7 @@ static uintptr_t syz_fuse_mount(uintptr_t a0, uintptr_t a1, uintptr_t a2, uintpt } #endif -#ifdef __NR_syz_fuseblk_mount +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_fuseblk_mount) static uintptr_t syz_fuseblk_mount(uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7) { uint64_t target = a0; @@ -636,7 +667,7 @@ static uintptr_t syz_fuseblk_mount(uintptr_t a0, uintptr_t a1, uintptr_t a2, uin } #endif -#ifdef __NR_syz_kvm_setup_cpu +#if defined(SYZ_EXECUTOR) || defined(__NR_syz_kvm_setup_cpu) #if defined(__x86_64__) @@ -1600,45 +1631,10 @@ static uintptr_t syz_kvm_setup_cpu(uintptr_t a0, uintptr_t a1, uintptr_t a2, uin #endif #endif -#ifdef SYZ_EXECUTOR -static uintptr_t execute_syscall(int nr, uintptr_t a0, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7, uintptr_t a8) +#if defined(SYZ_EXECUTOR) +static uintptr_t syz_test() { - switch (nr) { - default: - return syscall(nr, a0, a1, a2, a3, a4, a5); -#ifdef __NR_syz_test - case __NR_syz_test: - return 0; -#endif -#ifdef __NR_syz_open_dev - case __NR_syz_open_dev: - return syz_open_dev(a0, a1, a2); -#endif -#ifdef __NR_syz_open_pts - case __NR_syz_open_pts: - return syz_open_pts(a0, a1); -#endif -#ifdef __NR_syz_fuse_mount - case __NR_syz_fuse_mount: - return syz_fuse_mount(a0, a1, a2, a3, a4, a5); -#endif -#ifdef __NR_syz_fuseblk_mount - 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 -#ifdef __NR_syz_extract_tcp_res - case __NR_syz_extract_tcp_res: - return syz_extract_tcp_res(a0, a1, a2); -#endif -#ifdef __NR_syz_kvm_setup_cpu - case __NR_syz_kvm_setup_cpu: - return syz_kvm_setup_cpu(a0, a1, a2, a3, a4, a5, a6, a7); -#endif - } + return 0; } #endif @@ -1887,17 +1883,6 @@ retry: } #endif -#if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT)) -static uint64_t current_time_ms() -{ - struct timespec ts; - - if (clock_gettime(CLOCK_MONOTONIC, &ts)) - fail("clock_gettime failed"); - return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000; -} -#endif - #if defined(SYZ_EXECUTOR) || defined(SYZ_FAULT_INJECTION) static int inject_fault(int nth) { @@ -1915,6 +1900,22 @@ static int inject_fault(int nth) } #endif +#if defined(SYZ_EXECUTOR) +static int fault_injected(int fail_fd) +{ + char buf[16]; + int n = read(fail_fd, buf, sizeof(buf) - 1); + if (n <= 0) + fail("failed to read /proc/self/task/tid/fail-nth"); + int res = n == 2 && buf[0] == '0' && buf[1] == '\n'; + buf[0] = '0'; + if (write(fail_fd, buf, 1) != 1) + fail("failed to write /proc/self/task/tid/fail-nth"); + close(fail_fd); + return res; +} +#endif + #if defined(SYZ_REPEAT) static void test(); diff --git a/pkg/csource/csource.go b/pkg/csource/csource.go index 7ead83deb..1bd818962 100644 --- a/pkg/csource/csource.go +++ b/pkg/csource/csource.go @@ -2,7 +2,7 @@ // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. // I heard you like shell... -//go:generate bash -c "echo -e '// AUTOGENERATED FROM executor/common.h\npackage csource\nvar commonHeader = `' > common.go; cat ../../executor/common.h | sed -e '/#include \"common_kvm_amd64.h\"/ {' -e 'r ../../executor/common_kvm_amd64.h' -e 'd' -e '}' - | sed -e '/#include \"common_kvm_arm64.h\"/ {' -e 'r ../../executor/common_kvm_arm64.h' -e 'd' -e '}' - | sed -e '/#include \"kvm.h\"/ {' -e 'r ../../executor/kvm.h' -e 'd' -e '}' - | sed -e '/#include \"kvm.S.h\"/ {' -e 'r ../../executor/kvm.S.h' -e 'd' -e '}' - | egrep -v '^[ ]*//' | sed '/^[ ]*\\/\\/.*/d' | sed 's#[ ]*//.*##g' >> common.go; echo '`' >> common.go" +//go:generate bash -c "echo -e '// AUTOGENERATED FROM executor/common_linux.h\npackage csource\nvar commonHeader = `' > common.go; cat ../../executor/common_linux.h | sed -e '/#include \"common.h\"/ {' -e 'r ../../executor/common.h' -e 'd' -e '}' - | sed -e '/#include \"common_kvm_amd64.h\"/ {' -e 'r ../../executor/common_kvm_amd64.h' -e 'd' -e '}' - | sed -e '/#include \"common_kvm_arm64.h\"/ {' -e 'r ../../executor/common_kvm_arm64.h' -e 'd' -e '}' - | sed -e '/#include \"kvm.h\"/ {' -e 'r ../../executor/kvm.h' -e 'd' -e '}' - | sed -e '/#include \"kvm.S.h\"/ {' -e 'r ../../executor/kvm.S.h' -e 'd' -e '}' - | egrep -v '^[ ]*//' | sed '/^[ ]*\\/\\/.*/d' | sed 's#[ ]*//.*##g' >> common.go; echo '`' >> common.go" //go:generate go fmt common.go package csource diff --git a/pkg/csource/csource_test.go b/pkg/csource/csource_test.go index 2ac048da2..be3c4b4c4 100644 --- a/pkg/csource/csource_test.go +++ b/pkg/csource/csource_test.go @@ -100,6 +100,10 @@ func TestOne(t *testing.T) { UseTmpDir: true, } for _, target := range prog.AllTargets() { + if target.OS == "fuchsia" { + // TODO(dvyukov): support fuchsia + continue + } target := target t.Run(target.OS+"/"+target.Arch, func(t *testing.T) { if target.OS == "linux" && target.Arch == "arm" { diff --git a/pkg/ipc/ipc.go b/pkg/ipc/ipc.go index a7e79b033..bae83806c 100644 --- a/pkg/ipc/ipc.go +++ b/pkg/ipc/ipc.go @@ -31,6 +31,14 @@ const ( FlagCollectComps // collect KCOV comparisons ) +const ( + outputSize = 16 << 20 + + statusFail = 67 + statusError = 68 + statusRetry = 69 +) + var ( flagThreaded = flag.Bool("threaded", true, "use threaded mode in executor") flagCollide = flag.Bool("collide", true, "collide syscalls to provoke data races") diff --git a/pkg/ipc/ipc_fuchsia.go b/pkg/ipc/ipc_fuchsia.go index a95a47f85..7f576b1c2 100644 --- a/pkg/ipc/ipc_fuchsia.go +++ b/pkg/ipc/ipc_fuchsia.go @@ -6,18 +6,54 @@ package ipc import ( + "bytes" + "encoding/binary" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "time" + + "github.com/google/syzkaller/pkg/osutil" "github.com/google/syzkaller/prog" ) type Env struct { - In []byte + bin []string + pid int + config Config StatExecs uint64 StatRestarts uint64 } func MakeEnv(bin string, pid int, config Config) (*Env, error) { - env := &Env{} + if config.Timeout < 7*time.Second { + config.Timeout = 7 * time.Second + } + env := &Env{ + bin: strings.Split(bin, " "), + pid: pid, + config: config, + } + if len(env.bin) == 0 { + return nil, fmt.Errorf("binary is empty string") + } + if false { + env.bin[0] = osutil.Abs(env.bin[0]) + base := filepath.Base(env.bin[0]) + pidStr := fmt.Sprint(pid) + if len(base)+len(pidStr) >= 16 { + // TASK_COMM_LEN is currently set to 16 + base = base[:15-len(pidStr)] + } + binCopy := filepath.Join(filepath.Dir(env.bin[0]), base+pidStr) + if err := os.Link(env.bin[0], binCopy); err == nil { + env.bin[0] = binCopy + } + } return env, nil } @@ -26,5 +62,47 @@ func (env *Env) Close() error { } func (env *Env) Exec(opts *ExecOpts, p *prog.Prog) (output []byte, info []CallInfo, failed, hanged bool, err0 error) { + dir, err := ioutil.TempDir("./", "syzkaller-testdir") + if err != nil { + err0 = fmt.Errorf("failed to create temp dir: %v", err) + return + } + defer os.RemoveAll(dir) + + data := make([]byte, prog.ExecBufferSize) + if err := p.SerializeForExec(data, env.pid); err != nil { + err0 = err + return + } + inbuf := new(bytes.Buffer) + binary.Write(inbuf, binary.LittleEndian, uint64(env.config.Flags)) + binary.Write(inbuf, binary.LittleEndian, uint64(opts.Flags)) + binary.Write(inbuf, binary.LittleEndian, uint64(env.pid)) + inbuf.Write(data) + + cmd := exec.Command(env.bin[0], env.bin[1:]...) + cmd.Env = []string{} + cmd.Dir = dir + cmd.Stdin = inbuf + if env.config.Flags&FlagDebug != 0 { + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stdout + } + if err := cmd.Start(); err != nil { + err0 = err + return + } + done := make(chan error) + go func() { + done <- cmd.Wait() + }() + t := time.NewTimer(env.config.Timeout) + select { + case <-done: + t.Stop() + case <-t.C: + cmd.Process.Kill() + <-done + } return } diff --git a/pkg/ipc/ipc_linux.go b/pkg/ipc/ipc_linux.go index 3023ac0a6..793611b91 100644 --- a/pkg/ipc/ipc_linux.go +++ b/pkg/ipc/ipc_linux.go @@ -22,7 +22,7 @@ import ( ) type Env struct { - In []byte + in []byte out []byte cmd *command @@ -37,12 +37,6 @@ type Env struct { } const ( - outputSize = 16 << 20 - - statusFail = 67 - statusError = 68 - statusRetry = 69 - // Comparison types masks taken from KCOV headers. compSizeMask = 6 compSize8 = 6 @@ -77,7 +71,7 @@ func MakeEnv(bin string, pid int, config Config) (*Env, error) { serializeUint64(inmem[8:], uint64(pid)) inmem = inmem[16:] env := &Env{ - In: inmem, + in: inmem, out: outmem, inFile: inf, outFile: outf, @@ -88,10 +82,7 @@ func MakeEnv(bin string, pid int, config Config) (*Env, error) { if len(env.bin) == 0 { return nil, fmt.Errorf("binary is empty string") } - env.bin[0], err = filepath.Abs(env.bin[0]) // we are going to chdir - if err != nil { - return nil, fmt.Errorf("filepath.Abs failed: %v", err) - } + env.bin[0] = osutil.Abs(env.bin[0]) // we are going to chdir // Append pid to binary name. // E.g. if binary is 'syz-executor' and pid=15, // we create a link from 'syz-executor15' to 'syz-executor' and use 'syz-executor15' as binary. @@ -116,7 +107,7 @@ func (env *Env) Close() error { if env.cmd != nil { env.cmd.close() } - err1 := closeMapping(env.inFile, env.In) + err1 := closeMapping(env.inFile, env.in) err2 := closeMapping(env.outFile, env.out) switch { case err1 != nil: @@ -137,7 +128,7 @@ func (env *Env) Close() error { func (env *Env) Exec(opts *ExecOpts, p *prog.Prog) (output []byte, info []CallInfo, failed, hanged bool, err0 error) { if p != nil { // Copy-in serialized program. - if err := p.SerializeForExec(env.In, env.pid); err != nil { + if err := p.SerializeForExec(env.in, env.pid); err != nil { err0 = fmt.Errorf("executor %v: failed to serialize: %v", env.pid, err) return } diff --git a/pkg/osutil/osutil.go b/pkg/osutil/osutil.go index 4fbd04506..79cd455c2 100644 --- a/pkg/osutil/osutil.go +++ b/pkg/osutil/osutil.go @@ -55,26 +55,6 @@ func runCmd(timeout time.Duration, env []string, dir, bin string, args ...string return output.Bytes(), nil } -var wd string - -func init() { - var err error - wd, err = os.Getwd() - if err != nil { - panic(fmt.Sprintf("failed to get wd: %v", err)) - } -} - -func Abs(path string) string { - if wd1, err := os.Getwd(); err == nil && wd1 != wd { - panic("don't mess with wd in a concurrent program") - } - if path == "" || filepath.IsAbs(path) { - return path - } - return filepath.Join(wd, path) -} - // IsExist returns true if the file name exists. func IsExist(name string) bool { _, err := os.Stat(name) diff --git a/pkg/osutil/osutil_fuchsia.go b/pkg/osutil/osutil_fuchsia.go new file mode 100644 index 000000000..75cdad843 --- /dev/null +++ b/pkg/osutil/osutil_fuchsia.go @@ -0,0 +1,17 @@ +// Copyright 2017 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. + +// +build fuchsia + +package osutil + +func HandleInterrupts(shutdown chan struct{}) { +} + +func Abs(path string) string { + // Getwd is not implemented. Let's hope for best. + if path == "" { + return "" + } + return "./" + path +} diff --git a/pkg/osutil/osutil_unix.go b/pkg/osutil/osutil_unix.go index 01eabc15b..27482b99a 100644 --- a/pkg/osutil/osutil_unix.go +++ b/pkg/osutil/osutil_unix.go @@ -90,3 +90,23 @@ func LongPipe() (io.ReadCloser, io.WriteCloser, error) { prolongPipe(r, w) return r, w, err } + +var wd string + +func init() { + var err error + wd, err = os.Getwd() + if err != nil { + panic(fmt.Sprintf("failed to get wd: %v", err)) + } +} + +func Abs(path string) string { + if wd1, err := os.Getwd(); err == nil && wd1 != wd { + panic("don't mess with wd in a concurrent program") + } + if path == "" || filepath.IsAbs(path) { + return path + } + return filepath.Join(wd, path) +} diff --git a/sys/fuchsia/amd64.go b/sys/fuchsia/amd64.go index eb51d797d..8331d756d 100644 --- a/sys/fuchsia/amd64.go +++ b/sys/fuchsia/amd64.go @@ -7,12 +7,31 @@ func init() { RegisterTarget(&Target{OS: "fuchsia", Arch: "amd64", Revision: revision_amd64, PtrSize: 8, Syscalls: syscalls_amd64, Resources: resources_amd64, Structs: structDescs_amd64, Consts: consts_amd64}, initTarget) } -var resources_amd64 = []*ResourceDesc(nil) +var resources_amd64 = []*ResourceDesc{ + {Name: "zx_handle", Type: &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", TypeSize: 4}}}, Kind: []string{"zx_handle"}, Values: []uint64{0}}, +} var structDescs_amd64 = []*KeyedStruct(nil) var syscalls_amd64 = []*Syscall{ - {Name: "mx_time_get", CallName: "mx_time_get", Args: []Type{ + {NR: 1000000, Name: "syz_mmap", CallName: "syz_mmap", Args: []Type{ + &VmaType{TypeCommon: TypeCommon{TypeName: "vma", FldName: "addr", TypeSize: 8}}, + &LenType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "len", FldName: "len", TypeSize: 8}}, Buf: "addr"}, + }}, + {ID: 1, Name: "zx_handle_close", CallName: "zx_handle_close", Args: []Type{ + &ResourceType{TypeCommon: TypeCommon{TypeName: "zx_handle", FldName: "handle", TypeSize: 4}}, + }}, + {ID: 2, Name: "zx_handle_duplicate", CallName: "zx_handle_duplicate", Args: []Type{ + &ResourceType{TypeCommon: TypeCommon{TypeName: "zx_handle", FldName: "handle", TypeSize: 4}}, + &FlagsType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "zx_rights", FldName: "rights", TypeSize: 8}}, Vals: []uint64{0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 2147483648}}, + &PtrType{TypeCommon: TypeCommon{TypeName: "ptr", FldName: "out", TypeSize: 8}, Type: &ResourceType{TypeCommon: TypeCommon{TypeName: "zx_handle", TypeSize: 4, ArgDir: 1}}}, + }}, + {ID: 3, Name: "zx_handle_replace", CallName: "zx_handle_replace", Args: []Type{ + &ResourceType{TypeCommon: TypeCommon{TypeName: "zx_handle", FldName: "handle", TypeSize: 4}}, + &FlagsType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "zx_rights", FldName: "rights", TypeSize: 8}}, Vals: []uint64{0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 2147483648}}, + &PtrType{TypeCommon: TypeCommon{TypeName: "ptr", FldName: "out", TypeSize: 8}, Type: &ResourceType{TypeCommon: TypeCommon{TypeName: "zx_handle", TypeSize: 4, ArgDir: 1}}}, + }}, + {ID: 4, Name: "zx_time_get", CallName: "zx_time_get", Args: []Type{ &FlagsType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "clock_id", FldName: "clock_id", TypeSize: 8}}, Vals: []uint64{0, 1, 2}}, }}, } @@ -21,7 +40,22 @@ var consts_amd64 = []ConstValue{ {Name: "MX_CLOCK_MONOTONIC"}, {Name: "MX_CLOCK_THREAD", Value: 2}, {Name: "MX_CLOCK_UTC", Value: 1}, - {Name: "__NR_mx_time_get"}, + {Name: "ZX_RIGHT_DESTROY", Value: 512}, + {Name: "ZX_RIGHT_DUPLICATE", Value: 1}, + {Name: "ZX_RIGHT_ENUMERATE", Value: 256}, + {Name: "ZX_RIGHT_EXECUTE", Value: 16}, + {Name: "ZX_RIGHT_GET_POLICY", Value: 2048}, + {Name: "ZX_RIGHT_GET_PROPERTY", Value: 64}, + {Name: "ZX_RIGHT_MAP", Value: 32}, + {Name: "ZX_RIGHT_NONE"}, + {Name: "ZX_RIGHT_READ", Value: 4}, + {Name: "ZX_RIGHT_SAME_RIGHTS", Value: 2147483648}, + {Name: "ZX_RIGHT_SET_POLICY", Value: 1024}, + {Name: "ZX_RIGHT_SET_PROPERTY", Value: 128}, + {Name: "ZX_RIGHT_SIGNAL", Value: 4096}, + {Name: "ZX_RIGHT_SIGNAL_PEER", Value: 8192}, + {Name: "ZX_RIGHT_TRANSFER", Value: 2}, + {Name: "ZX_RIGHT_WRITE", Value: 8}, } -const revision_amd64 = "d65b9adb4853817be6f471df44fc8347ebf6dfc6" +const revision_amd64 = "640bc9ab193d2488d41793c10c5a44bab3ad33f9" diff --git a/sys/fuchsia/arm64.go b/sys/fuchsia/arm64.go index b39870ffa..1341b864c 100644 --- a/sys/fuchsia/arm64.go +++ b/sys/fuchsia/arm64.go @@ -7,12 +7,31 @@ func init() { RegisterTarget(&Target{OS: "fuchsia", Arch: "arm64", Revision: revision_arm64, PtrSize: 8, Syscalls: syscalls_arm64, Resources: resources_arm64, Structs: structDescs_arm64, Consts: consts_arm64}, initTarget) } -var resources_arm64 = []*ResourceDesc(nil) +var resources_arm64 = []*ResourceDesc{ + {Name: "zx_handle", Type: &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", TypeSize: 4}}}, Kind: []string{"zx_handle"}, Values: []uint64{0}}, +} var structDescs_arm64 = []*KeyedStruct(nil) var syscalls_arm64 = []*Syscall{ - {Name: "mx_time_get", CallName: "mx_time_get", Args: []Type{ + {NR: 1000000, Name: "syz_mmap", CallName: "syz_mmap", Args: []Type{ + &VmaType{TypeCommon: TypeCommon{TypeName: "vma", FldName: "addr", TypeSize: 8}}, + &LenType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "len", FldName: "len", TypeSize: 8}}, Buf: "addr"}, + }}, + {ID: 1, Name: "zx_handle_close", CallName: "zx_handle_close", Args: []Type{ + &ResourceType{TypeCommon: TypeCommon{TypeName: "zx_handle", FldName: "handle", TypeSize: 4}}, + }}, + {ID: 2, Name: "zx_handle_duplicate", CallName: "zx_handle_duplicate", Args: []Type{ + &ResourceType{TypeCommon: TypeCommon{TypeName: "zx_handle", FldName: "handle", TypeSize: 4}}, + &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "zx_rights", FldName: "rights", TypeSize: 8}}}, + &PtrType{TypeCommon: TypeCommon{TypeName: "ptr", FldName: "out", TypeSize: 8}, Type: &ResourceType{TypeCommon: TypeCommon{TypeName: "zx_handle", TypeSize: 4, ArgDir: 1}}}, + }}, + {ID: 3, Name: "zx_handle_replace", CallName: "zx_handle_replace", Args: []Type{ + &ResourceType{TypeCommon: TypeCommon{TypeName: "zx_handle", FldName: "handle", TypeSize: 4}}, + &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "zx_rights", FldName: "rights", TypeSize: 8}}}, + &PtrType{TypeCommon: TypeCommon{TypeName: "ptr", FldName: "out", TypeSize: 8}, Type: &ResourceType{TypeCommon: TypeCommon{TypeName: "zx_handle", TypeSize: 4, ArgDir: 1}}}, + }}, + {ID: 4, Name: "zx_time_get", CallName: "zx_time_get", Args: []Type{ &FlagsType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "clock_id", FldName: "clock_id", TypeSize: 8}}, Vals: []uint64{0, 1, 2}}, }}, } @@ -21,7 +40,6 @@ var consts_arm64 = []ConstValue{ {Name: "MX_CLOCK_MONOTONIC"}, {Name: "MX_CLOCK_THREAD", Value: 2}, {Name: "MX_CLOCK_UTC", Value: 1}, - {Name: "__NR_mx_time_get"}, } -const revision_arm64 = "bd2655e6d85f1fecceb1648649e1ad5adda81dc8" +const revision_arm64 = "8c364809f3ccf951f9c9ac974a817dfaf8d1713c" diff --git a/sys/fuchsia/init.go b/sys/fuchsia/init.go index 98cfcf189..12ee37daf 100644 --- a/sys/fuchsia/init.go +++ b/sys/fuchsia/init.go @@ -8,7 +8,9 @@ import ( ) func initTarget(target *prog.Target) { - arch := &arch{} + arch := &arch{ + mmapSyscall: target.SyscallMap["syz_mmap"], + } target.PageSize = pageSize target.DataOffset = dataOffset @@ -26,11 +28,24 @@ type arch struct { mmapSyscall *prog.Syscall } -// createMmapCall creates a "normal" mmap call that maps [start, start+npages) page range. func (arch *arch) makeMmap(start, npages uint64) *prog.Call { - return nil + meta := arch.mmapSyscall + return &prog.Call{ + Meta: meta, + Args: []prog.Arg{ + prog.MakePointerArg(meta.Args[0], start, 0, npages, nil), + prog.MakeConstArg(meta.Args[1], npages*pageSize), + }, + Ret: prog.MakeReturnArg(meta.Ret), + } } func (arch *arch) analyzeMmap(c *prog.Call) (start, npages uint64, mapped bool) { + switch c.Meta.Name { + case "syz_mmap": + npages = c.Args[1].(*prog.ConstArg).Val / pageSize + start = c.Args[0].(*prog.PointerArg).PageIndex + mapped = true + } return } diff --git a/sys/fuchsia/sys.txt b/sys/fuchsia/sys.txt index 543d5da01..0da424b97 100644 --- a/sys/fuchsia/sys.txt +++ b/sys/fuchsia/sys.txt @@ -1,6 +1,16 @@ # Copyright 2017 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. -mx_time_get(clock_id flags[clock_id]) +syz_mmap(addr vma, len len[addr]) + +resource zx_handle[int32]: 0 + +zx_handle_close(handle zx_handle) +zx_handle_duplicate(handle zx_handle, rights flags[zx_rights], out ptr[out, zx_handle]) +zx_handle_replace(handle zx_handle, rights flags[zx_rights], out ptr[out, zx_handle]) + +zx_time_get(clock_id flags[clock_id]) clock_id = MX_CLOCK_MONOTONIC, MX_CLOCK_UTC, MX_CLOCK_THREAD + +zx_rights = ZX_RIGHT_NONE, ZX_RIGHT_DUPLICATE, ZX_RIGHT_TRANSFER, ZX_RIGHT_READ, ZX_RIGHT_WRITE, ZX_RIGHT_EXECUTE, ZX_RIGHT_MAP, ZX_RIGHT_GET_PROPERTY, ZX_RIGHT_SET_PROPERTY, ZX_RIGHT_ENUMERATE, ZX_RIGHT_DESTROY, ZX_RIGHT_SET_POLICY, ZX_RIGHT_GET_POLICY, ZX_RIGHT_SIGNAL, ZX_RIGHT_SIGNAL_PEER, ZX_RIGHT_SAME_RIGHTS diff --git a/sys/fuchsia/sys_amd64.const b/sys/fuchsia/sys_amd64.const index c6aa53ea0..c65545df9 100644 --- a/sys/fuchsia/sys_amd64.const +++ b/sys/fuchsia/sys_amd64.const @@ -1,5 +1,20 @@ -__NR_mx_time_get = 0 - MX_CLOCK_MONOTONIC = 0 MX_CLOCK_UTC = 1 MX_CLOCK_THREAD = 2 + +ZX_RIGHT_NONE = 0 +ZX_RIGHT_DUPLICATE = 1 +ZX_RIGHT_TRANSFER = 2 +ZX_RIGHT_READ = 4 +ZX_RIGHT_WRITE = 8 +ZX_RIGHT_EXECUTE = 16 +ZX_RIGHT_MAP = 32 +ZX_RIGHT_GET_PROPERTY = 64 +ZX_RIGHT_SET_PROPERTY = 128 +ZX_RIGHT_ENUMERATE = 256 +ZX_RIGHT_DESTROY = 512 +ZX_RIGHT_SET_POLICY = 1024 +ZX_RIGHT_GET_POLICY = 2048 +ZX_RIGHT_SIGNAL = 4096 +ZX_RIGHT_SIGNAL_PEER = 8192 +ZX_RIGHT_SAME_RIGHTS = 2147483648 diff --git a/sys/fuchsia/sys_arm64.const b/sys/fuchsia/sys_arm64.const index c6aa53ea0..caae81d94 100644 --- a/sys/fuchsia/sys_arm64.const +++ b/sys/fuchsia/sys_arm64.const @@ -1,5 +1,3 @@ -__NR_mx_time_get = 0 - MX_CLOCK_MONOTONIC = 0 MX_CLOCK_UTC = 1 MX_CLOCK_THREAD = 2 diff --git a/sys/sys.go b/sys/sys.go index 33718510c..87abb9997 100644 --- a/sys/sys.go +++ b/sys/sys.go @@ -4,6 +4,7 @@ package sys import ( + _ "github.com/google/syzkaller/sys/fuchsia" _ "github.com/google/syzkaller/sys/linux" ) diff --git a/sys/syz-sysgen/sysgen.go b/sys/syz-sysgen/sysgen.go index 3aa98a16a..273bb7d04 100644 --- a/sys/syz-sysgen/sysgen.go +++ b/sys/syz-sysgen/sysgen.go @@ -71,7 +71,7 @@ func main() { if consts == nil { return } - prog := compiler.Compile(top, consts, job.Target.PtrSize, eh) + prog := compiler.Compile(top, consts, job.Target, eh) if prog == nil { return } @@ -168,8 +168,10 @@ func generate(target *targets.Target, prg *compiler.Prog, consts map[string]uint func generateExecutorSyscalls(target *targets.Target, syscalls []*prog.Syscall, rev string) []byte { type SyscallData struct { - Name string - NR int32 + Name string + CallName string + NR int32 + NeedCall bool } type ArchData struct { Revision string @@ -184,14 +186,24 @@ func generateExecutorSyscalls(target *targets.Target, syscalls []*prog.Syscall, CARCH: target.CArch, } fake := make(map[string]uint64) + syscallNumbers := targets.OSList[target.OS].SyscallNumbers for _, c := range syscalls { - data.Calls = append(data.Calls, SyscallData{c.Name, int32(c.NR)}) - if strings.HasPrefix(c.CallName, "syz_") { + syz := strings.HasPrefix(c.CallName, "syz_") + if syz { fake[c.CallName] = c.NR } + data.Calls = append(data.Calls, SyscallData{ + Name: c.Name, + CallName: c.CallName, + NR: int32(c.NR), + NeedCall: syz || !syscallNumbers, + }) } for name, nr := range fake { - data.Fake = append(data.Fake, SyscallData{name, int32(nr)}) + data.Fake = append(data.Fake, SyscallData{ + Name: name, + NR: int32(nr), + }) } sort.Slice(data.Calls, func(i, j int) bool { return data.Calls[i].Name < data.Calls[j].Name @@ -208,7 +220,7 @@ func generateExecutorSyscalls(target *targets.Target, syscalls []*prog.Syscall, func writeExecutorSyscalls(OS string, archs [][]byte) { buf := new(bytes.Buffer) - buf.WriteString(syscallsTempl) + buf.WriteString("// AUTOGENERATED FILE\n\n") for _, arch := range archs { buf.Write(arch) } @@ -247,22 +259,15 @@ func logf(v int, msg string, args ...interface{}) { } } -var syscallsTempl = `// AUTOGENERATED FILE - -struct call_t { - const char* name; - int sys_nr; -}; -` - var archTempl = template.Must(template.New("").Parse(` #if {{range $cdef := $.CARCH}}defined({{$cdef}}) || {{end}}0 #define GOARCH "{{.GOARCH}}" #define SYZ_REVISION "{{.Revision}}" {{range $c := $.Fake}}#define __NR_{{$c.Name}} {{$c.NR}} {{end}} -static call_t syscalls[] = { -{{range $c := $.Calls}} {"{{$c.Name}}", {{$c.NR}}}, +unsigned syscall_count = {{len $.Calls}}; +call_t syscalls[] = { +{{range $c := $.Calls}} {"{{$c.Name}}", {{$c.NR}}{{if $c.NeedCall}}, (syscall_t){{$c.CallName}}{{end}}}, {{end}} }; #endif diff --git a/sys/targets/targets.go b/sys/targets/targets.go index 6c5028c1d..8e00b0fa6 100644 --- a/sys/targets/targets.go +++ b/sys/targets/targets.go @@ -74,6 +74,20 @@ var List = map[string]map[string]*Target{ }, } +type OS struct { + // Does the OS use syscall numbers (e.g. Linux) or has interface based on functions (e.g. fuchsia). + SyscallNumbers bool +} + +var OSList = map[string]*OS{ + "linux": { + SyscallNumbers: true, + }, + "fuchsia": { + SyscallNumbers: false, + }, +} + func init() { for OS, archs := range List { for arch, target := range archs { diff --git a/tools/syz-execprog/execprog.go b/tools/syz-execprog/execprog.go index 9f2c45ba6..ff85f4032 100644 --- a/tools/syz-execprog/execprog.go +++ b/tools/syz-execprog/execprog.go @@ -14,7 +14,6 @@ import ( "os" "runtime" "sync" - "sync/atomic" "time" "github.com/google/syzkaller/pkg/cover" @@ -110,7 +109,7 @@ func main() { gate := ipc.NewGate(2**flagProcs, nil) var pos int var lastPrint time.Time - var shutdown uint32 + shutdown := make(chan struct{}) for p := 0; p < *flagProcs; p++ { pid := p go func() { @@ -146,8 +145,10 @@ func main() { logMu.Unlock() } output, info, failed, hanged, err := env.Exec(execOpts, p) - if atomic.LoadUint32(&shutdown) != 0 { + select { + case <-shutdown: return false + default: } if failed { fmt.Printf("BUG: executor-detected bug:\n%s", output) @@ -190,6 +191,6 @@ func main() { }() } - go handleInterrupt(&shutdown) + osutil.HandleInterrupts(shutdown) wg.Wait() } diff --git a/tools/syz-execprog/execprog_fuchsia.go b/tools/syz-execprog/execprog_fuchsia.go deleted file mode 100644 index 14eac9306..000000000 --- a/tools/syz-execprog/execprog_fuchsia.go +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2017 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. - -// +build fuchsia - -package main - -func handleInterrupt(shutdown *uint32) { -} diff --git a/tools/syz-execprog/execprog_linux.go b/tools/syz-execprog/execprog_linux.go deleted file mode 100644 index 01e74cec5..000000000 --- a/tools/syz-execprog/execprog_linux.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2017 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 main - -import ( - "os" - "os/signal" - "sync/atomic" - "syscall" - - "github.com/google/syzkaller/pkg/log" -) - -func handleInterrupt(shutdown *uint32) { - c := make(chan os.Signal, 2) - signal.Notify(c, syscall.SIGINT) - <-c - log.Logf(0, "shutting down...") - atomic.StoreUint32(shutdown, 1) - <-c - log.Fatalf("terminating") -} |
