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
oclo_flags_match(const clo_rtdata_t * rt,bool child)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
oclo_record(const clo_create_t * c,int fd,int exp_flags,const char * desc)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
oclo_file(const clo_create_t * c)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
oclo_open(const clo_create_t * c)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
oclo_setfd_common(const clo_create_t * c,int targ_flags)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
oclo_setfd_none(const clo_create_t * c)224 oclo_setfd_none(const clo_create_t *c)
225 {
226 oclo_setfd_common(c, 0);
227 }
228
229 static void
oclo_setfd_exec(const clo_create_t * c)230 oclo_setfd_exec(const clo_create_t *c)
231 {
232 oclo_setfd_common(c, FD_CLOEXEC);
233 }
234
235 static void
oclo_setfd_fork(const clo_create_t * c)236 oclo_setfd_fork(const clo_create_t *c)
237 {
238 oclo_setfd_common(c, FD_CLOFORK);
239 }
240
241 static void
oclo_setfd_both(const clo_create_t * c)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
oclo_fdup_common(const clo_create_t * c,int targ_flags,int cmd)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
oclo_fdupfd(const clo_create_t * c)286 oclo_fdupfd(const clo_create_t *c)
287 {
288 oclo_fdup_common(c, 0, F_DUPFD);
289 }
290
291 static void
oclo_fdupfd_fork(const clo_create_t * c)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
oclo_fdupfd_exec(const clo_create_t * c)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
oclo_fdup2fd(const clo_create_t * c)304 oclo_fdup2fd(const clo_create_t *c)
305 {
306 oclo_fdup_common(c, 0, F_DUP2FD);
307 }
308
309 static void
oclo_fdup2fd_fork(const clo_create_t * c)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
oclo_fdup2fd_exec(const clo_create_t * c)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
oclo_fdup3fd_none(const clo_create_t * c)322 oclo_fdup3fd_none(const clo_create_t *c)
323 {
324 oclo_fdup_common(c, 0, F_DUP3FD);
325 }
326
327 static void
oclo_fdup3fd_exec(const clo_create_t * c)328 oclo_fdup3fd_exec(const clo_create_t *c)
329 {
330 oclo_fdup_common(c, FD_CLOEXEC, F_DUP3FD);
331 }
332
333 static void
oclo_fdup3fd_fork(const clo_create_t * c)334 oclo_fdup3fd_fork(const clo_create_t *c)
335 {
336 oclo_fdup_common(c, FD_CLOFORK, F_DUP3FD);
337 }
338
339 static void
oclo_fdup3fd_both(const clo_create_t * c)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
oclo_dup_common(const clo_create_t * c,int targ_flags,bool v3)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
oclo_dup2(const clo_create_t * c)366 oclo_dup2(const clo_create_t *c)
367 {
368 oclo_dup_common(c, 0, false);
369 }
370
371 static void
oclo_dup3_none(const clo_create_t * c)372 oclo_dup3_none(const clo_create_t *c)
373 {
374 oclo_dup_common(c, 0, true);
375 }
376
377 static void
oclo_dup3_exec(const clo_create_t * c)378 oclo_dup3_exec(const clo_create_t *c)
379 {
380 oclo_dup_common(c, FD_CLOEXEC, true);
381 }
382
383 static void
oclo_dup3_fork(const clo_create_t * c)384 oclo_dup3_fork(const clo_create_t *c)
385 {
386 oclo_dup_common(c, FD_CLOFORK, true);
387 }
388
389 static void
oclo_dup3_both(const clo_create_t * c)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
oclo_pipe(const clo_create_t * c)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
oclo_socket(const clo_create_t * c)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
oclo_accept_common(const clo_create_t * c,int targ_flags,bool a4)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
oclo_accept(const clo_create_t * c)503 oclo_accept(const clo_create_t *c)
504 {
505 oclo_accept_common(c, 0, false);
506 }
507
508 static void
oclo_accept4_none(const clo_create_t * c)509 oclo_accept4_none(const clo_create_t *c)
510 {
511 oclo_accept_common(c, 0, true);
512 }
513
514 static void
oclo_accept4_fork(const clo_create_t * c)515 oclo_accept4_fork(const clo_create_t *c)
516 {
517 oclo_accept_common(c, FD_CLOFORK, true);
518 }
519
520 static void
oclo_accept4_exec(const clo_create_t * c)521 oclo_accept4_exec(const clo_create_t *c)
522 {
523 oclo_accept_common(c, FD_CLOEXEC, true);
524 }
525
526 static void
oclo_accept4_both(const clo_create_t * c)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
oclo_rights_common(const clo_create_t * c,int targ_flags)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
oclo_rights_none(const clo_create_t * c)626 oclo_rights_none(const clo_create_t *c)
627 {
628 oclo_rights_common(c, 0);
629 }
630
631 static void
oclo_rights_exec(const clo_create_t * c)632 oclo_rights_exec(const clo_create_t *c)
633 {
634 oclo_rights_common(c, FD_CLOEXEC);
635 }
636
637 static void
oclo_rights_fork(const clo_create_t * c)638 oclo_rights_fork(const clo_create_t *c)
639 {
640 oclo_rights_common(c, FD_CLOFORK);
641 }
642
643 static void
oclo_rights_both(const clo_create_t * c)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
oclo_verify_fork(void)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
oclo_child_reopen(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
oclo_exec(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
main(void)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