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
oclo_flags_match(const clo_rtdata_t * rt,bool child)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
oclo_record(const clo_create_t * c,int fd,int exp_flags,const char * desc)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
oclo_file(const clo_create_t * c)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
oclo_open(const clo_create_t * c)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
oclo_setfd_common(const clo_create_t * c,int targ_flags)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
oclo_setfd_none(const clo_create_t * c)228 oclo_setfd_none(const clo_create_t *c)
229 {
230 oclo_setfd_common(c, 0);
231 }
232
233 static void
oclo_setfd_exec(const clo_create_t * c)234 oclo_setfd_exec(const clo_create_t *c)
235 {
236 oclo_setfd_common(c, FD_CLOEXEC);
237 }
238
239 static void
oclo_setfd_fork(const clo_create_t * c)240 oclo_setfd_fork(const clo_create_t *c)
241 {
242 oclo_setfd_common(c, FD_CLOFORK);
243 }
244
245 static void
oclo_setfd_both(const clo_create_t * c)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
oclo_fdup_common(const clo_create_t * c,int targ_flags,int cmd)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
oclo_fdupfd(const clo_create_t * c)290 oclo_fdupfd(const clo_create_t *c)
291 {
292 oclo_fdup_common(c, 0, F_DUPFD);
293 }
294
295 static void
oclo_fdupfd_fork(const clo_create_t * c)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
oclo_fdupfd_exec(const clo_create_t * c)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
oclo_fdup2fd(const clo_create_t * c)308 oclo_fdup2fd(const clo_create_t *c)
309 {
310 oclo_fdup_common(c, 0, F_DUP2FD);
311 }
312
313 static void
oclo_fdup2fd_fork(const clo_create_t * c)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
oclo_fdup2fd_exec(const clo_create_t * c)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
oclo_fdup3fd_none(const clo_create_t * c)326 oclo_fdup3fd_none(const clo_create_t *c)
327 {
328 oclo_fdup_common(c, 0, F_DUP3FD);
329 }
330
331 static void
oclo_fdup3fd_exec(const clo_create_t * c)332 oclo_fdup3fd_exec(const clo_create_t *c)
333 {
334 oclo_fdup_common(c, FD_CLOEXEC, F_DUP3FD);
335 }
336
337 static void
oclo_fdup3fd_fork(const clo_create_t * c)338 oclo_fdup3fd_fork(const clo_create_t *c)
339 {
340 oclo_fdup_common(c, FD_CLOFORK, F_DUP3FD);
341 }
342
343 static void
oclo_fdup3fd_both(const clo_create_t * c)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
oclo_dup_common(const clo_create_t * c,int targ_flags,bool v3)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
oclo_dup2(const clo_create_t * c)370 oclo_dup2(const clo_create_t *c)
371 {
372 oclo_dup_common(c, 0, false);
373 }
374
375 static void
oclo_dup3_none(const clo_create_t * c)376 oclo_dup3_none(const clo_create_t *c)
377 {
378 oclo_dup_common(c, 0, true);
379 }
380
381 static void
oclo_dup3_exec(const clo_create_t * c)382 oclo_dup3_exec(const clo_create_t *c)
383 {
384 oclo_dup_common(c, FD_CLOEXEC, true);
385 }
386
387 static void
oclo_dup3_fork(const clo_create_t * c)388 oclo_dup3_fork(const clo_create_t *c)
389 {
390 oclo_dup_common(c, FD_CLOFORK, true);
391 }
392
393 static void
oclo_dup3_both(const clo_create_t * c)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
oclo_pipe(const clo_create_t * c)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
oclo_socket(const clo_create_t * c)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
oclo_accept_common(const clo_create_t * c,int targ_flags,bool a4)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
oclo_accept(const clo_create_t * c)507 oclo_accept(const clo_create_t *c)
508 {
509 oclo_accept_common(c, 0, false);
510 }
511
512 static void
oclo_accept4_none(const clo_create_t * c)513 oclo_accept4_none(const clo_create_t *c)
514 {
515 oclo_accept_common(c, 0, true);
516 }
517
518 static void
oclo_accept4_fork(const clo_create_t * c)519 oclo_accept4_fork(const clo_create_t *c)
520 {
521 oclo_accept_common(c, FD_CLOFORK, true);
522 }
523
524 static void
oclo_accept4_exec(const clo_create_t * c)525 oclo_accept4_exec(const clo_create_t *c)
526 {
527 oclo_accept_common(c, FD_CLOEXEC, true);
528 }
529
530 static void
oclo_accept4_both(const clo_create_t * c)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
oclo_rights_common(const clo_create_t * c,int targ_flags)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
oclo_rights_none(const clo_create_t * c)630 oclo_rights_none(const clo_create_t *c)
631 {
632 oclo_rights_common(c, 0);
633 }
634
635 static void
oclo_rights_exec(const clo_create_t * c)636 oclo_rights_exec(const clo_create_t *c)
637 {
638 oclo_rights_common(c, FD_CLOEXEC);
639 }
640
641 static void
oclo_rights_fork(const clo_create_t * c)642 oclo_rights_fork(const clo_create_t *c)
643 {
644 oclo_rights_common(c, FD_CLOFORK);
645 }
646
647 static void
oclo_rights_both(const clo_create_t * c)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
oclo_verify_fork(void)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
oclo_child_reopen(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
oclo_exec(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
main(void)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