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