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