xref: /freebsd/cddl/contrib/opensolaris/tests/os-tests/tests/oclo/oclo.c (revision 4140012f83690e9ed9b31d87c16e4f698332e24c)
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