github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/executor/common_openbsd.h (about)

     1  // Copyright 2017 syzkaller project authors. All rights reserved.
     2  // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  
     4  // This file is shared between executor and csource package.
     5  
     6  #include <unistd.h>
     7  
     8  #include <pwd.h>
     9  #include <stdarg.h>
    10  #include <stdbool.h>
    11  #include <string.h>
    12  #include <sys/syscall.h>
    13  
    14  // Needed syscall libc stubs.
    15  #include <dirent.h>
    16  #include <fcntl.h>
    17  #include <poll.h>
    18  #include <sys/event.h>
    19  #include <sys/ioctl.h>
    20  #include <sys/ktrace.h>
    21  #include <sys/mman.h>
    22  #include <sys/msg.h>
    23  #include <sys/sem.h>
    24  #include <sys/shm.h>
    25  #include <sys/socket.h>
    26  #include <sys/stat.h>
    27  #include <sys/sysctl.h>
    28  #include <sys/syslog.h>
    29  
    30  #define CAST
    31  
    32  #if (SYZ_EXECUTOR || __NR_syz_open_pts)
    33  #include <termios.h>
    34  #include <util.h>
    35  
    36  static uintptr_t syz_open_pts(void)
    37  {
    38  	int master, slave;
    39  
    40  	if (openpty(&master, &slave, NULL, NULL, NULL) == -1)
    41  		return -1;
    42  	// Move the master fd up in order to reduce the chances of the fuzzer
    43  	// generating a call to close(2) with the same fd.
    44  	if (dup2(master, master + 100) != -1)
    45  		close(master);
    46  	return slave;
    47  }
    48  #endif // (SYZ_EXECUTOR || __NR_syz_open_pts)
    49  
    50  #if SYZ_EXECUTOR || SYZ_NET_INJECTION
    51  
    52  #include <net/if_tun.h>
    53  #include <sys/types.h>
    54  
    55  static int tunfd = -1;
    56  
    57  #define MAX_TUN 8
    58  
    59  // Because the interface and device name contain an int, use MAXINT to determine
    60  // the maximum size of the string.
    61  // Since on *BSD  sizeof(int) is 4, MAXINT is 2147483647.
    62  #define TUN_IFACE "tap%d"
    63  #define MAX_TUN_IFACE_SIZE sizeof("tap2147483647")
    64  #define TUN_DEVICE "/dev/tap%d"
    65  #define MAX_TUN_DEVICE_SIZE sizeof("/dev/tap2147483647")
    66  
    67  #define LOCAL_MAC "aa:aa:aa:aa:aa:aa"
    68  #define REMOTE_MAC "aa:aa:aa:aa:aa:bb"
    69  #define LOCAL_IPV4 "172.20.%d.170"
    70  #define MAX_LOCAL_IPV4_SIZE sizeof("172.20.255.170")
    71  #define REMOTE_IPV4 "172.20.%d.187"
    72  #define MAX_REMOTE_IPV4_SIZE sizeof("172.20.255.187")
    73  #define LOCAL_IPV6 "fe80::%02xaa"
    74  #define MAX_LOCAL_IPV6_SIZE sizeof("fe80::ffaa")
    75  #define REMOTE_IPV6 "fe80::%02xbb"
    76  #define MAX_REMOTE_IPV6_SIZE sizeof("fe80::ffbb")
    77  
    78  static void vsnprintf_check(char* str, size_t size, const char* format, va_list args)
    79  {
    80  	int rv = vsnprintf(str, size, format, args);
    81  	if (rv < 0)
    82  		fail("vsnprintf failed");
    83  	if ((size_t)rv >= size)
    84  		failmsg("vsnprintf: string doesn't fit into buffer", "string='%s'", str);
    85  }
    86  
    87  static void snprintf_check(char* str, size_t size, const char* format, ...)
    88  {
    89  	va_list args;
    90  
    91  	va_start(args, format);
    92  	vsnprintf_check(str, size, format, args);
    93  	va_end(args);
    94  }
    95  
    96  #define COMMAND_MAX_LEN 128
    97  #define PATH_PREFIX "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin "
    98  #define PATH_PREFIX_LEN (sizeof(PATH_PREFIX) - 1)
    99  
   100  static void execute_command(bool panic, const char* format, ...)
   101  {
   102  	va_list args;
   103  	va_start(args, format);
   104  	// Executor process does not have any env, including PATH.
   105  	// On some distributions, system/shell adds a minimal PATH, on some it does not.
   106  	// Set own standard PATH to make it work across distributions.
   107  	char command[PATH_PREFIX_LEN + COMMAND_MAX_LEN];
   108  	memcpy(command, PATH_PREFIX, PATH_PREFIX_LEN);
   109  	vsnprintf_check(command + PATH_PREFIX_LEN, COMMAND_MAX_LEN, format, args);
   110  	va_end(args);
   111  	int rv = system(command);
   112  	if (rv) {
   113  		if (panic)
   114  			failmsg("command failed", "command=%s: %d", &command[0], rv);
   115  		debug("command '%s': %d\n", &command[0], rv);
   116  	}
   117  }
   118  
   119  static void initialize_tun(int tun_id)
   120  {
   121  #if SYZ_EXECUTOR
   122  	if (!flag_net_injection)
   123  		return;
   124  #endif // SYZ_EXECUTOR
   125  
   126  	if (tun_id < 0 || tun_id >= MAX_TUN)
   127  		failmsg("tun_id out of range", "tun_id=%d", tun_id);
   128  
   129  	char tun_device[MAX_TUN_DEVICE_SIZE];
   130  	snprintf_check(tun_device, sizeof(tun_device), TUN_DEVICE, tun_id);
   131  
   132  	char tun_iface[MAX_TUN_IFACE_SIZE];
   133  	snprintf_check(tun_iface, sizeof(tun_iface), TUN_IFACE, tun_id);
   134  
   135  	execute_command(0, "ifconfig %s destroy", tun_iface);
   136  
   137  	tunfd = open(tun_device, O_RDWR | O_NONBLOCK);
   138  	if (tunfd == -1) {
   139  #if SYZ_EXECUTOR
   140  		failmsg("tun: can't open device", "device=%s", tun_device);
   141  #else
   142  		printf("tun: can't open %s: errno=%d\n", tun_device, errno);
   143  		return;
   144  #endif // SYZ_EXECUTOR
   145  	}
   146  	// Remap tun onto higher fd number to hide it from fuzzer and to keep
   147  	// fd numbers stable regardless of whether tun is opened or not (also see kMaxFd).
   148  	const int kTunFd = 200;
   149  	if (dup2(tunfd, kTunFd) < 0)
   150  		fail("dup2(tunfd, kTunFd) failed");
   151  	close(tunfd);
   152  	tunfd = kTunFd;
   153  
   154  	char local_mac[sizeof(LOCAL_MAC)];
   155  	snprintf_check(local_mac, sizeof(local_mac), LOCAL_MAC);
   156  
   157  	// Set the MAC address of the interface to LOCAL_MAC
   158  	execute_command(1, "ifconfig %s lladdr %s", tun_iface, local_mac);
   159  
   160  	// Setting up a static ip for the interface
   161  	char local_ipv4[MAX_LOCAL_IPV4_SIZE];
   162  	snprintf_check(local_ipv4, sizeof(local_ipv4), LOCAL_IPV4, tun_id);
   163  	execute_command(1, "ifconfig %s inet %s netmask 255.255.255.0", tun_iface, local_ipv4);
   164  
   165  	// Creates an ARP table entry for the remote ip and MAC address
   166  	char remote_mac[sizeof(REMOTE_MAC)];
   167  	char remote_ipv4[MAX_REMOTE_IPV4_SIZE];
   168  	snprintf_check(remote_mac, sizeof(remote_mac), REMOTE_MAC);
   169  	snprintf_check(remote_ipv4, sizeof(remote_ipv4), REMOTE_IPV4, tun_id);
   170  	execute_command(0, "arp -s %s %s", remote_ipv4, remote_mac);
   171  
   172  	// Set up a static ipv6 address for the interface
   173  	char local_ipv6[MAX_LOCAL_IPV6_SIZE];
   174  	snprintf_check(local_ipv6, sizeof(local_ipv6), LOCAL_IPV6, tun_id);
   175  	execute_command(1, "ifconfig %s inet6 %s", tun_iface, local_ipv6);
   176  
   177  	// Registers an NDP entry for the remote MAC with the remote ipv6 address
   178  	char remote_ipv6[MAX_REMOTE_IPV6_SIZE];
   179  	snprintf_check(remote_ipv6, sizeof(remote_ipv6), REMOTE_IPV6, tun_id);
   180  	execute_command(0, "ndp -s %s%%%s %s", remote_ipv6, tun_iface, remote_mac);
   181  }
   182  
   183  #endif // SYZ_EXECUTOR || SYZ_NET_INJECTION
   184  
   185  #if SYZ_EXECUTOR || __NR_syz_emit_ethernet && SYZ_NET_INJECTION
   186  #include <sys/uio.h>
   187  
   188  static long syz_emit_ethernet(volatile long a0, volatile long a1)
   189  {
   190  	// syz_emit_ethernet(len len[packet], packet ptr[in, array[int8]])
   191  	if (tunfd < 0)
   192  		return (uintptr_t)-1;
   193  
   194  	size_t length = a0;
   195  	const char* data = (char*)a1;
   196  	debug_dump_data(data, length);
   197  
   198  	return write(tunfd, data, length);
   199  }
   200  #endif
   201  
   202  #if SYZ_EXECUTOR || SYZ_NET_INJECTION && (__NR_syz_extract_tcp_res || SYZ_REPEAT)
   203  #include <errno.h>
   204  
   205  static int read_tun(char* data, int size)
   206  {
   207  	if (tunfd < 0)
   208  		return -1;
   209  
   210  	int rv = read(tunfd, data, size);
   211  	if (rv < 0) {
   212  		if (errno == EAGAIN)
   213  			return -1;
   214  		fail("tun: read failed");
   215  	}
   216  	return rv;
   217  }
   218  #endif
   219  
   220  #if SYZ_EXECUTOR || __NR_syz_extract_tcp_res && SYZ_NET_INJECTION
   221  
   222  struct tcp_resources {
   223  	uint32 seq;
   224  	uint32 ack;
   225  };
   226  
   227  #include <net/ethertypes.h>
   228  #include <net/if.h>
   229  #include <net/if_arp.h>
   230  #include <netinet/in.h>
   231  #include <netinet/ip.h>
   232  #include <netinet/ip6.h>
   233  #include <netinet/tcp.h>
   234  
   235  // Include order matters, empty line prevent re-sorting. See a workaround in
   236  // pkg/csource hoistIncludes.
   237  #include <netinet/if_ether.h>
   238  
   239  static long syz_extract_tcp_res(volatile long a0, volatile long a1, volatile long a2)
   240  {
   241  	// syz_extract_tcp_res(res ptr[out, tcp_resources], seq_inc int32, ack_inc int32)
   242  
   243  	if (tunfd < 0)
   244  		return (uintptr_t)-1;
   245  
   246  	// We just need this to be large enough to hold headers that we parse (ethernet/ip/tcp).
   247  	// Rest of the packet (if any) will be silently truncated which is fine.
   248  	char data[1000];
   249  	int rv = read_tun(&data[0], sizeof(data));
   250  	if (rv == -1)
   251  		return (uintptr_t)-1;
   252  	size_t length = rv;
   253  	debug_dump_data(data, length);
   254  
   255  	if (length < sizeof(struct ether_header))
   256  		return (uintptr_t)-1;
   257  	struct ether_header* ethhdr = (struct ether_header*)&data[0];
   258  
   259  	struct tcphdr* tcphdr = 0;
   260  	if (ethhdr->ether_type == htons(ETHERTYPE_IP)) {
   261  		if (length < sizeof(struct ether_header) + sizeof(struct ip))
   262  			return (uintptr_t)-1;
   263  		struct ip* iphdr = (struct ip*)&data[sizeof(struct ether_header)];
   264  		if (iphdr->ip_p != IPPROTO_TCP)
   265  			return (uintptr_t)-1;
   266  		if (length < sizeof(struct ether_header) + iphdr->ip_hl * 4 + sizeof(struct tcphdr))
   267  			return (uintptr_t)-1;
   268  		tcphdr = (struct tcphdr*)&data[sizeof(struct ether_header) + iphdr->ip_hl * 4];
   269  	} else {
   270  		if (length < sizeof(struct ether_header) + sizeof(struct ip6_hdr))
   271  			return (uintptr_t)-1;
   272  		struct ip6_hdr* ipv6hdr = (struct ip6_hdr*)&data[sizeof(struct ether_header)];
   273  		// TODO: parse and skip extension headers.
   274  		if (ipv6hdr->ip6_nxt != IPPROTO_TCP)
   275  			return (uintptr_t)-1;
   276  		if (length < sizeof(struct ether_header) + sizeof(struct ip6_hdr) + sizeof(struct tcphdr))
   277  			return (uintptr_t)-1;
   278  		tcphdr = (struct tcphdr*)&data[sizeof(struct ether_header) + sizeof(struct ip6_hdr)];
   279  	}
   280  
   281  	struct tcp_resources* res = (struct tcp_resources*)a0;
   282  	res->seq = htonl(ntohl(tcphdr->th_seq) + (uint32)a1);
   283  	res->ack = htonl(ntohl(tcphdr->th_ack) + (uint32)a2);
   284  
   285  	debug("extracted seq: %08x\n", res->seq);
   286  	debug("extracted ack: %08x\n", res->ack);
   287  
   288  	return 0;
   289  }
   290  #endif
   291  
   292  #if SYZ_EXECUTOR || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NONE
   293  
   294  #include <sys/resource.h>
   295  
   296  static void sandbox_common()
   297  {
   298  #if !SYZ_THREADED
   299  #if SYZ_EXECUTOR
   300  	if (!flag_threaded)
   301  #endif
   302  		if (setsid() == -1)
   303  			fail("setsid failed");
   304  #endif
   305  
   306  	// Some minimal sandboxing.
   307  	struct rlimit rlim;
   308  	rlim.rlim_cur = rlim.rlim_max = 8 << 20;
   309  	setrlimit(RLIMIT_MEMLOCK, &rlim);
   310  	rlim.rlim_cur = rlim.rlim_max = 1 << 20;
   311  	setrlimit(RLIMIT_FSIZE, &rlim);
   312  	rlim.rlim_cur = rlim.rlim_max = 1 << 20;
   313  	setrlimit(RLIMIT_STACK, &rlim);
   314  	rlim.rlim_cur = rlim.rlim_max = 0;
   315  	setrlimit(RLIMIT_CORE, &rlim);
   316  	rlim.rlim_cur = rlim.rlim_max = 256; // see kMaxFd
   317  	setrlimit(RLIMIT_NOFILE, &rlim);
   318  }
   319  #endif //  SYZ_EXECUTOR || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NONE
   320  
   321  #if SYZ_EXECUTOR || SYZ_SANDBOX_NONE
   322  
   323  static void loop();
   324  
   325  static int do_sandbox_none(void)
   326  {
   327  	sandbox_common();
   328  #if SYZ_EXECUTOR || SYZ_NET_INJECTION
   329  	initialize_tun(procid);
   330  #endif
   331  	loop();
   332  	return 0;
   333  }
   334  #endif // SYZ_EXECUTOR || SYZ_SANDBOX_NONE
   335  
   336  #if SYZ_EXECUTOR || SYZ_SANDBOX_SETUID
   337  
   338  #include <sys/wait.h>
   339  
   340  static void loop();
   341  
   342  static int wait_for_loop(int pid)
   343  {
   344  	if (pid < 0)
   345  		fail("sandbox fork failed");
   346  	debug("spawned loop pid %d\n", pid);
   347  	int status = 0;
   348  	while (waitpid(-1, &status, WUNTRACED) != pid) {
   349  	}
   350  	return WEXITSTATUS(status);
   351  }
   352  
   353  #define SYZ_HAVE_SANDBOX_SETUID 1
   354  static int do_sandbox_setuid(void)
   355  {
   356  	int pid = fork();
   357  	if (pid != 0)
   358  		return wait_for_loop(pid);
   359  
   360  	sandbox_common();
   361  #if SYZ_EXECUTOR || SYZ_NET_INJECTION
   362  	initialize_tun(procid);
   363  #endif
   364  
   365  	char pwbuf[1024];
   366  	struct passwd *pw, pwres;
   367  	if (getpwnam_r("nobody", &pwres, pwbuf, sizeof(pwbuf), &pw) != 0 || !pw)
   368  		fail("getpwnam_r(\"nobody\") failed");
   369  
   370  	if (setgroups(0, NULL))
   371  		fail("failed to setgroups");
   372  	if (setgid(pw->pw_gid))
   373  		fail("failed to setgid");
   374  	if (setuid(pw->pw_uid))
   375  		fail("failed to setuid");
   376  
   377  	loop();
   378  	doexit(1);
   379  }
   380  #endif // SYZ_EXECUTOR || SYZ_SANDBOX_SETUID