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