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