aboutsummaryrefslogtreecommitdiffstats
path: root/executor
diff options
context:
space:
mode:
authorGreg Steuck <greg@nest.cx>2023-04-25 17:14:16 +0100
committerGreg Steuck <blackgnezdo@gmail.com>2023-04-27 09:52:01 -0700
commita18ed5a24b4dd24f8b3e9ac673347e182c9de81a (patch)
treecab47faef2ad291ac2db8428919aa3ebd5d49bd1 /executor
parent59e959932704c7a500e97fda11bbae7ccca50020 (diff)
executor: detangle common_openbsd.h out of common_bsd.h
Diffstat (limited to 'executor')
-rw-r--r--executor/common.h4
-rw-r--r--executor/common_openbsd.h380
2 files changed, 383 insertions, 1 deletions
diff --git a/executor/common.h b/executor/common.h
index a47e6694e..26d964b65 100644
--- a/executor/common.h
+++ b/executor/common.h
@@ -493,8 +493,10 @@ static uint16 csum_inet_digest(struct csum_inet* csum)
#if GOOS_akaros
#include "common_akaros.h"
-#elif GOOS_freebsd || GOOS_darwin || GOOS_netbsd || GOOS_openbsd
+#elif GOOS_freebsd || GOOS_darwin || GOOS_netbsd
#include "common_bsd.h"
+#elif GOOS_openbsd
+#include "common_openbsd.h"
#elif GOOS_fuchsia
#include "common_fuchsia.h"
#elif GOOS_linux
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