1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2025 Oxide Computer Company 14 */ 15 16 /* 17 * Verify the behavior of the various O_CLOFORK and O_CLOEXEC variants. In 18 * particular getting this via: 19 * 20 * - open(2): O_CLOFORK/O_CLOEXEC 21 * - fcntl(2): F_SETFD FD_CLOFORK/FD_CLOEXEC 22 * - fcntl(2): F_DUPFD_CLOFORK/F_DUPFD_CLOEXEC 23 * - fcntl(2): F_DUP2FD_CLOFORK/F_DUP2FD_CLOEXEC 24 * - dup2(3C) 25 * - dup3(3C): argument translation 26 * - pipe2(2) 27 * - socket(2): SOCK_CLOEXEC/SOCK_CLOFORK 28 * - accept(2): flags on the listen socket aren't inherited on accept 29 * - socketpair(3SOCKET) 30 * - accept4(2): SOCK_CLOEXEC/SOCK_CLOFORK 31 * - recvmsg(2): SCM_RIGHTS MSG_CMSG_CLOFORK/MSG_CMSG_CLOEXEC 32 * 33 * The test is designed such that we have an array of functions that are used to 34 * create file descriptors with different rules. This is found in the 35 * oclo_create array. Each file descriptor that is created is then registered 36 * with information about what is expected about it. A given creation function 37 * can create more than one file descriptor; however, our expectation is that 38 * every file descriptor is accounted for (ignoring stdin, stdout, and stderr). 39 * 40 * We pass a record of each file descriptor that was recorded to a verification 41 * program that will verify everything is correctly honored after an exec. Note 42 * that O_CLOFORK is cleared after exec. The original specification in POSIX has 43 * it being retained; however, this issue was raised after the spec was 44 * published as folks went to implement it and we have ended up following along 45 * with the divergence of other implementations. 46 */ 47 48 #include <stdlib.h> 49 #include <unistd.h> 50 #include <stdbool.h> 51 #include <err.h> 52 #include <sys/types.h> 53 #include <sys/stat.h> 54 #include <fcntl.h> 55 #include <sys/sysmacros.h> 56 #include <sys/fork.h> 57 #include <wait.h> 58 #include <errno.h> 59 #include <string.h> 60 #include <limits.h> 61 #include <libgen.h> 62 #include <sys/socket.h> 63 64 /* 65 * Verification program name. 66 */ 67 #define OCLO_VERIFY "ocloexec_verify" 68 69 /* 70 * This structure represents a table of ways we expect to create file 71 * descriptors that should have the resulting flags set when done. The table is 72 * ordered and subsequent iterations are allowed to assume that the ones that 73 * have gone ahead of them have run and are therefore allowed to access them. 74 * The create function is expected to return the created fd. 75 */ 76 typedef struct clo_create clo_create_t; 77 struct clo_create { 78 const char *clo_desc; 79 int clo_flags; 80 void (*clo_func)(const clo_create_t *); 81 }; 82 83 /* 84 * This is our run-time data. We expect all file descriptors to be registered by 85 * our calling functions through oclo_record(). 86 */ 87 typedef struct clo_rtdata { 88 const clo_create_t *crt_data; 89 size_t crt_idx; 90 int crt_fd; 91 int crt_flags; 92 const char *crt_desc; 93 } clo_rtdata_t; 94 95 static clo_rtdata_t *oclo_rtdata; 96 size_t oclo_rtdata_nents = 0; 97 size_t oclo_rtdata_next = 0; 98 static int oclo_nextfd = STDERR_FILENO + 1; 99 100 static bool 101 oclo_flags_match(const clo_rtdata_t *rt, bool child) 102 { 103 const char *pass = child ? "post-fork" : "pre-fork"; 104 bool fail = child && (rt->crt_flags & FD_CLOFORK) != 0; 105 int flags = fcntl(rt->crt_fd, F_GETFD, NULL); 106 107 if (flags < 0) { 108 int e = errno; 109 110 if (fail) { 111 if (e == EBADF) { 112 (void) printf("TEST PASSED: %s (%s): fd %d: " 113 "correctly closed\n", 114 rt->crt_data->clo_desc, pass, rt->crt_fd); 115 return (true); 116 } 117 118 warn("TEST FAILED: %s (%s): fd %d: expected fcntl to " 119 "fail with EBADF, but found %s", 120 rt->crt_data->clo_desc, pass, rt->crt_fd, 121 strerrorname_np(e)); 122 return (false); 123 } 124 125 warnx("TEST FAILED: %s (%s): fd %d: fcntl(F_GETFD) " 126 "unexpectedly failed", rt->crt_data->clo_desc, pass, 127 rt->crt_fd); 128 return (false); 129 } 130 131 if (fail) { 132 warnx("TEST FAILED: %s (%s): fd %d: received flags %d, but " 133 "expected to fail based on flags %d", 134 rt->crt_data->clo_desc, pass, rt->crt_fd, flags, 135 rt->crt_fd); 136 return (false); 137 } 138 139 if (flags != rt->crt_flags) { 140 warnx("TEST FAILED: %s (%s): fd %d: discovered flags 0x%x do " 141 "not match expected flags 0x%x", rt->crt_data->clo_desc, 142 pass, rt->crt_fd, flags, rt->crt_fd); 143 return (false); 144 } 145 146 (void) printf("TEST PASSED: %s (%s): fd %d discovered flags match " 147 "(0x%x)\n", rt->crt_data->clo_desc, pass, rt->crt_fd, flags); 148 return (true); 149 } 150 151 152 static void 153 oclo_record(const clo_create_t *c, int fd, int exp_flags, const char *desc) 154 { 155 if (oclo_rtdata_next == oclo_rtdata_nents) { 156 size_t newrt = oclo_rtdata_nents + 8; 157 clo_rtdata_t *rt; 158 rt = recallocarray(oclo_rtdata, oclo_rtdata_nents, newrt, 159 sizeof (clo_rtdata_t)); 160 if (rt == NULL) { 161 err(EXIT_FAILURE, "TEST_FAILED: internal error " 162 "expanding fd records to %zu entries", newrt); 163 } 164 165 oclo_rtdata_nents = newrt; 166 oclo_rtdata = rt; 167 } 168 169 if (fd != oclo_nextfd) { 170 errx(EXIT_FAILURE, "TEST FAILED: internal test error: expected " 171 "to record next fd %d, given %d", oclo_nextfd, fd); 172 } 173 174 oclo_rtdata[oclo_rtdata_next].crt_data = c; 175 oclo_rtdata[oclo_rtdata_next].crt_fd = fd; 176 oclo_rtdata[oclo_rtdata_next].crt_flags = exp_flags; 177 oclo_rtdata[oclo_rtdata_next].crt_desc = desc; 178 179 /* 180 * Matching errors at this phase are fatal as it means we screwed up the 181 * program pretty badly. 182 */ 183 if (!oclo_flags_match(&oclo_rtdata[oclo_rtdata_next], false)) { 184 exit(EXIT_FAILURE); 185 } 186 187 oclo_rtdata_next++; 188 oclo_nextfd++; 189 } 190 191 static int 192 oclo_file(const clo_create_t *c) 193 { 194 int flags = O_RDWR, fd; 195 196 if ((c->clo_flags & FD_CLOEXEC) != 0) 197 flags |= O_CLOEXEC; 198 if ((c->clo_flags & FD_CLOFORK) != 0) 199 flags |= O_CLOFORK; 200 fd = open("/dev/null", flags); 201 if (fd < 0) { 202 err(EXIT_FAILURE, "TEST FAILED: %s: failed to open /dev/null", 203 c->clo_desc); 204 } 205 206 return (fd); 207 } 208 209 static void 210 oclo_open(const clo_create_t *c) 211 { 212 oclo_record(c, oclo_file(c), c->clo_flags, NULL); 213 } 214 215 static void 216 oclo_setfd_common(const clo_create_t *c, int targ_flags) 217 { 218 int fd = oclo_file(c); 219 if (fcntl(fd, F_SETFD, targ_flags) < 0) { 220 err(EXIT_FAILURE, "TEST FAILED: %s: F_SETFD failed to set " 221 "flags to %d", c->clo_desc, targ_flags); 222 } 223 224 oclo_record(c, fd, targ_flags, NULL); 225 } 226 227 static void 228 oclo_setfd_none(const clo_create_t *c) 229 { 230 oclo_setfd_common(c, 0); 231 } 232 233 static void 234 oclo_setfd_exec(const clo_create_t *c) 235 { 236 oclo_setfd_common(c, FD_CLOEXEC); 237 } 238 239 static void 240 oclo_setfd_fork(const clo_create_t *c) 241 { 242 oclo_setfd_common(c, FD_CLOFORK); 243 } 244 245 static void 246 oclo_setfd_both(const clo_create_t *c) 247 { 248 oclo_setfd_common(c, FD_CLOFORK | FD_CLOEXEC); 249 } 250 251 /* 252 * Open an fd with flags in a certain form and then use one of the F_DUPFD or 253 * F_DUP2FD variants and ensure that flags are properly propagated as expected. 254 */ 255 static void 256 oclo_fdup_common(const clo_create_t *c, int targ_flags, int cmd) 257 { 258 int dup, fd; 259 260 fd = oclo_file(c); 261 oclo_record(c, fd, c->clo_flags, "base"); 262 switch (cmd) { 263 case F_DUPFD: 264 case F_DUPFD_CLOEXEC: 265 case F_DUPFD_CLOFORK: 266 dup = fcntl(fd, cmd, fd); 267 break; 268 case F_DUP2FD: 269 case F_DUP2FD_CLOEXEC: 270 case F_DUP2FD_CLOFORK: 271 dup = fcntl(fd, cmd, fd + 1); 272 break; 273 case F_DUP3FD: 274 dup = fcntl(fd, cmd, fd + 1, targ_flags); 275 break; 276 default: 277 errx(EXIT_FAILURE, "TEST FAILURE: %s: internal error: " 278 "unexpected fcntl cmd: 0x%x", c->clo_desc, cmd); 279 } 280 281 if (dup < 0) { 282 err(EXIT_FAILURE, "TEST FAILURE: %s: failed to dup fd with " 283 "fcntl command 0x%x", c->clo_desc, cmd); 284 } 285 286 oclo_record(c, dup, targ_flags, "dup"); 287 } 288 289 static void 290 oclo_fdupfd(const clo_create_t *c) 291 { 292 oclo_fdup_common(c, 0, F_DUPFD); 293 } 294 295 static void 296 oclo_fdupfd_fork(const clo_create_t *c) 297 { 298 oclo_fdup_common(c, FD_CLOFORK, F_DUPFD_CLOFORK); 299 } 300 301 static void 302 oclo_fdupfd_exec(const clo_create_t *c) 303 { 304 oclo_fdup_common(c, FD_CLOEXEC, F_DUPFD_CLOEXEC); 305 } 306 307 static void 308 oclo_fdup2fd(const clo_create_t *c) 309 { 310 oclo_fdup_common(c, 0, F_DUP2FD); 311 } 312 313 static void 314 oclo_fdup2fd_fork(const clo_create_t *c) 315 { 316 oclo_fdup_common(c, FD_CLOFORK, F_DUP2FD_CLOFORK); 317 } 318 319 static void 320 oclo_fdup2fd_exec(const clo_create_t *c) 321 { 322 oclo_fdup_common(c, FD_CLOEXEC, F_DUP2FD_CLOEXEC); 323 } 324 325 static void 326 oclo_fdup3fd_none(const clo_create_t *c) 327 { 328 oclo_fdup_common(c, 0, F_DUP3FD); 329 } 330 331 static void 332 oclo_fdup3fd_exec(const clo_create_t *c) 333 { 334 oclo_fdup_common(c, FD_CLOEXEC, F_DUP3FD); 335 } 336 337 static void 338 oclo_fdup3fd_fork(const clo_create_t *c) 339 { 340 oclo_fdup_common(c, FD_CLOFORK, F_DUP3FD); 341 } 342 343 static void 344 oclo_fdup3fd_both(const clo_create_t *c) 345 { 346 oclo_fdup_common(c, FD_CLOEXEC | FD_CLOFORK, F_DUP3FD); 347 } 348 349 static void 350 oclo_dup_common(const clo_create_t *c, int targ_flags, bool v3) 351 { 352 int dup, fd; 353 fd = oclo_file(c); 354 oclo_record(c, fd, c->clo_flags, "base"); 355 if (v3) { 356 int dflags = 0; 357 if ((targ_flags & FD_CLOEXEC) != 0) 358 dflags |= O_CLOEXEC; 359 if ((targ_flags & FD_CLOFORK) != 0) 360 dflags |= O_CLOFORK; 361 dup = dup3(fd, fd + 1, dflags); 362 } else { 363 dup = dup2(fd, fd + 1); 364 } 365 366 oclo_record(c, dup, targ_flags, "dup"); 367 } 368 369 static void 370 oclo_dup2(const clo_create_t *c) 371 { 372 oclo_dup_common(c, 0, false); 373 } 374 375 static void 376 oclo_dup3_none(const clo_create_t *c) 377 { 378 oclo_dup_common(c, 0, true); 379 } 380 381 static void 382 oclo_dup3_exec(const clo_create_t *c) 383 { 384 oclo_dup_common(c, FD_CLOEXEC, true); 385 } 386 387 static void 388 oclo_dup3_fork(const clo_create_t *c) 389 { 390 oclo_dup_common(c, FD_CLOFORK, true); 391 } 392 393 static void 394 oclo_dup3_both(const clo_create_t *c) 395 { 396 oclo_dup_common(c, FD_CLOEXEC | FD_CLOFORK, true); 397 } 398 399 static void 400 oclo_pipe(const clo_create_t *c) 401 { 402 int flags = 0, fds[2]; 403 404 if ((c->clo_flags & FD_CLOEXEC) != 0) 405 flags |= O_CLOEXEC; 406 if ((c->clo_flags & FD_CLOFORK) != 0) 407 flags |= O_CLOFORK; 408 409 if (pipe2(fds, flags) < 0) { 410 err(EXIT_FAILURE, "TEST FAILED: %s: pipe2() with flags %d " 411 "failed", c->clo_desc, flags); 412 } 413 414 oclo_record(c, fds[0], c->clo_flags, "pipe[0]"); 415 oclo_record(c, fds[1], c->clo_flags, "pipe[1]"); 416 } 417 418 static void 419 oclo_socket(const clo_create_t *c) 420 { 421 int type = SOCK_DGRAM, fd; 422 423 if ((c->clo_flags & FD_CLOEXEC) != 0) 424 type |= SOCK_CLOEXEC; 425 if ((c->clo_flags & FD_CLOFORK) != 0) 426 type |= SOCK_CLOFORK; 427 fd = socket(PF_INET, type, 0); 428 if (fd < 0) { 429 err(EXIT_FAILURE, "TEST FAILED: %s: failed to create socket " 430 "with flags: 0x%x\n", c->clo_desc, c->clo_flags); 431 } 432 433 oclo_record(c, fd, c->clo_flags, NULL); 434 } 435 436 static void 437 oclo_accept_common(const clo_create_t *c, int targ_flags, bool a4) 438 { 439 int lsock, csock, asock; 440 int ltype = SOCK_STREAM, atype = 0; 441 struct sockaddr_in in; 442 socklen_t slen; 443 444 if ((c->clo_flags & FD_CLOEXEC) != 0) 445 ltype |= SOCK_CLOEXEC; 446 if ((c->clo_flags & FD_CLOFORK) != 0) 447 ltype |= SOCK_CLOFORK; 448 449 if ((targ_flags & FD_CLOEXEC) != 0) 450 atype |= SOCK_CLOEXEC; 451 if ((targ_flags & FD_CLOFORK) != 0) 452 atype |= SOCK_CLOFORK; 453 454 lsock = socket(PF_INET, ltype, 0); 455 if (lsock < 0) { 456 err(EXIT_FAILURE, "TEST FAILED: %s: failed to create listen " 457 "socket with flags: 0x%x\n", c->clo_desc, c->clo_flags); 458 } 459 460 oclo_record(c, lsock, c->clo_flags, "listen"); 461 (void) memset(&in, 0, sizeof (in)); 462 in.sin_family = AF_INET; 463 in.sin_port = 0; 464 in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 465 466 if (bind(lsock, (struct sockaddr *)&in, sizeof (in)) != 0) { 467 err(EXIT_FAILURE, "TEST FAILED: %s: failed to bind socket", 468 c->clo_desc); 469 } 470 471 slen = sizeof (struct sockaddr_in); 472 if (getsockname(lsock, (struct sockaddr *)&in, &slen) != 0) { 473 err(EXIT_FAILURE, "TEST FAILED: %s: failed to discover bound " 474 "socket address", c->clo_desc); 475 } 476 477 if (listen(lsock, 5) < 0) { 478 err(EXIT_FAILURE, "TEST FAILED: %s: failed to listen on socket", 479 c->clo_desc); 480 } 481 482 csock = socket(PF_INET, SOCK_STREAM, 0); 483 if (csock < 0) { 484 err(EXIT_FAILURE, "TEST FAILED: %s: failed to create client " 485 "socket", c->clo_desc); 486 } 487 oclo_record(c, csock, 0, "connect"); 488 489 if (connect(csock, (struct sockaddr *)&in, sizeof (in)) != 0) { 490 err(EXIT_FAILURE, "TEST FAILED: %s: failed to connect to " 491 "server socket", c->clo_desc); 492 } 493 494 if (a4) { 495 asock = accept4(lsock, NULL, NULL, atype); 496 } else { 497 asock = accept(lsock, NULL, NULL); 498 } 499 if (asock < 0) { 500 err(EXIT_FAILURE, "TEST FAILED: %s: failed to accept client " 501 "connection", c->clo_desc); 502 } 503 oclo_record(c, asock, targ_flags, "accept"); 504 } 505 506 static void 507 oclo_accept(const clo_create_t *c) 508 { 509 oclo_accept_common(c, 0, false); 510 } 511 512 static void 513 oclo_accept4_none(const clo_create_t *c) 514 { 515 oclo_accept_common(c, 0, true); 516 } 517 518 static void 519 oclo_accept4_fork(const clo_create_t *c) 520 { 521 oclo_accept_common(c, FD_CLOFORK, true); 522 } 523 524 static void 525 oclo_accept4_exec(const clo_create_t *c) 526 { 527 oclo_accept_common(c, FD_CLOEXEC, true); 528 } 529 530 static void 531 oclo_accept4_both(const clo_create_t *c) 532 { 533 oclo_accept_common(c, FD_CLOEXEC | FD_CLOFORK, true); 534 } 535 536 /* 537 * Go through the process of sending ourselves a file descriptor. 538 */ 539 static void 540 oclo_rights_common(const clo_create_t *c, int targ_flags) 541 { 542 int pair[2], type = SOCK_DGRAM, sflags = 0; 543 int tosend = oclo_file(c), recvfd; 544 uint32_t data = 0x7777; 545 struct iovec iov; 546 struct msghdr msg; 547 struct cmsghdr *cm; 548 549 if ((c->clo_flags & FD_CLOEXEC) != 0) 550 type |= SOCK_CLOEXEC; 551 if ((c->clo_flags & FD_CLOFORK) != 0) 552 type |= SOCK_CLOFORK; 553 554 if (socketpair(PF_UNIX, type, 0, pair) < 0) { 555 err(EXIT_FAILURE, "TEST FAILED: %s: failed to create socket " 556 "pair", c->clo_desc); 557 } 558 559 oclo_record(c, tosend, c->clo_flags, "send fd"); 560 oclo_record(c, pair[0], c->clo_flags, "pair[0]"); 561 oclo_record(c, pair[1], c->clo_flags, "pair[1]"); 562 563 iov.iov_base = (void *)&data; 564 iov.iov_len = sizeof (data); 565 566 (void) memset(&msg, 0, sizeof (msg)); 567 msg.msg_iov = &iov; 568 msg.msg_iovlen = 1; 569 msg.msg_controllen = CMSG_SPACE(sizeof (int)); 570 571 msg.msg_control = calloc(1, msg.msg_controllen); 572 if (msg.msg_control == NULL) { 573 err(EXIT_FAILURE, "TEST FAILED: %s: failed to allocate %u " 574 "bytes for SCM_RIGHTS control message", c->clo_desc, 575 msg.msg_controllen); 576 } 577 578 cm = CMSG_FIRSTHDR(&msg); 579 cm->cmsg_len = CMSG_LEN(sizeof (int)); 580 cm->cmsg_level = SOL_SOCKET; 581 cm->cmsg_type = SCM_RIGHTS; 582 (void) memcpy(CMSG_DATA(cm), &tosend, sizeof (tosend)); 583 584 if ((targ_flags & FD_CLOEXEC) != 0) 585 sflags |= MSG_CMSG_CLOEXEC; 586 if ((targ_flags & FD_CLOFORK) != 0) 587 sflags |= MSG_CMSG_CLOFORK; 588 589 if (sendmsg(pair[0], &msg, 0) < 0) { 590 err(EXIT_FAILURE, "TEST FAILED: %s: failed to send fd", 591 c->clo_desc); 592 } 593 594 data = 0; 595 if (recvmsg(pair[1], &msg, sflags) < 0) { 596 err(EXIT_FAILURE, "TEST FAILED: %s: failed to get fd", 597 c->clo_desc); 598 } 599 600 if (data != 0x7777) { 601 errx(EXIT_FAILURE, "TEST FAILED: %s: did not receive correct " 602 "data: expected 0x7777, found 0x%x", c->clo_desc, data); 603 } 604 605 if (msg.msg_controllen < CMSG_SPACE(sizeof (int))) { 606 errx(EXIT_FAILURE, "TEST FAILED: %s: found insufficient " 607 "message control length: expected at least 0x%x, found " 608 "0x%x", c->clo_desc, CMSG_SPACE(sizeof (int)), 609 msg.msg_controllen); 610 } 611 612 cm = CMSG_FIRSTHDR(&msg); 613 if (cm->cmsg_level != SOL_SOCKET || cm->cmsg_type != SCM_RIGHTS) { 614 errx(EXIT_FAILURE, "TEST FAILED: %s: found surprising cmsg " 615 "0x%x/0x%x, expected 0x%x/0x%x", c->clo_desc, 616 cm->cmsg_level, cm->cmsg_type, SOL_SOCKET, SCM_RIGHTS); 617 } 618 619 if (cm->cmsg_len != CMSG_LEN(sizeof (int))) { 620 errx(EXIT_FAILURE, "TEST FAILED: %s: found unexpected " 621 "SCM_RIGHTS length 0x%x: expected 0x%zx", c->clo_desc, 622 cm->cmsg_len, CMSG_LEN(sizeof (int))); 623 } 624 625 (void) memcpy(&recvfd, CMSG_DATA(cm), sizeof (recvfd)); 626 oclo_record(c, recvfd, targ_flags, "SCM_RIGHTS"); 627 } 628 629 static void 630 oclo_rights_none(const clo_create_t *c) 631 { 632 oclo_rights_common(c, 0); 633 } 634 635 static void 636 oclo_rights_exec(const clo_create_t *c) 637 { 638 oclo_rights_common(c, FD_CLOEXEC); 639 } 640 641 static void 642 oclo_rights_fork(const clo_create_t *c) 643 { 644 oclo_rights_common(c, FD_CLOFORK); 645 } 646 647 static void 648 oclo_rights_both(const clo_create_t *c) 649 { 650 oclo_rights_common(c, FD_CLOEXEC | FD_CLOFORK); 651 } 652 653 static const clo_create_t oclo_create[] = { { 654 .clo_desc = "open(2), no flags", 655 .clo_flags = 0, 656 .clo_func = oclo_open 657 }, { 658 .clo_desc = "open(2), O_CLOEXEC", 659 .clo_flags = FD_CLOEXEC, 660 .clo_func = oclo_open 661 }, { 662 .clo_desc = "open(2), O_CLOFORK", 663 .clo_flags = FD_CLOFORK, 664 .clo_func = oclo_open 665 }, { 666 .clo_desc = "open(2), O_CLOEXEC|O_CLOFORK", 667 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 668 .clo_func = oclo_open 669 }, { 670 .clo_desc = "fcntl(F_SETFD) no flags->no flags", 671 .clo_flags = 0, 672 .clo_func = oclo_setfd_none 673 }, { 674 .clo_desc = "fcntl(F_SETFD) O_CLOFORK|O_CLOEXEC->no flags", 675 .clo_flags = O_CLOFORK | O_CLOEXEC, 676 .clo_func = oclo_setfd_none 677 }, { 678 .clo_desc = "fcntl(F_SETFD) O_CLOEXEC->no flags", 679 .clo_flags = O_CLOEXEC, 680 .clo_func = oclo_setfd_none 681 }, { 682 .clo_desc = "fcntl(F_SETFD) O_CLOFORK->no flags", 683 .clo_flags = O_CLOFORK, 684 .clo_func = oclo_setfd_none 685 }, { 686 .clo_desc = "fcntl(F_SETFD) no flags->O_CLOEXEC", 687 .clo_flags = 0, 688 .clo_func = oclo_setfd_exec 689 }, { 690 .clo_desc = "fcntl(F_SETFD) O_CLOFORK|O_CLOEXEC->O_CLOEXEC", 691 .clo_flags = O_CLOFORK | O_CLOEXEC, 692 .clo_func = oclo_setfd_exec 693 }, { 694 .clo_desc = "fcntl(F_SETFD) O_CLOEXEC->O_CLOEXEC", 695 .clo_flags = O_CLOEXEC, 696 .clo_func = oclo_setfd_exec 697 }, { 698 .clo_desc = "fcntl(F_SETFD) O_CLOFORK->O_CLOEXEC", 699 .clo_flags = O_CLOFORK, 700 .clo_func = oclo_setfd_exec 701 }, { 702 .clo_desc = "fcntl(F_SETFD) no flags->O_CLOFORK", 703 .clo_flags = 0, 704 .clo_func = oclo_setfd_fork 705 }, { 706 .clo_desc = "fcntl(F_SETFD) O_CLOFORK|O_CLOEXEC->O_CLOFORK", 707 .clo_flags = O_CLOFORK | O_CLOEXEC, 708 .clo_func = oclo_setfd_fork 709 }, { 710 .clo_desc = "fcntl(F_SETFD) O_CLOEXEC->O_CLOFORK", 711 .clo_flags = O_CLOEXEC, 712 .clo_func = oclo_setfd_fork 713 }, { 714 .clo_desc = "fcntl(F_SETFD) O_CLOFORK->O_CLOFORK", 715 .clo_flags = O_CLOFORK, 716 .clo_func = oclo_setfd_fork 717 }, { 718 .clo_desc = "fcntl(F_SETFD) no flags->O_CLOFORK|O_CLOEXEC", 719 .clo_flags = 0, 720 .clo_func = oclo_setfd_both 721 }, { 722 .clo_desc = "fcntl(F_SETFD) O_CLOFORK|O_CLOEXEC->O_CLOFORK|O_CLOEXEC", 723 .clo_flags = O_CLOFORK | O_CLOEXEC, 724 .clo_func = oclo_setfd_both 725 }, { 726 .clo_desc = "fcntl(F_SETFD) O_CLOEXEC->O_CLOFORK|O_CLOEXEC", 727 .clo_flags = O_CLOEXEC, 728 .clo_func = oclo_setfd_both 729 }, { 730 .clo_desc = "fcntl(F_SETFD) O_CLOFORK->O_CLOFORK|O_CLOEXEC", 731 .clo_flags = O_CLOFORK, 732 .clo_func = oclo_setfd_both 733 }, { 734 .clo_desc = "fcntl(F_DUPFD) none->none", 735 .clo_flags = 0, 736 .clo_func = oclo_fdupfd 737 }, { 738 .clo_desc = "fcntl(F_DUPFD) FD_CLOEXEC->none", 739 .clo_flags = FD_CLOEXEC, 740 .clo_func = oclo_fdupfd 741 }, { 742 .clo_desc = "fcntl(F_DUPFD) FD_CLOFORK->none", 743 .clo_flags = FD_CLOFORK, 744 .clo_func = oclo_fdupfd 745 }, { 746 .clo_desc = "fcntl(F_DUPFD) FD_CLOEXEC|FD_CLOFORK->none", 747 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 748 .clo_func = oclo_fdupfd 749 }, { 750 .clo_desc = "fcntl(F_DUPFD_CLOFORK) none", 751 .clo_flags = 0, 752 .clo_func = oclo_fdupfd_fork 753 }, { 754 .clo_desc = "fcntl(F_DUPFD_CLOFORK) FD_CLOEXEC", 755 .clo_flags = FD_CLOEXEC, 756 .clo_func = oclo_fdupfd_fork 757 }, { 758 .clo_desc = "fcntl(F_DUPFD_CLOFORK) FD_CLOFORK", 759 .clo_flags = FD_CLOFORK, 760 .clo_func = oclo_fdupfd_fork 761 }, { 762 .clo_desc = "fcntl(F_DUPFD_CLOFORK) FD_CLOEXEC|FD_CLOFORK", 763 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 764 .clo_func = oclo_fdupfd_fork 765 }, { 766 .clo_desc = "fcntl(F_DUPFD_CLOEXEC) none", 767 .clo_flags = 0, 768 .clo_func = oclo_fdupfd_exec 769 }, { 770 .clo_desc = "fcntl(F_DUPFD_CLOEXEC) FD_CLOEXEC", 771 .clo_flags = FD_CLOEXEC, 772 .clo_func = oclo_fdupfd_exec 773 }, { 774 .clo_desc = "fcntl(F_DUPFD_CLOEXEC) FD_CLOFORK", 775 .clo_flags = FD_CLOFORK, 776 .clo_func = oclo_fdupfd_exec 777 }, { 778 .clo_desc = "fcntl(F_DUPFD_CLOEXEC) FD_CLOEXEC|FD_CLOFORK", 779 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 780 .clo_func = oclo_fdupfd_exec 781 }, { 782 .clo_desc = "fcntl(F_DUP2FD) none->none", 783 .clo_flags = 0, 784 .clo_func = oclo_fdup2fd 785 }, { 786 .clo_desc = "fcntl(F_DUP2FD) FD_CLOEXEC->none", 787 .clo_flags = FD_CLOEXEC, 788 .clo_func = oclo_fdup2fd 789 }, { 790 .clo_desc = "fcntl(F_DUP2FD) FD_CLOFORK->none", 791 .clo_flags = FD_CLOFORK, 792 .clo_func = oclo_fdup2fd 793 }, { 794 .clo_desc = "fcntl(F_DUP2FD) FD_CLOEXEC|FD_CLOFORK->none", 795 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 796 .clo_func = oclo_fdup2fd 797 }, { 798 .clo_desc = "fcntl(F_DUP2FD_CLOFORK) none", 799 .clo_flags = 0, 800 .clo_func = oclo_fdup2fd_fork 801 }, { 802 .clo_desc = "fcntl(F_DUP2FD_CLOFORK) FD_CLOEXEC", 803 .clo_flags = FD_CLOEXEC, 804 .clo_func = oclo_fdup2fd_fork 805 }, { 806 .clo_desc = "fcntl(F_DUP2FD_CLOFORK) FD_CLOFORK", 807 .clo_flags = FD_CLOFORK, 808 .clo_func = oclo_fdup2fd_fork 809 }, { 810 .clo_desc = "fcntl(F_DUP2FD_CLOFORK) FD_CLOEXEC|FD_CLOFORK", 811 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 812 .clo_func = oclo_fdup2fd_fork 813 }, { 814 .clo_desc = "fcntl(F_DUP2FD_CLOEXEC) none", 815 .clo_flags = 0, 816 .clo_func = oclo_fdup2fd_exec 817 }, { 818 .clo_desc = "fcntl(F_DUP2FD_CLOEXEC) FD_CLOEXEC", 819 .clo_flags = FD_CLOEXEC, 820 .clo_func = oclo_fdup2fd_exec 821 }, { 822 .clo_desc = "fcntl(F_DUP2FD_CLOEXEC) FD_CLOFORK", 823 .clo_flags = FD_CLOFORK, 824 .clo_func = oclo_fdup2fd_exec 825 }, { 826 .clo_desc = "fcntl(F_DUP2FD_CLOEXEC) FD_CLOEXEC|FD_CLOFORK", 827 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 828 .clo_func = oclo_fdup2fd_exec 829 }, { 830 .clo_desc = "fcntl(F_DUP3FD) none->none", 831 .clo_flags = 0, 832 .clo_func = oclo_fdup3fd_none 833 }, { 834 .clo_desc = "fcntl(F_DUP3FD) FD_CLOEXEC->none", 835 .clo_flags = FD_CLOEXEC, 836 .clo_func = oclo_fdup3fd_none 837 }, { 838 .clo_desc = "fcntl(F_DUP3FD) FD_CLOFORK->none", 839 .clo_flags = FD_CLOFORK, 840 .clo_func = oclo_fdup3fd_none 841 }, { 842 .clo_desc = "fcntl(F_DUP3FD) FD_CLOEXEC|FD_CLOFORK->none", 843 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 844 .clo_func = oclo_fdup3fd_none 845 }, { 846 .clo_desc = "fcntl(F_DUP3FD) none->FD_CLOEXEC", 847 .clo_flags = 0, 848 .clo_func = oclo_fdup3fd_exec 849 }, { 850 .clo_desc = "fcntl(F_DUP3FD) FD_CLOEXEC->FD_CLOEXEC", 851 .clo_flags = FD_CLOEXEC, 852 .clo_func = oclo_fdup3fd_exec 853 }, { 854 .clo_desc = "fcntl(F_DUP3FD) FD_CLOFORK->FD_CLOEXEC", 855 .clo_flags = FD_CLOFORK, 856 .clo_func = oclo_fdup3fd_exec 857 }, { 858 .clo_desc = "fcntl(F_DUP3FD) FD_CLOEXEC|FD_CLOFORK->FD_CLOEXEC", 859 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 860 .clo_func = oclo_fdup3fd_exec 861 }, { 862 .clo_desc = "fcntl(F_DUP3FD) none->FD_CLOFORK|FD_CLOEXEC", 863 .clo_flags = 0, 864 .clo_func = oclo_fdup3fd_both 865 }, { 866 .clo_desc = "fcntl(F_DUP3FD) FD_CLOEXEC->FD_CLOFORK|FD_CLOEXEC", 867 .clo_flags = FD_CLOEXEC, 868 .clo_func = oclo_fdup3fd_both 869 }, { 870 .clo_desc = "fcntl(F_DUP3FD) FD_CLOFORK->FD_CLOFORK|FD_CLOEXEC", 871 .clo_flags = FD_CLOFORK, 872 .clo_func = oclo_fdup3fd_both 873 }, { 874 .clo_desc = "fcntl(F_DUP3FD) FD_CLOEXEC|FD_CLOFORK->" 875 "FD_CLOFORK|FD_CLOEXEC", 876 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 877 .clo_func = oclo_fdup3fd_both 878 }, { 879 .clo_desc = "fcntl(F_DUP3FD) none->FD_CLOFORK", 880 .clo_flags = 0, 881 .clo_func = oclo_fdup3fd_fork 882 }, { 883 .clo_desc = "fcntl(F_DUP3FD) FD_CLOEXEC->FD_CLOFORK", 884 .clo_flags = FD_CLOEXEC, 885 .clo_func = oclo_fdup3fd_fork 886 }, { 887 .clo_desc = "fcntl(F_DUP3FD) FD_CLOFORK->FD_CLOFORK", 888 .clo_flags = FD_CLOFORK, 889 .clo_func = oclo_fdup3fd_fork 890 }, { 891 .clo_desc = "fcntl(F_DUP3FD) FD_CLOEXEC|FD_CLOFORK->FD_CLOFORK", 892 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 893 .clo_func = oclo_fdup3fd_fork 894 }, { 895 .clo_desc = "dup2() none->none", 896 .clo_flags = 0, 897 .clo_func = oclo_dup2 898 }, { 899 .clo_desc = "dup2() FD_CLOEXEC->none", 900 .clo_flags = FD_CLOEXEC, 901 .clo_func = oclo_dup2 902 }, { 903 .clo_desc = "dup2() FD_CLOFORK->none", 904 .clo_flags = FD_CLOFORK, 905 .clo_func = oclo_dup2 906 }, { 907 .clo_desc = "dup2() FD_CLOEXEC|FD_CLOFORK->none", 908 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 909 .clo_func = oclo_dup2 910 }, { 911 .clo_desc = "dup3() none->none", 912 .clo_flags = 0, 913 .clo_func = oclo_dup3_none 914 }, { 915 .clo_desc = "dup3() FD_CLOEXEC->none", 916 .clo_flags = FD_CLOEXEC, 917 .clo_func = oclo_dup3_none 918 }, { 919 .clo_desc = "dup3() FD_CLOFORK->none", 920 .clo_flags = FD_CLOFORK, 921 .clo_func = oclo_dup3_none 922 }, { 923 .clo_desc = "dup3() FD_CLOEXEC|FD_CLOFORK->none", 924 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 925 .clo_func = oclo_dup3_none 926 }, { 927 .clo_desc = "dup3() none->FD_CLOEXEC", 928 .clo_flags = 0, 929 .clo_func = oclo_dup3_exec 930 }, { 931 .clo_desc = "dup3() FD_CLOEXEC->FD_CLOEXEC", 932 .clo_flags = FD_CLOEXEC, 933 .clo_func = oclo_dup3_exec 934 }, { 935 .clo_desc = "dup3() FD_CLOFORK->FD_CLOEXEC", 936 .clo_flags = FD_CLOFORK, 937 .clo_func = oclo_dup3_exec 938 }, { 939 .clo_desc = "dup3() FD_CLOEXEC|FD_CLOFORK->FD_CLOEXEC", 940 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 941 .clo_func = oclo_dup3_exec 942 }, { 943 .clo_desc = "dup3() none->FD_CLOFORK|FD_CLOEXEC", 944 .clo_flags = 0, 945 .clo_func = oclo_dup3_both 946 }, { 947 .clo_desc = "dup3() FD_CLOEXEC->FD_CLOFORK|FD_CLOEXEC", 948 .clo_flags = FD_CLOEXEC, 949 .clo_func = oclo_dup3_both 950 }, { 951 .clo_desc = "dup3() FD_CLOFORK->FD_CLOFORK|FD_CLOEXEC", 952 .clo_flags = FD_CLOFORK, 953 .clo_func = oclo_dup3_both 954 }, { 955 .clo_desc = "dup3() FD_CLOEXEC|FD_CLOFORK->FD_CLOFORK|FD_CLOEXEC", 956 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 957 .clo_func = oclo_dup3_both 958 }, { 959 .clo_desc = "dup3() none->FD_CLOFORK", 960 .clo_flags = 0, 961 .clo_func = oclo_dup3_fork 962 }, { 963 .clo_desc = "dup3() FD_CLOEXEC->FD_CLOFORK", 964 .clo_flags = FD_CLOEXEC, 965 .clo_func = oclo_dup3_fork 966 }, { 967 .clo_desc = "dup3() FD_CLOFORK->FD_CLOFORK", 968 .clo_flags = FD_CLOFORK, 969 .clo_func = oclo_dup3_fork 970 }, { 971 .clo_desc = "dup3() FD_CLOEXEC|FD_CLOFORK->FD_CLOFORK", 972 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 973 .clo_func = oclo_dup3_fork 974 }, { 975 .clo_desc = "pipe(2), no flags", 976 .clo_flags = 0, 977 .clo_func = oclo_pipe 978 }, { 979 .clo_desc = "pipe(2), O_CLOEXEC", 980 .clo_flags = FD_CLOEXEC, 981 .clo_func = oclo_pipe 982 }, { 983 .clo_desc = "pipe(2), O_CLOFORK", 984 .clo_flags = FD_CLOFORK, 985 .clo_func = oclo_pipe 986 }, { 987 .clo_desc = "pipe(2), O_CLOEXEC|O_CLOFORK", 988 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 989 .clo_func = oclo_pipe 990 }, { 991 .clo_desc = "socket(2), no flags", 992 .clo_flags = 0, 993 .clo_func = oclo_socket 994 }, { 995 .clo_desc = "socket(2), O_CLOEXEC", 996 .clo_flags = FD_CLOEXEC, 997 .clo_func = oclo_socket 998 }, { 999 .clo_desc = "socket(2), O_CLOFORK", 1000 .clo_flags = FD_CLOFORK, 1001 .clo_func = oclo_socket 1002 }, { 1003 .clo_desc = "socket(2), O_CLOEXEC|O_CLOFORK", 1004 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 1005 .clo_func = oclo_socket 1006 }, { 1007 .clo_desc = "socket(2), no flags->accept() none", 1008 .clo_flags = 0, 1009 .clo_func = oclo_accept 1010 }, { 1011 .clo_desc = "socket(2), O_CLOEXEC->accept() none", 1012 .clo_flags = FD_CLOEXEC, 1013 .clo_func = oclo_accept 1014 }, { 1015 .clo_desc = "socket(2), O_CLOFORK->accept() none", 1016 .clo_flags = FD_CLOFORK, 1017 .clo_func = oclo_accept 1018 }, { 1019 .clo_desc = "socket(2), O_CLOEXEC|O_CLOFORK->accept() none", 1020 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 1021 .clo_func = oclo_accept 1022 }, { 1023 .clo_desc = "socket(2), no flags->accept4() none", 1024 .clo_flags = 0, 1025 .clo_func = oclo_accept4_none 1026 }, { 1027 .clo_desc = "socket(2), O_CLOEXEC->accept4() none", 1028 .clo_flags = FD_CLOEXEC, 1029 .clo_func = oclo_accept4_none 1030 }, { 1031 .clo_desc = "socket(2), O_CLOFORK->accept4() none", 1032 .clo_flags = FD_CLOFORK, 1033 .clo_func = oclo_accept4_none 1034 }, { 1035 .clo_desc = "socket(2), O_CLOEXEC|O_CLOFORK->accept4() none", 1036 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 1037 .clo_func = oclo_accept4_none 1038 }, { 1039 .clo_desc = "socket(2), no flags->accept4() SOCK_CLOFORK|SOCK_CLOEXEC", 1040 .clo_flags = 0, 1041 .clo_func = oclo_accept4_both 1042 }, { 1043 .clo_desc = "socket(2), O_CLOEXEC->accept4() SOCK_CLOFORK|SOCK_CLOEXEC", 1044 .clo_flags = FD_CLOEXEC, 1045 .clo_func = oclo_accept4_both 1046 }, { 1047 .clo_desc = "socket(2), O_CLOFORK->accept4() SOCK_CLOFORK|SOCK_CLOEXEC", 1048 .clo_flags = FD_CLOFORK, 1049 .clo_func = oclo_accept4_both 1050 }, { 1051 .clo_desc = "socket(2), O_CLOEXEC|O_CLOFORK->accept4() " 1052 "SOCK_CLOFORK|SOCK_CLOEXEC", 1053 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 1054 .clo_func = oclo_accept4_both 1055 }, { 1056 .clo_desc = "socket(2), no flags->accept4() SOCK_CLOFORK", 1057 .clo_flags = 0, 1058 .clo_func = oclo_accept4_fork 1059 }, { 1060 .clo_desc = "socket(2), O_CLOEXEC->accept4() SOCK_CLOFORK", 1061 .clo_flags = FD_CLOEXEC, 1062 .clo_func = oclo_accept4_fork 1063 }, { 1064 .clo_desc = "socket(2), O_CLOFORK->accept4() SOCK_CLOFORK", 1065 .clo_flags = FD_CLOFORK, 1066 .clo_func = oclo_accept4_fork 1067 }, { 1068 .clo_desc = "socket(2), O_CLOEXEC|O_CLOFORK->accept4() SOCK_CLOFORK", 1069 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 1070 .clo_func = oclo_accept4_fork 1071 }, { 1072 .clo_desc = "socket(2), no flags->accept4() SOCK_CLOEXEC", 1073 .clo_flags = 0, 1074 .clo_func = oclo_accept4_exec 1075 }, { 1076 .clo_desc = "socket(2), O_CLOEXEC->accept4() SOCK_CLOEXEC", 1077 .clo_flags = FD_CLOEXEC, 1078 .clo_func = oclo_accept4_exec 1079 }, { 1080 .clo_desc = "socket(2), O_CLOFORK->accept4() SOCK_CLOEXEC", 1081 .clo_flags = FD_CLOFORK, 1082 .clo_func = oclo_accept4_exec 1083 }, { 1084 .clo_desc = "socket(2), O_CLOEXEC|O_CLOFORK->accept4() SOCK_CLOEXEC", 1085 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 1086 .clo_func = oclo_accept4_exec 1087 }, { 1088 .clo_desc = "SCM_RIGHTS none->none", 1089 .clo_flags = 0, 1090 .clo_func = oclo_rights_none 1091 }, { 1092 .clo_desc = "SCM_RIGHTS FD_CLOFORK->none", 1093 .clo_flags = FD_CLOFORK, 1094 .clo_func = oclo_rights_none 1095 }, { 1096 .clo_desc = "SCM_RIGHTS FD_CLOEXEC->none", 1097 .clo_flags = FD_CLOEXEC, 1098 .clo_func = oclo_rights_none 1099 }, { 1100 .clo_desc = "SCM_RIGHTS FD_CLOEXEC|FD_CLOFORK->none", 1101 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 1102 .clo_func = oclo_rights_none 1103 }, { 1104 .clo_desc = "SCM_RIGHTS none->MSG_CMSG_CLOEXEC", 1105 .clo_flags = 0, 1106 .clo_func = oclo_rights_exec 1107 }, { 1108 .clo_desc = "SCM_RIGHTS FD_CLOFORK->MSG_CMSG_CLOEXEC", 1109 .clo_flags = FD_CLOFORK, 1110 .clo_func = oclo_rights_exec 1111 }, { 1112 .clo_desc = "SCM_RIGHTS FD_CLOEXEC->MSG_CMSG_CLOEXEC", 1113 .clo_flags = FD_CLOEXEC, 1114 .clo_func = oclo_rights_exec 1115 }, { 1116 .clo_desc = "SCM_RIGHTS FD_CLOEXEC|FD_CLOFORK->MSG_CMSG_CLOEXEC", 1117 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 1118 .clo_func = oclo_rights_exec 1119 }, { 1120 .clo_desc = "SCM_RIGHTS MSG_CMSG_CLOFORK->nMSG_CMSG_CLOFORK", 1121 .clo_flags = 0, 1122 .clo_func = oclo_rights_fork 1123 }, { 1124 .clo_desc = "SCM_RIGHTS FD_CLOFORK->MSG_CMSG_CLOFORK", 1125 .clo_flags = FD_CLOFORK, 1126 .clo_func = oclo_rights_fork 1127 }, { 1128 .clo_desc = "SCM_RIGHTS FD_CLOEXEC->MSG_CMSG_CLOFORK", 1129 .clo_flags = FD_CLOEXEC, 1130 .clo_func = oclo_rights_fork 1131 }, { 1132 .clo_desc = "SCM_RIGHTS FD_CLOEXEC|FD_CLOFORK->MSG_CMSG_CLOFORK", 1133 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 1134 .clo_func = oclo_rights_fork 1135 }, { 1136 .clo_desc = "SCM_RIGHTS none->MSG_CMSG_CLOEXEC|MSG_CMSG_CLOFORK", 1137 .clo_flags = 0, 1138 .clo_func = oclo_rights_both 1139 }, { 1140 .clo_desc = "SCM_RIGHTS FD_CLOFORK->MSG_CMSG_CLOEXEC|MSG_CMSG_CLOFORK", 1141 .clo_flags = FD_CLOFORK, 1142 .clo_func = oclo_rights_both 1143 }, { 1144 .clo_desc = "SCM_RIGHTS FD_CLOEXEC->MSG_CMSG_CLOEXEC|MSG_CMSG_CLOFORK", 1145 .clo_flags = FD_CLOEXEC, 1146 .clo_func = oclo_rights_both 1147 }, { 1148 .clo_desc = "SCM_RIGHTS FD_CLOEXEC|FD_CLOFORK->" 1149 "MSG_CMSG_CLOEXEC|MSG_CMSG_CLOFORK", 1150 .clo_flags = FD_CLOEXEC | FD_CLOFORK, 1151 .clo_func = oclo_rights_both 1152 } }; 1153 1154 static bool 1155 oclo_verify_fork(void) 1156 { 1157 bool ret = true; 1158 1159 for (size_t i = 0; i < oclo_rtdata_next; i++) { 1160 if (!oclo_flags_match(&oclo_rtdata[i], true)) { 1161 ret = false; 1162 } 1163 } 1164 1165 return (ret); 1166 } 1167 1168 /* 1169 * Here we proceed to re-open any fd that was closed due to O_CLOFORK again to 1170 * make sure that the file descriptor makes it to our child verifier. 1171 * Importantly, with the changes that cause O_CLOFORK to be cleared on exec, we 1172 * should not see any file descriptors with the flag in the child. This allows 1173 * us to confirm that this is what we expect. 1174 * 1175 * In addition, this serves as a test to make sure that our opening of the 1176 * lowest fd is correct. While this doesn't actually use the same method as was 1177 * done previously, this should get us most of the way there. 1178 */ 1179 static void 1180 oclo_child_reopen(void) 1181 { 1182 for (size_t i = 0; i < oclo_rtdata_next; i++) { 1183 int fd; 1184 int flags = O_RDWR | O_CLOFORK; 1185 1186 if ((oclo_rtdata[i].crt_flags & FD_CLOFORK) == 0) 1187 continue; 1188 1189 if ((oclo_rtdata[i].crt_flags & FD_CLOEXEC) != 0) 1190 flags |= O_CLOEXEC; 1191 1192 fd = open("/dev/zero", flags); 1193 if (fd < 0) { 1194 err(EXIT_FAILURE, "TEST FAILED: failed to re-open fd " 1195 "%d with flags %d", oclo_rtdata[i].crt_fd, flags); 1196 } 1197 1198 if (fd != oclo_rtdata[i].crt_fd) { 1199 errx(EXIT_FAILURE, "TEST FAILED: re-opening fd %d " 1200 "returned fd %d: test design issue or lowest fd " 1201 "algorithm is broken", oclo_rtdata[i].crt_fd, fd); 1202 } 1203 } 1204 1205 (void) printf("TEST PASSED: successfully reopened fds post-fork"); 1206 } 1207 1208 /* 1209 * Look for the verification program in the same directory that this program is 1210 * found in. Note, that isn't the same thing as the current working directory. 1211 */ 1212 static void 1213 oclo_exec(void) 1214 { 1215 ssize_t ret; 1216 char dir[PATH_MAX], file[PATH_MAX]; 1217 char **argv; 1218 1219 ret = readlink("/proc/self/path/a.out", dir, sizeof (dir)); 1220 if (ret < 0) { 1221 err(EXIT_FAILURE, "TEST FAILED: failed to read our a.out path " 1222 "from /proc"); 1223 } else if (ret == 0) { 1224 errx(EXIT_FAILURE, "TEST FAILED: reading /proc/self/path/a.out " 1225 "returned 0 bytes"); 1226 } else if (ret == sizeof (dir)) { 1227 errx(EXIT_FAILURE, "TEST FAILED: Using /proc/self/path/a.out " 1228 "requires truncation"); 1229 } 1230 1231 if (snprintf(file, sizeof (file), "%s/%s", dirname(dir), OCLO_VERIFY) >= 1232 sizeof (file)) { 1233 errx(EXIT_FAILURE, "TEST FAILED: cannot assemble exec path " 1234 "name: internal buffer overflow"); 1235 } 1236 1237 /* We need an extra for both the NULL terminator and the program name */ 1238 argv = calloc(oclo_rtdata_next + 2, sizeof (char *)); 1239 if (argv == NULL) { 1240 err(EXIT_FAILURE, "TEST FAILED: failed to allocate exec " 1241 "argument array"); 1242 } 1243 1244 argv[0] = file; 1245 for (size_t i = 0; i < oclo_rtdata_next; i++) { 1246 if (asprintf(&argv[i + 1], "0x%x", oclo_rtdata[i].crt_flags) == 1247 -1) { 1248 err(EXIT_FAILURE, "TEST FAILED: failed to assemble " 1249 "exec argument %zu", i + 1); 1250 } 1251 } 1252 1253 (void) execv(file, argv); 1254 warn("TEST FAILED: failed to exec verifier %s", file); 1255 } 1256 1257 int 1258 main(void) 1259 { 1260 int ret = EXIT_SUCCESS; 1261 siginfo_t cret; 1262 1263 /* 1264 * Before we do anything else close all FDs that aren't standard. We 1265 * don't want anything the test suite environment may have left behind. 1266 */ 1267 (void) closefrom(STDERR_FILENO + 1); 1268 1269 /* 1270 * Treat failure during this set up phase as a hard failure. There's no 1271 * reason to continue if we can't successfully create the FDs we expect. 1272 */ 1273 for (size_t i = 0; i < ARRAY_SIZE(oclo_create); i++) { 1274 oclo_create[i].clo_func(&oclo_create[i]); 1275 } 1276 1277 pid_t child = forkx(FORK_NOSIGCHLD | FORK_WAITPID); 1278 if (child == 0) { 1279 if (!oclo_verify_fork()) { 1280 ret = EXIT_FAILURE; 1281 } 1282 1283 oclo_child_reopen(); 1284 1285 oclo_exec(); 1286 ret = EXIT_FAILURE; 1287 _exit(ret); 1288 } 1289 1290 if (waitid(P_PID, child, &cret, WEXITED) < 0) { 1291 err(EXIT_FAILURE, "TEST FAILED: internal test failure waiting " 1292 "for forked child to report"); 1293 } 1294 1295 if (cret.si_code != CLD_EXITED) { 1296 warnx("TEST FAILED: child process did not successfully exit: " 1297 "found si_code: %d", cret.si_code); 1298 ret = EXIT_FAILURE; 1299 } else if (cret.si_status != 0) { 1300 warnx("TEST FAILED: child process did not exit with code 0: " 1301 "found %d", cret.si_status); 1302 ret = EXIT_FAILURE; 1303 } 1304 1305 if (ret == EXIT_SUCCESS) { 1306 (void) printf("All tests passed successfully\n"); 1307 } 1308 1309 return (ret); 1310 } 1311