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