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