github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/executor/common_bsd.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  #if GOOS_netbsd
    15  
    16  #if SYZ_EXECUTOR || __NR_syz_usb_connect || __NR_syz_usb_disconnect
    17  #include "common_usb_netbsd.h"
    18  #endif
    19  #if SYZ_EXECUTOR || SYZ_USB
    20  #include <dirent.h>
    21  static void setup_usb(void)
    22  {
    23  	DIR* dir = opendir("/dev");
    24  	if (dir == NULL)
    25  		fail("failed to open /dev");
    26  
    27  	bool have_vhci = false;
    28  	struct dirent* ent = NULL;
    29  	while ((ent = readdir(dir)) != NULL) {
    30  		if (ent->d_type != DT_CHR)
    31  			continue;
    32  		if (strncmp(ent->d_name, "vhci", 4))
    33  			continue;
    34  		char path[1024];
    35  		snprintf(path, sizeof(path), "/dev/%s", ent->d_name);
    36  		if (chmod(path, 0666))
    37  			failmsg("failed to chmod vhci", "path=%s", path);
    38  		have_vhci = true;
    39  	}
    40  	if (!have_vhci)
    41  		fail("don't have any /dev/vhci devices");
    42  
    43  	closedir(dir);
    44  }
    45  #endif
    46  
    47  #if SYZ_EXECUTOR || SYZ_FAULT
    48  #include <fcntl.h>
    49  #include <sys/fault.h>
    50  #include <sys/stat.h>
    51  static void setup_fault(void)
    52  {
    53  	if (chmod("/dev/fault", 0666))
    54  		fail("failed to chmod /dev/fault");
    55  }
    56  static int inject_fault(int nth)
    57  {
    58  	struct fault_ioc_enable en;
    59  	int fd;
    60  
    61  	fd = open("/dev/fault", O_RDWR);
    62  	if (fd == -1)
    63  		fail("failed to open /dev/fault");
    64  
    65  	en.scope = FAULT_SCOPE_LWP;
    66  	en.mode = 0; // FAULT_MODE_NTH_ONESHOT
    67  	en.nth = nth + 1; // FAULT_NTH_MIN
    68  	if (ioctl(fd, FAULT_IOC_ENABLE, &en) != 0)
    69  		failmsg("FAULT_IOC_ENABLE failed", "nth=%d", nth);
    70  
    71  	return fd;
    72  }
    73  #endif
    74  #if SYZ_EXECUTOR
    75  static int fault_injected(int fd)
    76  {
    77  	struct fault_ioc_getinfo info;
    78  	struct fault_ioc_disable dis;
    79  	int res;
    80  
    81  	info.scope = FAULT_SCOPE_LWP;
    82  	if (ioctl(fd, FAULT_IOC_GETINFO, &info) != 0)
    83  		fail("FAULT_IOC_GETINFO failed");
    84  	res = (info.nfaults > 0);
    85  
    86  	dis.scope = FAULT_SCOPE_LWP;
    87  	if (ioctl(fd, FAULT_IOC_DISABLE, &dis) != 0)
    88  		fail("FAULT_IOC_DISABLE failed");
    89  
    90  	close(fd);
    91  
    92  	return res;
    93  }
    94  #endif
    95  
    96  #endif
    97  
    98  #if GOOS_darwin
    99  #define __syscall syscall
   100  #endif //  GOOS_darwin
   101  
   102  #if SYZ_EXECUTOR || SYZ_NET_INJECTION
   103  
   104  #include <fcntl.h>
   105  // FIXME(HerrSpace): XNU has xnu/bsd/netinet/if_tun.h, but it's not shipped in
   106  // the installed header files (probably internal). It's also likely not very
   107  // interesting, as XNU only ships a tun driver called utun, not tap which is
   108  // currently required for SYZ_NET_INJECTION anyhow.
   109  #if !GOOS_darwin
   110  #include <net/if_tun.h>
   111  #endif // !GOOS_darwin
   112  #include <sys/types.h>
   113  
   114  static int tunfd = -1;
   115  
   116  #if GOOS_netbsd
   117  // Increased number of tap and tun devices if image script is used
   118  #define MAX_TUN 64
   119  #elif GOOS_freebsd
   120  // The maximum number of tun devices is limited by the way IP addresses
   121  // are assigned. Based on this, the limit is 256.
   122  #define MAX_TUN 256
   123  #else
   124  // Maximum number of tun devices in the default install.
   125  #define MAX_TUN 4
   126  #endif
   127  
   128  // Because the interface and device name contain an int, use MAXINT to determine
   129  // the maximum size of the string.
   130  // Since on *BSD  sizeof(int) is 4, MAXINT is 2147483647.
   131  #define TUN_IFACE "tap%d"
   132  #define MAX_TUN_IFACE_SIZE sizeof("tap2147483647")
   133  #define TUN_DEVICE "/dev/tap%d"
   134  #define MAX_TUN_DEVICE_SIZE sizeof("/dev/tap2147483647")
   135  
   136  #define LOCAL_MAC "aa:aa:aa:aa:aa:aa"
   137  #define REMOTE_MAC "aa:aa:aa:aa:aa:bb"
   138  #define LOCAL_IPV4 "172.20.%d.170"
   139  #define MAX_LOCAL_IPV4_SIZE sizeof("172.20.255.170")
   140  #define REMOTE_IPV4 "172.20.%d.187"
   141  #define MAX_REMOTE_IPV4_SIZE sizeof("172.20.255.187")
   142  #define LOCAL_IPV6 "fe80::%02xaa"
   143  #define MAX_LOCAL_IPV6_SIZE sizeof("fe80::ffaa")
   144  #define REMOTE_IPV6 "fe80::%02xbb"
   145  #define MAX_REMOTE_IPV6_SIZE sizeof("fe80::ffbb")
   146  
   147  static void vsnprintf_check(char* str, size_t size, const char* format, va_list args)
   148  {
   149  	int rv = vsnprintf(str, size, format, args);
   150  	if (rv < 0)
   151  		fail("vsnprintf failed");
   152  	if ((size_t)rv >= size)
   153  		failmsg("vsnprintf: string doesn't fit into buffer", "string='%s'", str);
   154  }
   155  
   156  static void snprintf_check(char* str, size_t size, const char* format, ...)
   157  {
   158  	va_list args;
   159  
   160  	va_start(args, format);
   161  	vsnprintf_check(str, size, format, args);
   162  	va_end(args);
   163  }
   164  
   165  #define COMMAND_MAX_LEN 128
   166  #define PATH_PREFIX "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin "
   167  #define PATH_PREFIX_LEN (sizeof(PATH_PREFIX) - 1)
   168  
   169  static void execute_command(bool panic, const char* format, ...)
   170  {
   171  	va_list args;
   172  	va_start(args, format);
   173  	// Executor process does not have any env, including PATH.
   174  	// On some distributions, system/shell adds a minimal PATH, on some it does not.
   175  	// Set own standard PATH to make it work across distributions.
   176  	char command[PATH_PREFIX_LEN + COMMAND_MAX_LEN];
   177  	memcpy(command, PATH_PREFIX, PATH_PREFIX_LEN);
   178  	vsnprintf_check(command + PATH_PREFIX_LEN, COMMAND_MAX_LEN, format, args);
   179  	va_end(args);
   180  	int rv = system(command);
   181  	if (rv) {
   182  		if (panic)
   183  			failmsg("command failed", "command=%s: %d", &command[0], rv);
   184  		debug("command '%s': %d\n", &command[0], rv);
   185  	}
   186  }
   187  
   188  static void initialize_tun(int tun_id)
   189  {
   190  #if SYZ_EXECUTOR
   191  	if (!flag_net_injection)
   192  		return;
   193  #endif // SYZ_EXECUTOR
   194  
   195  	if (tun_id < 0 || tun_id >= MAX_TUN)
   196  		failmsg("tun_id out of range", "tun_id=%d", tun_id);
   197  
   198  	char tun_device[MAX_TUN_DEVICE_SIZE];
   199  	snprintf_check(tun_device, sizeof(tun_device), TUN_DEVICE, tun_id);
   200  
   201  	char tun_iface[MAX_TUN_IFACE_SIZE];
   202  	snprintf_check(tun_iface, sizeof(tun_iface), TUN_IFACE, tun_id);
   203  
   204  #if GOOS_netbsd
   205  	// open(2) doesn't create an new tap/tun interface node
   206  	// so we use ifconfig to create the node. Might be casued due to regression
   207  	execute_command(0, "ifconfig %s destroy", tun_iface);
   208  	execute_command(0, "ifconfig %s create", tun_iface);
   209  #else
   210  	execute_command(0, "ifconfig %s destroy", tun_iface);
   211  #endif
   212  
   213  	tunfd = open(tun_device, O_RDWR | O_NONBLOCK);
   214  #if GOOS_freebsd
   215  	if ((tunfd < 0) && (errno == ENOENT)) {
   216  		execute_command(0, "kldload -q if_tap");
   217  		tunfd = open(tun_device, O_RDWR | O_NONBLOCK);
   218  	}
   219  #endif
   220  	if (tunfd == -1) {
   221  #if SYZ_EXECUTOR
   222  		failmsg("tun: can't open device", "device=%s", tun_device);
   223  #else
   224  		printf("tun: can't open %s: errno=%d\n", tun_device, errno);
   225  		return;
   226  #endif // SYZ_EXECUTOR
   227  	}
   228  	// Remap tun onto higher fd number to hide it from fuzzer and to keep
   229  	// fd numbers stable regardless of whether tun is opened or not (also see kMaxFd).
   230  	const int kTunFd = 200;
   231  	if (dup2(tunfd, kTunFd) < 0)
   232  		fail("dup2(tunfd, kTunFd) failed");
   233  	close(tunfd);
   234  	tunfd = kTunFd;
   235  
   236  	char local_mac[sizeof(LOCAL_MAC)];
   237  	snprintf_check(local_mac, sizeof(local_mac), LOCAL_MAC);
   238  
   239  	// Set the MAC address of the interface to LOCAL_MAC
   240  #if GOOS_netbsd
   241  	execute_command(1, "ifconfig %s link %s", tun_iface, local_mac);
   242  #else
   243  	execute_command(1, "ifconfig %s ether %s", tun_iface, local_mac);
   244  #endif
   245  
   246  	// Setting up a static ip for the interface
   247  	char local_ipv4[MAX_LOCAL_IPV4_SIZE];
   248  	snprintf_check(local_ipv4, sizeof(local_ipv4), LOCAL_IPV4, tun_id);
   249  	execute_command(1, "ifconfig %s inet %s netmask 255.255.255.0", tun_iface, local_ipv4);
   250  
   251  	// Creates an ARP table entry for the remote ip and MAC address
   252  	char remote_mac[sizeof(REMOTE_MAC)];
   253  	char remote_ipv4[MAX_REMOTE_IPV4_SIZE];
   254  	snprintf_check(remote_mac, sizeof(remote_mac), REMOTE_MAC);
   255  	snprintf_check(remote_ipv4, sizeof(remote_ipv4), REMOTE_IPV4, tun_id);
   256  	execute_command(0, "arp -s %s %s", remote_ipv4, remote_mac);
   257  
   258  	// Set up a static ipv6 address for the interface
   259  	char local_ipv6[MAX_LOCAL_IPV6_SIZE];
   260  	snprintf_check(local_ipv6, sizeof(local_ipv6), LOCAL_IPV6, tun_id);
   261  	execute_command(1, "ifconfig %s inet6 %s", tun_iface, local_ipv6);
   262  
   263  	// Registers an NDP entry for the remote MAC with the remote ipv6 address
   264  	char remote_ipv6[MAX_REMOTE_IPV6_SIZE];
   265  	snprintf_check(remote_ipv6, sizeof(remote_ipv6), REMOTE_IPV6, tun_id);
   266  	execute_command(0, "ndp -s %s%%%s %s", remote_ipv6, tun_iface, remote_mac);
   267  }
   268  
   269  #endif // SYZ_EXECUTOR || SYZ_NET_INJECTION
   270  
   271  #if !GOOS_darwin && SYZ_EXECUTOR || __NR_syz_emit_ethernet && SYZ_NET_INJECTION
   272  #include <stdbool.h>
   273  #include <sys/uio.h>
   274  
   275  static long syz_emit_ethernet(volatile long a0, volatile long a1)
   276  {
   277  	// syz_emit_ethernet(len len[packet], packet ptr[in, array[int8]])
   278  	if (tunfd < 0)
   279  		return (uintptr_t)-1;
   280  
   281  	size_t length = a0;
   282  	const char* data = (char*)a1;
   283  	debug_dump_data(data, length);
   284  
   285  	return write(tunfd, data, length);
   286  }
   287  #endif
   288  
   289  #if !GOOS_darwin && SYZ_EXECUTOR || SYZ_NET_INJECTION && (__NR_syz_extract_tcp_res || SYZ_REPEAT)
   290  #include <errno.h>
   291  
   292  static int read_tun(char* data, int size)
   293  {
   294  	if (tunfd < 0)
   295  		return -1;
   296  
   297  	int rv = read(tunfd, data, size);
   298  	if (rv < 0) {
   299  		if (errno == EAGAIN)
   300  			return -1;
   301  		fail("tun: read failed");
   302  	}
   303  	return rv;
   304  }
   305  #endif
   306  
   307  #if !GOOS_darwin && SYZ_EXECUTOR || __NR_syz_extract_tcp_res && SYZ_NET_INJECTION
   308  
   309  struct tcp_resources {
   310  	uint32 seq;
   311  	uint32 ack;
   312  };
   313  
   314  #if GOOS_freebsd || GOOS_darwin
   315  #include <net/ethernet.h>
   316  #else
   317  #include <net/ethertypes.h>
   318  #endif
   319  #include <net/if.h>
   320  #include <net/if_arp.h>
   321  #include <netinet/in.h>
   322  #include <netinet/ip.h>
   323  #include <netinet/ip6.h>
   324  #include <netinet/tcp.h>
   325  
   326  // Include order matters, empty line prevent re-sorting. See a workaround in
   327  // pkg/csource hoistIncludes.
   328  #include <netinet/if_ether.h>
   329  
   330  static long syz_extract_tcp_res(volatile long a0, volatile long a1, volatile long a2)
   331  {
   332  	// syz_extract_tcp_res(res ptr[out, tcp_resources], seq_inc int32, ack_inc int32)
   333  
   334  	if (tunfd < 0)
   335  		return (uintptr_t)-1;
   336  
   337  	// We just need this to be large enough to hold headers that we parse (ethernet/ip/tcp).
   338  	// Rest of the packet (if any) will be silently truncated which is fine.
   339  	char data[1000];
   340  	int rv = read_tun(&data[0], sizeof(data));
   341  	if (rv == -1)
   342  		return (uintptr_t)-1;
   343  	size_t length = rv;
   344  	debug_dump_data(data, length);
   345  
   346  	if (length < sizeof(struct ether_header))
   347  		return (uintptr_t)-1;
   348  	struct ether_header* ethhdr = (struct ether_header*)&data[0];
   349  
   350  	struct tcphdr* tcphdr = 0;
   351  	if (ethhdr->ether_type == htons(ETHERTYPE_IP)) {
   352  		if (length < sizeof(struct ether_header) + sizeof(struct ip))
   353  			return (uintptr_t)-1;
   354  		struct ip* iphdr = (struct ip*)&data[sizeof(struct ether_header)];
   355  		if (iphdr->ip_p != IPPROTO_TCP)
   356  			return (uintptr_t)-1;
   357  		if (length < sizeof(struct ether_header) + iphdr->ip_hl * 4 + sizeof(struct tcphdr))
   358  			return (uintptr_t)-1;
   359  		tcphdr = (struct tcphdr*)&data[sizeof(struct ether_header) + iphdr->ip_hl * 4];
   360  	} else {
   361  		if (length < sizeof(struct ether_header) + sizeof(struct ip6_hdr))
   362  			return (uintptr_t)-1;
   363  		struct ip6_hdr* ipv6hdr = (struct ip6_hdr*)&data[sizeof(struct ether_header)];
   364  		// TODO: parse and skip extension headers.
   365  		if (ipv6hdr->ip6_nxt != IPPROTO_TCP)
   366  			return (uintptr_t)-1;
   367  		if (length < sizeof(struct ether_header) + sizeof(struct ip6_hdr) + sizeof(struct tcphdr))
   368  			return (uintptr_t)-1;
   369  		tcphdr = (struct tcphdr*)&data[sizeof(struct ether_header) + sizeof(struct ip6_hdr)];
   370  	}
   371  
   372  	struct tcp_resources* res = (struct tcp_resources*)a0;
   373  	res->seq = htonl(ntohl(tcphdr->th_seq) + (uint32)a1);
   374  	res->ack = htonl(ntohl(tcphdr->th_ack) + (uint32)a2);
   375  
   376  	debug("extracted seq: %08x\n", res->seq);
   377  	debug("extracted ack: %08x\n", res->ack);
   378  
   379  	return 0;
   380  }
   381  #endif
   382  
   383  #if SYZ_EXECUTOR || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NONE
   384  
   385  #include <sys/resource.h>
   386  #include <unistd.h>
   387  
   388  static void sandbox_common()
   389  {
   390  #if !SYZ_THREADED
   391  #if SYZ_EXECUTOR
   392  	if (!flag_threaded)
   393  #endif
   394  		if (setsid() == -1)
   395  			fail("setsid failed");
   396  #endif
   397  
   398  	// Some minimal sandboxing.
   399  	struct rlimit rlim;
   400  #ifdef GOOS_freebsd
   401  	// This also causes ENOMEM on NetBSD during early init.
   402  	rlim.rlim_cur = rlim.rlim_max = 128 << 20;
   403  	setrlimit(RLIMIT_AS, &rlim);
   404  #endif
   405  	rlim.rlim_cur = rlim.rlim_max = 8 << 20;
   406  	setrlimit(RLIMIT_MEMLOCK, &rlim);
   407  	rlim.rlim_cur = rlim.rlim_max = 1 << 20;
   408  	setrlimit(RLIMIT_FSIZE, &rlim);
   409  	rlim.rlim_cur = rlim.rlim_max = 1 << 20;
   410  	setrlimit(RLIMIT_STACK, &rlim);
   411  	rlim.rlim_cur = rlim.rlim_max = 0;
   412  	setrlimit(RLIMIT_CORE, &rlim);
   413  	rlim.rlim_cur = rlim.rlim_max = 256; // see kMaxFd
   414  	setrlimit(RLIMIT_NOFILE, &rlim);
   415  }
   416  #endif //  SYZ_EXECUTOR || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NONE
   417  
   418  #if SYZ_EXECUTOR || SYZ_SANDBOX_NONE
   419  
   420  static void loop();
   421  
   422  static int do_sandbox_none(void)
   423  {
   424  	sandbox_common();
   425  #if SYZ_EXECUTOR || SYZ_NET_INJECTION
   426  	initialize_tun(procid);
   427  #endif
   428  	loop();
   429  	return 0;
   430  }
   431  #endif // SYZ_EXECUTOR || SYZ_SANDBOX_NONE
   432  
   433  #if SYZ_EXECUTOR || SYZ_SANDBOX_SETUID
   434  
   435  #include <sys/resource.h>
   436  #include <sys/wait.h>
   437  #include <unistd.h>
   438  
   439  static void loop();
   440  
   441  static int wait_for_loop(int pid)
   442  {
   443  	if (pid < 0)
   444  		fail("sandbox fork failed");
   445  	debug("spawned loop pid %d\n", pid);
   446  	int status = 0;
   447  	while (waitpid(-1, &status, WUNTRACED) != pid) {
   448  	}
   449  	return WEXITSTATUS(status);
   450  }
   451  
   452  #define SYZ_HAVE_SANDBOX_SETUID 1
   453  static int do_sandbox_setuid(void)
   454  {
   455  	int pid = fork();
   456  	if (pid != 0)
   457  		return wait_for_loop(pid);
   458  
   459  	sandbox_common();
   460  #if SYZ_EXECUTOR || SYZ_NET_INJECTION
   461  	initialize_tun(procid);
   462  #endif
   463  
   464  	char pwbuf[1024];
   465  	struct passwd *pw, pwres;
   466  	if (getpwnam_r("nobody", &pwres, pwbuf, sizeof(pwbuf), &pw) != 0 || !pw)
   467  		fail("getpwnam_r(\"nobody\") failed");
   468  
   469  	if (setgroups(0, NULL))
   470  		fail("failed to setgroups");
   471  	if (setgid(pw->pw_gid))
   472  		fail("failed to setgid");
   473  	if (setuid(pw->pw_uid))
   474  		fail("failed to setuid");
   475  
   476  	loop();
   477  	doexit(1);
   478  }
   479  #endif // SYZ_EXECUTOR || SYZ_SANDBOX_SETUID