diff options
| author | Greg Steuck <greg@nest.cx> | 2023-04-25 17:14:16 +0100 |
|---|---|---|
| committer | Greg Steuck <blackgnezdo@gmail.com> | 2023-04-27 09:52:01 -0700 |
| commit | a18ed5a24b4dd24f8b3e9ac673347e182c9de81a (patch) | |
| tree | cab47faef2ad291ac2db8428919aa3ebd5d49bd1 /executor/common_openbsd.h | |
| parent | 59e959932704c7a500e97fda11bbae7ccca50020 (diff) | |
executor: detangle common_openbsd.h out of common_bsd.h
Diffstat (limited to 'executor/common_openbsd.h')
| -rw-r--r-- | executor/common_openbsd.h | 380 |
1 files changed, 380 insertions, 0 deletions
diff --git a/executor/common_openbsd.h b/executor/common_openbsd.h new file mode 100644 index 000000000..031aaf88a --- /dev/null +++ b/executor/common_openbsd.h @@ -0,0 +1,380 @@ +// 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 <unistd.h> + +#include <pwd.h> +#include <stdarg.h> +#include <stdbool.h> +#include <string.h> +#include <sys/syscall.h> + +// Needed syscall libc stubs. +#include <dirent.h> +#include <fcntl.h> +#include <poll.h> +#include <sys/event.h> +#include <sys/ioctl.h> +#include <sys/ktrace.h> +#include <sys/mman.h> +#include <sys/msg.h> +#include <sys/sem.h> +#include <sys/shm.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/sysctl.h> +#include <sys/syslog.h> + +#define CAST + +#if (SYZ_EXECUTOR || __NR_syz_open_pts) +#include <termios.h> +#include <util.h> + +static uintptr_t syz_open_pts(void) +{ + int master, slave; + + if (openpty(&master, &slave, NULL, NULL, NULL) == -1) + return -1; + // Move the master fd up in order to reduce the chances of the fuzzer + // generating a call to close(2) with the same fd. + if (dup2(master, master + 100) != -1) + close(master); + return slave; +} +#endif // (SYZ_EXECUTOR || __NR_syz_open_pts) + +#if SYZ_EXECUTOR || SYZ_NET_INJECTION + +#include <net/if_tun.h> +#include <sys/types.h> + +static int tunfd = -1; + +#define MAX_TUN 8 + +// Because the interface and device name contain an int, use MAXINT to determine +// the maximum size of the string. +// Since on *BSD sizeof(int) is 4, MAXINT is 2147483647. +#define TUN_IFACE "tap%d" +#define MAX_TUN_IFACE_SIZE sizeof("tap2147483647") +#define TUN_DEVICE "/dev/tap%d" +#define MAX_TUN_DEVICE_SIZE sizeof("/dev/tap2147483647") + +#define LOCAL_MAC "aa:aa:aa:aa:aa:aa" +#define REMOTE_MAC "aa:aa:aa:aa:aa:bb" +#define LOCAL_IPV4 "172.20.%d.170" +#define MAX_LOCAL_IPV4_SIZE sizeof("172.20.255.170") +#define REMOTE_IPV4 "172.20.%d.187" +#define MAX_REMOTE_IPV4_SIZE sizeof("172.20.255.187") +#define LOCAL_IPV6 "fe80::%02xaa" +#define MAX_LOCAL_IPV6_SIZE sizeof("fe80::ffaa") +#define REMOTE_IPV6 "fe80::%02xbb" +#define MAX_REMOTE_IPV6_SIZE sizeof("fe80::ffbb") + +static void vsnprintf_check(char* str, size_t size, const char* format, va_list args) +{ + int rv = vsnprintf(str, size, format, args); + if (rv < 0) + fail("vsnprintf failed"); + if ((size_t)rv >= size) + failmsg("vsnprintf: string doesn't fit into buffer", "string='%s'", 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 +#define PATH_PREFIX "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin " +#define PATH_PREFIX_LEN (sizeof(PATH_PREFIX) - 1) + +static void execute_command(bool panic, const char* format, ...) +{ + va_list args; + va_start(args, format); + // Executor process does not have any env, including PATH. + // On some distributions, system/shell adds a minimal PATH, on some it does not. + // Set own standard PATH to make it work across distributions. + char command[PATH_PREFIX_LEN + COMMAND_MAX_LEN]; + memcpy(command, PATH_PREFIX, PATH_PREFIX_LEN); + vsnprintf_check(command + PATH_PREFIX_LEN, COMMAND_MAX_LEN, format, args); + va_end(args); + int rv = system(command); + if (rv) { + if (panic) + failmsg("command failed", "command=%s: %d", &command[0], rv); + debug("command '%s': %d\n", &command[0], rv); + } +} + +static void initialize_tun(int tun_id) +{ +#if SYZ_EXECUTOR + if (!flag_net_injection) + return; +#endif // SYZ_EXECUTOR + + if (tun_id < 0 || tun_id >= MAX_TUN) + failmsg("tun_id out of range", "tun_id=%d", tun_id); + + char tun_device[MAX_TUN_DEVICE_SIZE]; + snprintf_check(tun_device, sizeof(tun_device), TUN_DEVICE, tun_id); + + char tun_iface[MAX_TUN_IFACE_SIZE]; + snprintf_check(tun_iface, sizeof(tun_iface), TUN_IFACE, tun_id); + + execute_command(0, "ifconfig %s destroy", tun_iface); + + tunfd = open(tun_device, O_RDWR | O_NONBLOCK); + if (tunfd == -1) { +#if SYZ_EXECUTOR + failmsg("tun: can't open device", "device=%s", tun_device); +#else + printf("tun: can't open %s: errno=%d\n", tun_device, errno); + return; +#endif // SYZ_EXECUTOR + } + // Remap tun onto higher fd number to hide it from fuzzer and to keep + // fd numbers stable regardless of whether tun is opened or not (also see kMaxFd). + const int kTunFd = 200; + if (dup2(tunfd, kTunFd) < 0) + fail("dup2(tunfd, kTunFd) failed"); + close(tunfd); + tunfd = kTunFd; + + char local_mac[sizeof(LOCAL_MAC)]; + snprintf_check(local_mac, sizeof(local_mac), LOCAL_MAC); + + // Set the MAC address of the interface to LOCAL_MAC + execute_command(1, "ifconfig %s lladdr %s", tun_iface, local_mac); + + // Setting up a static ip for the interface + char local_ipv4[MAX_LOCAL_IPV4_SIZE]; + snprintf_check(local_ipv4, sizeof(local_ipv4), LOCAL_IPV4, tun_id); + execute_command(1, "ifconfig %s inet %s netmask 255.255.255.0", tun_iface, local_ipv4); + + // Creates an ARP table entry for the remote ip and MAC address + char remote_mac[sizeof(REMOTE_MAC)]; + char remote_ipv4[MAX_REMOTE_IPV4_SIZE]; + snprintf_check(remote_mac, sizeof(remote_mac), REMOTE_MAC); + snprintf_check(remote_ipv4, sizeof(remote_ipv4), REMOTE_IPV4, tun_id); + execute_command(0, "arp -s %s %s", remote_ipv4, remote_mac); + + // Set up a static ipv6 address for the interface + char local_ipv6[MAX_LOCAL_IPV6_SIZE]; + snprintf_check(local_ipv6, sizeof(local_ipv6), LOCAL_IPV6, tun_id); + execute_command(1, "ifconfig %s inet6 %s", tun_iface, local_ipv6); + + // Registers an NDP entry for the remote MAC with the remote ipv6 address + char remote_ipv6[MAX_REMOTE_IPV6_SIZE]; + snprintf_check(remote_ipv6, sizeof(remote_ipv6), REMOTE_IPV6, tun_id); + execute_command(0, "ndp -s %s%%%s %s", remote_ipv6, tun_iface, remote_mac); +} + +#endif // SYZ_EXECUTOR || SYZ_NET_INJECTION + +#if SYZ_EXECUTOR || __NR_syz_emit_ethernet && SYZ_NET_INJECTION +#include <sys/uio.h> + +static long syz_emit_ethernet(volatile long a0, volatile long a1) +{ + // syz_emit_ethernet(len len[packet], packet ptr[in, array[int8]]) + if (tunfd < 0) + return (uintptr_t)-1; + + size_t length = a0; + const char* data = (char*)a1; + debug_dump_data(data, length); + + return write(tunfd, data, length); +} +#endif + +#if SYZ_EXECUTOR || SYZ_NET_INJECTION && (__NR_syz_extract_tcp_res || SYZ_REPEAT) +#include <errno.h> + +static int read_tun(char* data, int size) +{ + if (tunfd < 0) + return -1; + + int rv = read(tunfd, data, size); + if (rv < 0) { + if (errno == EAGAIN) + return -1; + fail("tun: read failed"); + } + return rv; +} +#endif + +#if SYZ_EXECUTOR || __NR_syz_extract_tcp_res && SYZ_NET_INJECTION + +struct tcp_resources { + uint32 seq; + uint32 ack; +}; + +#include <net/ethertypes.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/tcp.h> + +// Include order matters, empty line prevent re-sorting. See a workaround in +// pkg/csource hoistIncludes. +#include <netinet/if_ether.h> + +static long syz_extract_tcp_res(volatile long a0, volatile long a1, volatile long a2) +{ + // syz_extract_tcp_res(res ptr[out, tcp_resources], seq_inc int32, ack_inc int32) + + if (tunfd < 0) + return (uintptr_t)-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. + char data[1000]; + int rv = read_tun(&data[0], sizeof(data)); + if (rv == -1) + return (uintptr_t)-1; + size_t length = rv; + debug_dump_data(data, length); + + if (length < sizeof(struct ether_header)) + return (uintptr_t)-1; + struct ether_header* ethhdr = (struct ether_header*)&data[0]; + + struct tcphdr* tcphdr = 0; + if (ethhdr->ether_type == htons(ETHERTYPE_IP)) { + if (length < sizeof(struct ether_header) + sizeof(struct ip)) + return (uintptr_t)-1; + struct ip* iphdr = (struct ip*)&data[sizeof(struct ether_header)]; + if (iphdr->ip_p != IPPROTO_TCP) + return (uintptr_t)-1; + if (length < sizeof(struct ether_header) + iphdr->ip_hl * 4 + sizeof(struct tcphdr)) + return (uintptr_t)-1; + tcphdr = (struct tcphdr*)&data[sizeof(struct ether_header) + iphdr->ip_hl * 4]; + } else { + if (length < sizeof(struct ether_header) + sizeof(struct ip6_hdr)) + return (uintptr_t)-1; + struct ip6_hdr* ipv6hdr = (struct ip6_hdr*)&data[sizeof(struct ether_header)]; + // TODO: parse and skip extension headers. + if (ipv6hdr->ip6_nxt != IPPROTO_TCP) + return (uintptr_t)-1; + if (length < sizeof(struct ether_header) + sizeof(struct ip6_hdr) + sizeof(struct tcphdr)) + return (uintptr_t)-1; + tcphdr = (struct tcphdr*)&data[sizeof(struct ether_header) + sizeof(struct ip6_hdr)]; + } + + struct tcp_resources* res = (struct tcp_resources*)a0; + res->seq = htonl(ntohl(tcphdr->th_seq) + (uint32)a1); + res->ack = htonl(ntohl(tcphdr->th_ack) + (uint32)a2); + + debug("extracted seq: %08x\n", res->seq); + debug("extracted ack: %08x\n", res->ack); + + return 0; +} +#endif + +#if SYZ_EXECUTOR || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NONE + +#include <sys/resource.h> + +static void sandbox_common() +{ +#if !SYZ_THREADED +#if SYZ_EXECUTOR + if (!flag_threaded) +#endif + if (setsid() == -1) + fail("setsid failed"); +#endif + + // Some minimal sandboxing. + struct rlimit 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); + rlim.rlim_cur = rlim.rlim_max = 256; // see kMaxFd + setrlimit(RLIMIT_NOFILE, &rlim); +} +#endif // SYZ_EXECUTOR || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NONE + +#if SYZ_EXECUTOR || SYZ_SANDBOX_NONE + +static void loop(); + +static int do_sandbox_none(void) +{ + sandbox_common(); +#if SYZ_EXECUTOR || SYZ_NET_INJECTION + initialize_tun(procid); +#endif + loop(); + return 0; +} +#endif // SYZ_EXECUTOR || SYZ_SANDBOX_NONE + +#if SYZ_EXECUTOR || SYZ_SANDBOX_SETUID + +#include <sys/wait.h> + +static void loop(); + +static int wait_for_loop(int pid) +{ + if (pid < 0) + fail("sandbox fork failed"); + debug("spawned loop pid %d\n", pid); + int status = 0; + while (waitpid(-1, &status, WUNTRACED) != pid) { + } + return WEXITSTATUS(status); +} + +#define SYZ_HAVE_SANDBOX_SETUID 1 +static int do_sandbox_setuid(void) +{ + int pid = fork(); + if (pid != 0) + return wait_for_loop(pid); + + sandbox_common(); +#if SYZ_EXECUTOR || SYZ_NET_INJECTION + initialize_tun(procid); +#endif + + char pwbuf[1024]; + struct passwd *pw, pwres; + if (getpwnam_r("nobody", &pwres, pwbuf, sizeof(pwbuf), &pw) != 0 || !pw) + fail("getpwnam_r(\"nobody\") failed"); + + if (setgroups(0, NULL)) + fail("failed to setgroups"); + if (setgid(pw->pw_gid)) + fail("failed to setgid"); + if (setuid(pw->pw_uid)) + fail("failed to setuid"); + + loop(); + doexit(1); +} +#endif // SYZ_EXECUTOR || SYZ_SANDBOX_SETUID |
