diff options
Diffstat (limited to 'executor/common.h')
| -rw-r--r-- | executor/common.h | 918 |
1 files changed, 24 insertions, 894 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 |
