xref: /freebsd/tools/regression/security/open_to_operation/open_to_operation.c (revision 59e2ff550c448126b988150ce800cdf73bb5103e)
1 /*-
2  * Copyright (c) 2008 Robert N. M. Watson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 /*-
28  * This regression test attempts to confirm that the flags used at open-time
29  * for a file descriptor properly limit system calls that should be affected
30  * by those flags.  Currently:
31  *
32  * System call                    Policy                      Tested
33  * __acl_aclcheck_fd(2)           any                         no
34  * __acl_delete_fd(2)             any                         no
35  * __acl_get_fd(2)                any                         no
36  * __acl_set_fd(2)                any                         no
37  * aio_fsync(2)                   any                         no
38  * aio_read(2)                    O_RDONLY or O_RDWR          yes
39  * aio_write(2)                   O_WRONLY or O_RDWR          yes
40  * dup(2)                         any                         yes
41  * dup2(2)                        any                         yes
42  * extattr_delete_fd(2)           O_WRONLY or O_RDWR          no
43  * extattr_get_fd(2)              O_RDONLY or O_RDWR          no
44  * extattr_list_fd(2)             O_RDONLY or O_RDWR          no
45  * extattr_set_fd(2)              O_WRONLY or O_RDWR          no
46  * fchdir(2)                      any directory               yes
47  * fchflags(2)                    any                         yes
48  * fchmod(2)                      any                         yes
49  * fchown(2)                      any                         yes
50  * flock(2)                       any                         yes
51  * fpathconf(2)                   any                         yes
52  * fstat(2)                       any                         yes
53  * fstatfs(2)                     any                         yes
54  * fsync(2)                       any                         yes
55  * ftruncate(2)                   O_WRONLY or O_RDWR          yes
56  * futimes(2)                     any                         yes
57  * getdents(2)                    O_RDONLY directory          yes
58  * lseek(2)                       any                         yes
59  * mmap(2) PROT_READ              O_RDONLY or O_RDWR          yes
60  * mmap(2) PROT_WRITE             O_WRONLY or O_RDWR          yes
61  * mmap(2) PROT_WRITE + MAP_PRIV  O_RDONLY or O_RDWR          yes
62  * mmap(2) PROT_EXEC              O_RDONLY or O_RDWR          yes
63  * pread(2)                       O_RDONLY or O_RDWR          yes
64  * preadv(2)                      O_RDONLY or O_RDWR          yes
65  * pwrite(2)                      O_WRONLY or O_RDWR          yes
66  * pwritev(2)                     O_WRONLY or O_RDWR          yes
67  * read(2)                        O_RDONLY or O_RDWR          yes
68  * readv(2)                       O_RDONLY or O_RDWR          yes
69  * sendfile(2)                    O_RDONLY or O_RDWR on file  yes
70  * write(2)                       O_WRONLY or O_RDWR          yes
71  * writev(2)                      O_WRONLY or O_RDWR          yes
72  *
73  * These checks do not verify that original permissions would allow the
74  * operation or that open is properly impacted by permissions, just that once
75  * a file descriptor is held, open-time limitations are implemented.
76  *
77  * We do, however, test that directories cannot be opened as writable.
78  *
79  * XXXRW: Arguably we should also test combinations of bits to mmap(2).
80  *
81  * XXXRW: Should verify mprotect() remapping limits.
82  *
83  * XXXRW: kqueue(2)/kevent(2), poll(2), select(2)
84  *
85  * XXXRW: oaio_read(2), oaio_write(2), freebsd6_*(2).
86  *
87  * XXXRW: __mac*(2)
88  *
89  * XXXRW: message queue and shared memory fds?
90  */
91 
92 #include <sys/cdefs.h>
93 __FBSDID("$FreeBSD$");
94 
95 #include <sys/param.h>
96 #include <sys/mman.h>
97 #include <sys/mount.h>
98 #include <sys/socket.h>
99 #include <sys/stat.h>
100 #include <sys/sysctl.h>
101 #include <sys/uio.h>
102 
103 #include <aio.h>
104 #include <dirent.h>
105 #include <err.h>
106 #include <errno.h>
107 #include <fcntl.h>
108 #include <limits.h>
109 #include <stdio.h>
110 #include <stdlib.h>
111 #include <string.h>
112 #include <unistd.h>
113 
114 #define	PERM_FILE	0644		/* Allow read, write.  Someday exec? */
115 #define	PERM_DIR	0755		/* Allow read, write, exec. */
116 
117 /*
118  * Modes to try all tests with.
119  */
120 static const int file_modes[] = { O_RDONLY, O_WRONLY, O_RDWR,
121     O_RDONLY | O_TRUNC, O_WRONLY | O_TRUNC, O_RDWR | O_TRUNC };
122 static const int file_modes_count = sizeof(file_modes) / sizeof(int);
123 
124 static const int dir_modes[] = { O_RDONLY };
125 static const int dir_modes_count = sizeof(dir_modes) / sizeof(int);
126 
127 static int testnum;
128 static int aio_present;
129 
130 static void
131 ok_mode(const char *testname, const char *comment, int mode)
132 {
133 
134 	testnum++;
135 	if (comment == NULL)
136 		printf("ok %d - %s # mode 0x%x\n", testnum, testname, mode);
137 	else
138 		printf("ok %d - %s # mode 0x%x - %s\n", testnum, testname,
139 		    mode, comment);
140 }
141 
142 static void
143 notok_mode(const char *testname, const char *comment, int mode)
144 {
145 
146 	testnum++;
147 	if (comment == NULL)
148 		printf("not ok %d - %s # mode 0x%x\n", testnum, testname,
149 		    mode);
150 	else
151 		printf("not ok %d - %s # mode 0x%x - %s\n", testnum, testname,
152 		    mode, comment);
153 }
154 
155 /*
156  * Before we get started, confirm that we can't open directories writable.
157  */
158 static void
159 try_directory_open(const char *testname, const char *directory,
160     int mode, int expected_errno)
161 {
162 	int dfd;
163 
164 	dfd = open(directory, mode);
165 	if (dfd >= 0) {
166 		if (expected_errno)
167 			notok_mode(testname, "opened", mode);
168 		else
169 			ok_mode(testname, NULL, mode);
170 		close(dfd);
171 	} else {
172 		if (expected_errno && expected_errno == expected_errno)
173 			ok_mode(testname, NULL, mode);
174 		else if (expected_errno)
175 			notok_mode(testname, "wrong errno", mode);
176 		else
177 			notok_mode(testname, "failed", mode);
178 	}
179 }
180 
181 static void
182 check_directory_open_modes(const char *directory, const int *modes,
183     int modes_count)
184 {
185 	int expected_errno, i, mode;
186 
187 	/*
188 	 * Directories should only open with O_RDONLY.  Notice that we use
189 	 * file_modes and not dirmodes.
190 	 */
191 	for (i = 0; i < modes_count; i++) {
192 		mode = modes[i];
193 		if (mode == O_RDONLY)
194 			expected_errno = 0;
195 		else
196 			expected_errno = EISDIR;
197 		try_directory_open(__func__, directory, mode,
198 		    expected_errno);
199 	}
200 }
201 
202 static void
203 check_dup(const char *testname, const char *path, const int *modes,
204     int modes_count)
205 {
206 	int dfd, fd, i, mode;
207 
208 	/*
209 	 * dup() should work regardless of open mode.
210 	 */
211 	for (i = 0; i < modes_count; i++) {
212 		mode = modes[i];
213 		fd = open(path, mode);
214 		if (fd < 0) {
215 			notok_mode(testname, "open", mode);
216 			continue;
217 		}
218 		dfd = dup(fd);
219 		if (dfd >= 0) {
220 			ok_mode(testname, NULL, mode);
221 			close(dfd);
222 		} else
223 			notok_mode(testname, NULL, mode);
224 		close(fd);
225 	}
226 }
227 
228 static void
229 check_dup2(const char *testname, const char *path, const int *modes,
230     int modes_count)
231 {
232 	int dfd, fd, i, mode;
233 
234 	/*
235 	 * dup2() should work regardless of open mode.
236 	 */
237 	for (i = 0; i < modes_count; i++) {
238 		mode = modes[i];
239 		fd = open(path, mode);
240 		if (fd < 0) {
241 			notok_mode(testname, "open", mode);
242 			continue;
243 		}
244 		dfd = dup2(fd, 500);	/* Arbitrary but high number. */
245 		if (dfd >= 0) {
246 			ok_mode(testname, NULL, mode);
247 			close(dfd);
248 		} else
249 			notok_mode(testname, NULL, mode);
250 		close(fd);
251 	}
252 }
253 
254 static void
255 check_fchdir(const char *testname, const char *path, const int *modes,
256     int modes_count)
257 {
258 	int fd, i, mode;
259 
260 	/*
261 	 * fchdir() should work regardless of open mode.
262 	 */
263 	for (i = 0; i < modes_count; i++) {
264 		mode = modes[i];
265 		fd = open(path, mode);
266 		if (fd < 0) {
267 			notok_mode(testname, "open", mode);
268 			continue;
269 		}
270 		if (fchdir(fd) == 0)
271 			ok_mode(testname, NULL, mode);
272 		else
273 			notok_mode(testname, "failed", mode);
274 		close(fd);
275 	}
276 }
277 
278 static void
279 check_fchflags(const char *testname, const char *path, const int *modes,
280     int modes_count)
281 {
282 	int fd, i, mode;
283 
284 	/*
285 	 * fchflags() should work regardless of open mode.
286 	 */
287 	for (i = 0; i < modes_count; i++) {
288 		mode = modes[i];
289 		fd = open(path, mode);
290 		if (fd < 0) {
291 			notok_mode(testname, "open", mode);
292 			continue;
293 		}
294 		if (fchflags(fd, UF_NODUMP) == 0)
295 			ok_mode(testname, NULL, mode);
296 		else
297 			notok_mode(testname, "failed", mode);
298 		close(fd);
299 	}
300 }
301 
302 static void
303 check_fchmod(const char *testname, const char *path, int setmode,
304     const int *modes, int modes_count)
305 {
306 	int fd, i, mode;
307 
308 	/*
309 	 * fchmod() should work regardless of open mode.
310 	 */
311 	for (i = 0; i < modes_count; i++) {
312 		mode = modes[i];
313 		fd = open(path, mode);
314 		if (fd < 0) {
315 			notok_mode(testname, "open", mode);
316 			continue;
317 		}
318 		if (fchmod(fd, setmode) == 0)
319 			ok_mode(testname, NULL, mode);
320 		else
321 			notok_mode(testname, "failed", mode);
322 		close(fd);
323 	}
324 }
325 
326 static void
327 check_fchown(const char *testname, const char *path, const int *modes,
328     int modes_count)
329 {
330 	int fd, i, mode;
331 
332 	/*
333 	 * fchown() should work regardless of open mode.
334 	 */
335 	for (i = 0; i < modes_count; i++) {
336 		mode = modes[i];
337 		fd = open(path, mode);
338 		if (fd < 0) {
339 			notok_mode(testname, "open", mode);
340 			continue;
341 		}
342 		if (fchown(fd, -1, -1) == 0)
343 			ok_mode(testname, NULL, mode);
344 		else
345 			notok_mode(testname, "failed", mode);
346 		close(fd);
347 	}
348 }
349 
350 static void
351 check_flock(const char *testname, const char *path, const int *modes,
352     int modes_count)
353 {
354 	int fd, i, mode;
355 
356 	/*
357 	 * flock() should work regardless of open mode.
358 	 */
359 	for (i = 0; i < modes_count; i++) {
360 		mode = modes[i];
361 		fd = open(path, mode);
362 		if (fd < 0) {
363 			notok_mode(testname, "open", mode);
364 			continue;
365 		}
366 		if (flock(fd, LOCK_EX) == 0)
367 			ok_mode(testname, NULL, mode);
368 		else
369 			notok_mode(testname, "failed", mode);
370 		close(fd);
371 	}
372 }
373 
374 static void
375 check_fpathconf(const char *testname, const char *path, const int *modes,
376     int modes_count)
377 {
378 	int fd, i, mode;
379 	long l;
380 
381 	/*
382 	 * fpathconf() should work regardless of open mode.
383 	 */
384 	for (i = 0; i < modes_count; i++) {
385 		mode = modes[i];
386 		fd = open(path, mode);
387 		if (fd < 0) {
388 			notok_mode(testname, "open", mode);
389 			continue;
390 		}
391 		l = fpathconf(fd, _PC_FILESIZEBITS);
392 		if (l >= 0)
393 			ok_mode(testname, NULL, mode);
394 		else
395 			notok_mode(testname, "failed", mode);
396 		close(fd);
397 	}
398 }
399 
400 static void
401 check_fstat(const char *testname, const char *path, const int *modes,
402     int modes_count)
403 {
404 	struct stat sb;
405 	int fd, i, mode;
406 
407 	/*
408 	 * fstat() should work regardless of open mode.
409 	 */
410 	for (i = 0; i < modes_count; i++) {
411 		mode = modes[i];
412 		fd = open(path, mode);
413 		if (fd < 0) {
414 			notok_mode(testname, "open", mode);
415 			continue;
416 		}
417 		if (fstat(fd, &sb) == 0)
418 			ok_mode(testname, NULL, mode);
419 		else
420 			notok_mode(testname, "failed", mode);
421 		close(fd);
422 	}
423 }
424 
425 static void
426 check_fstatfs(const char *testname, const char *path, const int *modes,
427     int modes_count)
428 {
429 	struct statfs statfs;
430 	int fd, i, mode;
431 
432 	/*
433 	 * fstatfs() should work regardless of open mode.
434 	 */
435 	for (i = 0; i < modes_count; i++) {
436 		mode = modes[i];
437 		fd = open(path, mode);
438 		if (fd < 0) {
439 			notok_mode(testname, "open", mode);
440 			continue;
441 		}
442 		if (fstatfs(fd, &statfs) == 0)
443 			ok_mode(testname, NULL, mode);
444 		else
445 			notok_mode(testname, "failed", mode);
446 		close(fd);
447 	}
448 }
449 
450 static void
451 check_fsync(const char *testname, const char *path, const int *modes,
452     int modes_count)
453 {
454 	int fd, i, mode;
455 
456 	/*
457 	 * fstatfs() should work regardless of open mode.
458 	 */
459 	for (i = 0; i < modes_count; i++) {
460 		mode = modes[i];
461 		fd = open(path, mode);
462 		if (fd < 0) {
463 			notok_mode(testname, "open", mode);
464 			continue;
465 		}
466 		if (fsync(fd) == 0)
467 			ok_mode(testname, NULL, mode);
468 		else
469 			notok_mode(testname, "failed", mode);
470 		close(fd);
471 	}
472 }
473 
474 static void
475 check_ftruncate(const char *testname, const char *path, const int *modes,
476     int modes_count)
477 {
478 	struct stat sb;
479 	int fd, i, mode;
480 
481 	/*
482 	 * ftruncate() should work as long as long as (mode & O_ACCMODE) is
483 	 * O_RDWR or O_WRONLY.
484 	 *
485 	 * Directories should never be writable, so this test should always
486 	 * pass for directories...
487 	 */
488 	for (i = 0; i < modes_count; i++) {
489 		mode = modes[i];
490 		fd = open(path, mode);
491 		if (fd < 0) {
492 			notok_mode(testname, "open", mode);
493 			notok_mode(testname, "truncate1 skipped", mode);
494 			notok_mode(testname, "truncate2 skipped", mode);
495 			notok_mode(testname, "truncate3 skipped", mode);
496 			continue;
497 		}
498 		if (fstat(fd, &sb) < 0) {
499 			notok_mode(testname, "fstat", mode);
500 			notok_mode(testname, "truncate1 skipped", mode);
501 			notok_mode(testname, "truncate2 skipped", mode);
502 			notok_mode(testname, "truncate3 skipped", mode);
503 			close(fd);
504 			continue;
505 		}
506 		ok_mode(testname, "setup", mode);
507 
508 		/* Truncate to grow file. */
509 		if (ftruncate(fd, sb.st_size + 1) == 0) {
510 			if (((mode & O_ACCMODE) == O_WRONLY) ||
511 			    ((mode & O_ACCMODE) == O_RDWR))
512 				ok_mode(testname, "truncate1 succeeded",
513 				    mode);
514 			else {
515 				notok_mode(testname, "truncate1 succeeded",
516 				    mode);
517 				notok_mode(testname, "truncate2 skipped",
518 				    mode);
519 				notok_mode(testname, "truncate3 skipped",
520 				    mode);
521 				close(fd);
522 				continue;
523 			}
524 		} else {
525 			if (((mode & O_ACCMODE) == O_WRONLY) ||
526 			    ((mode & O_ACCMODE) == O_RDWR)) {
527 				notok_mode(testname, "truncate1 failed",
528 				    mode);
529 				notok_mode(testname, "truncate2 skipped",
530 				    mode);
531 				notok_mode(testname, "truncate3 skipped",
532 				    mode);
533 				close(fd);
534 				continue;
535 			} else
536 				ok_mode(testname, "truncate1 failed", mode);
537 		}
538 
539 		/* Truncate to same size. */
540 		if (ftruncate(fd, sb.st_size + 1) == 0) {
541 			if (((mode & O_ACCMODE) == O_WRONLY) ||
542 			    ((mode & O_ACCMODE) == O_RDWR))
543 				ok_mode(testname, "truncate2 succeeded",
544 				    mode);
545 			else {
546 				notok_mode(testname, "truncate2 succeeded",
547 				    mode);
548 				notok_mode(testname, "truncate3 skipped",
549 				    mode);
550 				close(fd);
551 				continue;
552 			}
553 		} else {
554 			if (((mode & O_ACCMODE) == O_WRONLY) ||
555 			    ((mode & O_ACCMODE) == O_RDWR)) {
556 				notok_mode(testname, "truncate2 failed",
557 				    mode);
558 				notok_mode(testname, "truncate3 skipped",
559 				    mode);
560 				close(fd);
561 				continue;
562 			} else
563 				ok_mode(testname, "truncate2 failed", mode);
564 		}
565 
566 		/* Truncate to shrink. */
567 		if (ftruncate(fd, sb.st_size) == 0) {
568 			if (((mode & O_ACCMODE) == O_WRONLY) ||
569 			    ((mode & O_ACCMODE) == O_RDWR))
570 				ok_mode(testname, "truncate3 succeeded",
571 				    mode);
572 			else
573 				notok_mode(testname, "truncate3 succeeded",
574 				    mode);
575 		} else {
576 			if (((mode & O_ACCMODE) == O_WRONLY) ||
577 			    ((mode & O_ACCMODE) == O_RDWR))
578 				notok_mode(testname, "truncate3 failed",
579 				    mode);
580 			else
581 				ok_mode(testname, "truncate3 failed", mode);
582 		}
583 		close(fd);
584 	}
585 }
586 
587 static void
588 check_futimes(const char *testname, const char *path, const int *modes,
589     int modes_count)
590 {
591 	int fd, i, mode;
592 
593 	/*
594 	 * futimes() should work regardless of open mode.
595 	 */
596 	for (i = 0; i < modes_count; i++) {
597 		mode = modes[i];
598 		fd = open(path, mode);
599 		if (fd < 0) {
600 			notok_mode(testname, "open", mode);
601 			continue;
602 		}
603 		if (futimes(fd, NULL) == 0)
604 			ok_mode(testname, NULL, mode);
605 		else
606 			notok_mode(testname, "failed", mode);
607 		close(fd);
608 	}
609 }
610 
611 static void
612 check_lseek(const char *testname, const char *path, const int *modes,
613     int modes_count)
614 {
615 	int fd, i, mode;
616 
617 	/*
618 	 * lseek() should work regardless of open mode.
619 	 */
620 	for (i = 0; i < modes_count; i++) {
621 		mode = modes[i];
622 		fd = open(path, mode);
623 		if (fd < 0) {
624 			notok_mode(testname, "open", mode);
625 			continue;
626 		}
627 		if (lseek(fd, 100, SEEK_SET) == 100)
628 			ok_mode(testname, NULL, mode);
629 		else
630 			notok_mode(testname, "failed", mode);
631 		close(fd);
632 	}
633 }
634 
635 static void
636 check_getdents(const char *testname, const char *path, int isdir,
637     const int *modes, int modes_count)
638 {
639 	int fd, i, mode;
640 	char buf[8192];
641 
642 	/*
643 	 * getdents() should always work on directories and never on files,
644 	 * assuming directories are always opened for read (which they are).
645 	 */
646 	for (i = 0; i < modes_count; i++) {
647 		mode = modes[i];
648 		fd = open(path, mode);
649 		if (fd < 0) {
650 			notok_mode(testname, "open", mode);
651 			continue;
652 		}
653 		if (getdents(fd, buf, sizeof(buf)) >= 0) {
654 			if (isdir && ((mode & O_ACCMODE) == O_RDONLY))
655 				ok_mode(testname, "directory succeeded",
656 				    mode);
657 			else if (isdir)
658 				notok_mode(testname, "directory succeeded",
659 				    mode);
660 			else
661 				notok_mode(testname, "file succeeded", mode);
662 		} else {
663 			if (isdir && ((mode & O_ACCMODE) == O_RDONLY))
664 				notok_mode(testname, "directory failed",
665 				    mode);
666 			else if (isdir)
667 				ok_mode(testname, "directory failed", mode);
668 			else
669 				ok_mode(testname, "file failed", mode);
670 		}
671 		close(fd);
672 	}
673 }
674 
675 static void
676 check_sendfile(const char *testname, const char *path, int isdir,
677     const int *modes, int modes_count)
678 {
679 	int fd, i, mode, sv[2];
680 	off_t sent;
681 
682 	/*
683 	 * sendfile() should work only on files, and only when the access mode
684 	 * is O_RDONLY or O_RDWR.
685 	 */
686 	for (i = 0; i < modes_count; i++) {
687 		mode = modes[i];
688 		fd = open(path, mode);
689 		if (fd < 0) {
690 			notok_mode(testname, "open", mode);
691 			continue;
692 		}
693 		if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sv) < 0) {
694 			notok_mode(testname, "socketpair", mode);
695 			continue;
696 		}
697 		if (sendfile(fd, sv[0], 0, 1, NULL, &sent, 0) == 0) {
698 			if (isdir)
699 				notok_mode(testname, "directory succeeded",
700 				    mode);
701 			else if (((mode & O_ACCMODE) == O_RDONLY) ||
702 			    ((mode & O_ACCMODE) == O_RDWR))
703 				ok_mode(testname, "succeeded", mode);
704 			else
705 				notok_mode(testname, "succeeded", mode);
706 		} else {
707 			if (isdir)
708 				ok_mode(testname, "directory failed", mode);
709 			else if (((mode & O_ACCMODE) == O_RDONLY) ||
710 			    ((mode & O_ACCMODE) == O_RDWR))
711 				notok_mode(testname, "failed", mode);
712 			else
713 				ok_mode(testname, "failed", mode);
714 		}
715 		close(sv[0]);
716 		close(sv[1]);
717 		close(fd);
718 	}
719 }
720 
721 /*
722  * Various functions write, so just make write-like wrappers for them.
723  */
724 typedef ssize_t (*write_fn)(int d, const void *buf, size_t nbytes);
725 
726 static ssize_t
727 writev_wrapper(int d, const void *buf, size_t nbytes)
728 {
729 	struct iovec iov;
730 
731 	iov.iov_base = (void *)buf;
732 	iov.iov_len = nbytes;
733 	return (writev(d, &iov, 1));
734 }
735 
736 static ssize_t
737 pwrite_wrapper(int d, const void *buf, size_t nbytes)
738 {
739 
740 	return (pwrite(d, buf, nbytes, 0));
741 }
742 
743 static ssize_t
744 pwritev_wrapper(int d, const void *buf, size_t nbytes)
745 {
746 	struct iovec iov;
747 
748 	iov.iov_base = (void *)buf;
749 	iov.iov_len = nbytes;
750 	return (pwritev(d, &iov, 1, 0));
751 }
752 
753 static ssize_t
754 aio_write_wrapper(int d, const void *buf, size_t nbytes)
755 {
756 	struct aiocb aiocb, *aiocb_array[1];
757 
758 	bzero(&aiocb, sizeof(aiocb));
759 	aiocb.aio_fildes = d;
760 	aiocb.aio_buf = (void *)buf;
761 	aiocb.aio_nbytes = nbytes;
762 	if (aio_write(&aiocb) < 0)
763 		return (-1);
764 	aiocb_array[0] = &aiocb;
765 	if (aio_suspend(aiocb_array, 1, NULL) < 0)
766 		return (-1);
767 	return (aio_return(&aiocb));
768 }
769 
770 static void
771 check_write(const char *testname, write_fn fn, const char *path,
772     const int *modes, int modes_count)
773 {
774 	int fd, i, mode;
775 	char ch;
776 
777 	/*
778 	 * write() should never succeed for directories, but especially
779 	 * because they can only be opened read-only.  write() on files
780 	 * should succeed for O_WRONLY and O_RDWR descriptors.
781 	 */
782 
783 	for (i = 0; i < modes_count; i++) {
784 		mode = modes[i];
785 		fd = open(path, mode);
786 		if (fd < 0) {
787 			notok_mode(testname, "open", mode);
788 			continue;
789 		}
790 		if (fn(fd, &ch, sizeof(ch)) < 0) {
791 			if ((mode & O_ACCMODE) == O_WRONLY ||
792 			    (mode & O_ACCMODE) == O_RDWR)
793 				notok_mode(testname, "write failed", mode);
794 			else
795 				ok_mode(testname, "write failed", mode);
796 		} else {
797 			if (!((mode & O_ACCMODE) == O_WRONLY ||
798 			    (mode & O_ACCMODE) == O_RDWR))
799 				notok_mode(testname, "write succeeded", mode);
800 			else
801 				ok_mode(testname, "write succeeded", mode);
802 		}
803 		close(fd);
804 	}
805 }
806 
807 /*
808  * Various functions read, so just make read-like wrappers for them.
809  */
810 typedef ssize_t (*read_fn)(int d, void *buf, size_t nbytes);
811 
812 static ssize_t
813 readv_wrapper(int d, void *buf, size_t nbytes)
814 {
815 	struct iovec iov;
816 
817 	iov.iov_base = buf;
818 	iov.iov_len = nbytes;
819 	return (readv(d, &iov, 1));
820 }
821 
822 static ssize_t
823 pread_wrapper(int d, void *buf, size_t nbytes)
824 {
825 
826 	return (pread(d, buf, nbytes, 0));
827 }
828 
829 static ssize_t
830 preadv_wrapper(int d, void *buf, size_t nbytes)
831 {
832 	struct iovec iov;
833 
834 	iov.iov_base = buf;
835 	iov.iov_len = nbytes;
836 	return (preadv(d, &iov, 1, 0));
837 }
838 
839 static ssize_t
840 aio_read_wrapper(int d, void *buf, size_t nbytes)
841 {
842 	struct aiocb aiocb, *aiocb_array[1];
843 
844 	bzero(&aiocb, sizeof(aiocb));
845 	aiocb.aio_fildes = d;
846 	aiocb.aio_buf = buf;
847 	aiocb.aio_nbytes = nbytes;
848 	if (aio_read(&aiocb) < 0)
849 		return (-1);
850 	aiocb_array[0] = &aiocb;
851 	if (aio_suspend(aiocb_array, 1, NULL) < 0)
852 		return (-1);
853 	return (aio_return(&aiocb));
854 }
855 
856 static void
857 check_read(const char *testname, read_fn fn, const char *path,
858     const int *modes, int modes_count)
859 {
860 	int fd, i, mode;
861 	char ch;
862 
863 	/*
864 	 * read() should (generally) succeeded on directories.  read() on
865 	 * files should succeed for O_RDONLY and O_RDWR descriptors.
866 	 */
867 	for (i = 0; i < modes_count; i++) {
868 		mode = modes[i];
869 		fd = open(path, mode);
870 		if (fd < 0) {
871 			notok_mode(testname, "open", mode);
872 			continue;
873 		}
874 		if (fn(fd, &ch, sizeof(ch)) < 0) {
875 			if ((mode & O_ACCMODE) == O_RDONLY ||
876 			    (mode & O_ACCMODE) == O_RDWR)
877 				notok_mode(testname, "read failed", mode);
878 			else
879 				ok_mode(testname, "read failed", mode);
880 		} else {
881 			if (!((mode & O_ACCMODE) == O_RDONLY ||
882 			    (mode & O_ACCMODE) == O_RDWR))
883 				notok_mode(testname, "read succeeded", mode);
884 			else
885 				ok_mode(testname, "read succeeded", mode);
886 		}
887 		close(fd);
888 	}
889 }
890 
891 static void
892 check_mmap_read(const char *testname, const char *path, int isdir,
893     const int *modes, int modes_count)
894 {
895 	int fd, i, mode;
896 	char *addr;
897 
898 	/*
899 	 * mmap() read should fail for directories (ideally?) but succeed for
900 	 * O_RDONLY and O_RDWR file descriptors.
901 	 */
902 	for (i = 0; i < modes_count; i++) {
903 		mode = modes[i];
904 		fd = open(path, mode);
905 		if (fd < 0) {
906 			notok_mode(testname, "open", mode);
907 			continue;
908 		}
909 		addr = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd,
910 		    0);
911 		if (addr == MAP_FAILED) {
912 			if (isdir)
913 				ok_mode(testname, "mmap dir failed", mode);
914 			else if ((mode & O_ACCMODE) == O_RDONLY ||
915 			    (mode & O_ACCMODE) == O_RDWR)
916 				notok_mode(testname, "mmap file failed",
917 				    mode);
918 			else
919 				ok_mode(testname, "mmap file failed", mode);
920 		} else {
921 			if (isdir)
922 				notok_mode(testname, "mmap dir succeeded",
923 				    mode);
924 			else if ((mode & O_ACCMODE) == O_RDONLY ||
925 			    (mode & O_ACCMODE) == O_RDWR)
926 				ok_mode(testname, "mmap file succeeded",
927 				    mode);
928 			else
929 				notok_mode(testname, "mmap file succeeded",
930 				    mode);
931 			(void)munmap(addr, getpagesize());
932 		}
933 		close(fd);
934 	}
935 }
936 
937 static void
938 check_mmap_write(const char *testname, const char *path, const int *modes,
939     int modes_count)
940 {
941 	int fd, i, mode;
942 	char *addr;
943 
944 	/*
945 	 * mmap() will always fail for directories (ideally) as they are
946 	 * always open O_RDONLY.  Check for O_WRONLY or O_RDWR to permit a
947 	 * write mapping.  This variant does a MAP_SHARED mapping, but we
948 	 * are also interested in MAP_PRIVATE.
949 	 */
950 	for (i = 0; i < modes_count; i++) {
951 		mode = modes[i];
952 		fd = open(path, mode);
953 		if (fd < 0) {
954 			notok_mode(testname, "open", mode);
955 			continue;
956 		}
957 		addr = mmap(NULL, getpagesize(), PROT_WRITE, MAP_SHARED, fd,
958 		    0);
959 		if (addr == MAP_FAILED) {
960 			if ((mode & O_ACCMODE) == O_WRONLY ||
961 			    (mode & O_ACCMODE) == O_RDWR)
962 				notok_mode(testname, "mmap failed",
963 				    mode);
964 			else
965 				ok_mode(testname, "mmap failed", mode);
966 		} else {
967 			if ((mode & O_ACCMODE) == O_WRONLY ||
968 			    (mode & O_ACCMODE) == O_RDWR)
969 				ok_mode(testname, "mmap succeeded",
970 				    mode);
971 			else
972 				notok_mode(testname, "mmap succeeded", mode);
973 			(void)munmap(addr, getpagesize());
974 		}
975 		close(fd);
976 	}
977 }
978 
979 static void
980 check_mmap_exec(const char *testname, const char *path, int isdir,
981     const int *modes, int modes_count)
982 {
983 	int fd, i, mode;
984 	char *addr;
985 
986 	/*
987 	 * mmap() exec should fail for directories (ideally?) but succeed for
988 	 * O_RDONLY and O_RDWR file descriptors.
989 	 */
990 	for (i = 0; i < modes_count; i++) {
991 		mode = modes[i];
992 		fd = open(path, mode);
993 		if (fd < 0) {
994 			notok_mode(testname, "open", mode);
995 			continue;
996 		}
997 		addr = mmap(NULL, getpagesize(), PROT_EXEC, MAP_SHARED, fd,
998 		    0);
999 		if (addr == MAP_FAILED) {
1000 			if (isdir)
1001 				ok_mode(testname, "mmap dir failed", mode);
1002 			else if ((mode & O_ACCMODE) == O_RDONLY ||
1003 			    (mode & O_ACCMODE) == O_RDWR)
1004 				notok_mode(testname, "mmap file failed",
1005 				    mode);
1006 			else
1007 				ok_mode(testname, "mmap file failed", mode);
1008 		} else {
1009 			if (isdir)
1010 				notok_mode(testname, "mmap dir succeeded",
1011 				    mode);
1012 			else if ((mode & O_ACCMODE) == O_RDONLY ||
1013 			    (mode & O_ACCMODE) == O_RDWR)
1014 				ok_mode(testname, "mmap file succeeded",
1015 				    mode);
1016 			else
1017 				notok_mode(testname, "mmap file succeeded",
1018 				    mode);
1019 			(void)munmap(addr, getpagesize());
1020 		}
1021 		close(fd);
1022 	}
1023 }
1024 
1025 static void
1026 check_mmap_write_private(const char *testname, const char *path, int isdir,
1027     const int *modes, int modes_count)
1028 {
1029 	int fd, i, mode;
1030 	char *addr;
1031 
1032 	/*
1033 	 * mmap() write private should succeed for readable descriptors
1034 	 * except for directories.
1035 	 */
1036 	for (i = 0; i < modes_count; i++) {
1037 		mode = modes[i];
1038 		fd = open(path, mode);
1039 		if (fd < 0) {
1040 			notok_mode(testname, "open", mode);
1041 			continue;
1042 		}
1043 		addr = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE,
1044 		    MAP_PRIVATE, fd, 0);
1045 		if (addr == MAP_FAILED) {
1046 			if (isdir)
1047 				ok_mode(testname, "mmap dir failed", mode);
1048 			else if ((mode & O_ACCMODE) == O_RDONLY ||
1049 			    (mode & O_ACCMODE) == O_RDWR)
1050 				notok_mode(testname, "mmap file failed",
1051 				    mode);
1052 			else
1053 				ok_mode(testname, "mmap file failed", mode);
1054 		} else {
1055 			if (isdir)
1056 				notok_mode(testname, "mmap dir succeeded",
1057 				    mode);
1058 			else if ((mode & O_ACCMODE) == O_RDONLY ||
1059 			    (mode & O_ACCMODE) == O_RDWR)
1060 				ok_mode(testname, "mmap file succeeded",
1061 				    mode);
1062 			else
1063 				notok_mode(testname, "mmap file succeeded",
1064 				    mode);
1065 			(void)munmap(addr, getpagesize());
1066 		}
1067 		close(fd);
1068 	}
1069 }
1070 
1071 int
1072 main(int argc, char *argv[])
1073 {
1074 	char dir_path[PATH_MAX], file_path[PATH_MAX];
1075 	int dummy, fd;
1076 	size_t size;
1077 
1078 	aio_present = 0;
1079 	size = sizeof(dummy);
1080 	if (sysctlbyname("vfs.aio", &dummy, &size, NULL, 0) < 0) {
1081 		if (errno == EISDIR)
1082 			aio_present = 1;
1083 	}
1084 
1085 	strlcpy(dir_path, "/tmp/open-dir.XXXXXXXXXXX", sizeof(dir_path));
1086 	if (mkdtemp(dir_path) == NULL)
1087 		err(-1, "mkdtemp");
1088 	if (chmod(dir_path, PERM_DIR) < 0) {
1089 		warn("chmod %s", dir_path);
1090 		(void)rmdir(dir_path);
1091 		exit(-1);
1092 	}
1093 	strlcpy(file_path, "/tmp/open-file.XXXXXXXXXXX", sizeof(file_path));
1094 	fd = mkstemp(file_path);
1095 	if (fd < 0) {
1096 		warn("mkstemp");
1097 		(void)rmdir(dir_path);
1098 		exit(-1);
1099 	}
1100 	close(fd);
1101 	if (chmod(file_path, PERM_FILE) < 0) {
1102 		warn("chmod %s", file_path);
1103 		(void)unlink(file_path);
1104 		(void)rmdir(dir_path);
1105 		exit(-1);
1106 	}
1107 	check_directory_open_modes(dir_path, file_modes, file_modes_count);
1108 
1109 	check_dup("check_dup_dir", dir_path, dir_modes, dir_modes_count);
1110 	check_dup("check_dup_file", file_path, file_modes, file_modes_count);
1111 
1112 	check_dup2("check_dup2_dir", dir_path, dir_modes, dir_modes_count);
1113 	check_dup2("check_dup2_file", file_path, file_modes,
1114 	    file_modes_count);
1115 
1116 	check_fchdir("check_fchdir", dir_path, dir_modes, dir_modes_count);
1117 
1118 	check_fchflags("check_fchflags_dir", dir_path, dir_modes,
1119 	    dir_modes_count);
1120 	check_fchflags("check_fchflags_file", file_path, file_modes,
1121 	    file_modes_count);
1122 
1123 	check_fchmod("check_fchmod_dir", dir_path, PERM_DIR, dir_modes,
1124 	    dir_modes_count);
1125 	check_fchmod("check_fchmod_file", file_path, PERM_FILE, file_modes,
1126 	    file_modes_count);
1127 
1128 	check_fchown("check_fchown_dir", dir_path, dir_modes,
1129 	    dir_modes_count);
1130 	check_fchown("check_fchown_file", file_path, file_modes,
1131 	    file_modes_count);
1132 
1133 	check_flock("check_flock_dir", dir_path, dir_modes, dir_modes_count);
1134 	check_flock("check_flock_file", file_path, file_modes,
1135 	    file_modes_count);
1136 
1137 	check_fpathconf("check_fpathconf_dir", dir_path, dir_modes,
1138 	    dir_modes_count);
1139 	check_fpathconf("check_fpathconf_file", file_path, file_modes,
1140 	    file_modes_count);
1141 
1142 	check_fstat("check_fstat_dir", dir_path, dir_modes, dir_modes_count);
1143 	check_fstat("check_fstat_file", file_path, file_modes,
1144 	    file_modes_count);
1145 
1146 	check_fstatfs("check_fstatfs_dir", dir_path, dir_modes,
1147 	    dir_modes_count);
1148 	check_fstatfs("check_fstatfs_file", file_path, file_modes,
1149 	    file_modes_count);
1150 
1151 	check_fsync("check_fsync_dir", dir_path, dir_modes, dir_modes_count);
1152 	check_fsync("check_fsync_file", file_path, file_modes,
1153 	    file_modes_count);
1154 
1155 	check_ftruncate("check_ftruncate_dir", dir_path, dir_modes,
1156 	    dir_modes_count);
1157 	check_ftruncate("check_ftruncate_file", file_path, file_modes,
1158 	    file_modes_count);
1159 
1160 	check_futimes("check_futimes_dir", dir_path, dir_modes,
1161 	    dir_modes_count);
1162 	check_futimes("check_futimes_file", file_path, file_modes,
1163 	    file_modes_count);
1164 
1165 	check_lseek("check_lseek_dir", dir_path, dir_modes, dir_modes_count);
1166 	check_lseek("check_lseek_file", file_path, file_modes,
1167 	    file_modes_count);
1168 
1169 	check_getdents("check_getdents_dir", dir_path, 1, dir_modes,
1170 	    dir_modes_count);
1171 	check_getdents("check_getdents_file", file_path, 0, file_modes,
1172 	    file_modes_count);
1173 
1174 	check_sendfile("check_sendfile_dir", dir_path, 1, dir_modes,
1175 	    dir_modes_count);
1176 	check_sendfile("check_sendfile_file", file_path, 0, file_modes,
1177 	    file_modes_count);
1178 
1179 	check_write("check_write_dir", write, dir_path, dir_modes,
1180 	    dir_modes_count);
1181 	check_write("check_write_file", write, file_path, file_modes,
1182 	    file_modes_count);
1183 
1184 	check_write("check_writev_dir", writev_wrapper, dir_path, dir_modes,
1185 	    dir_modes_count);
1186 	check_write("check_writev_file", writev_wrapper, file_path,
1187 	    file_modes, file_modes_count);
1188 
1189 	check_write("check_pwrite_dir", pwrite_wrapper, dir_path, dir_modes,
1190 	    dir_modes_count);
1191 	check_write("check_pwrite_file", pwrite_wrapper, file_path,
1192 	    file_modes, file_modes_count);
1193 
1194 	check_write("check_pwritev_dir", pwritev_wrapper, dir_path,
1195 	    dir_modes, dir_modes_count);
1196 	check_write("check_pwritev_file", pwritev_wrapper, file_path,
1197 	    file_modes, file_modes_count);
1198 
1199 	if (aio_present) {
1200 		check_write("check_aio_write_dir", aio_write_wrapper,
1201 		    dir_path, dir_modes, dir_modes_count);
1202 		check_write("check_aio_write_file", aio_write_wrapper,
1203 		    file_path, file_modes, file_modes_count);
1204 	}
1205 
1206 	check_read("check_read_dir", read, dir_path, dir_modes,
1207 	    dir_modes_count);
1208 	check_read("check_read_file", read, file_path, file_modes,
1209 	    file_modes_count);
1210 
1211 	check_read("check_readv_dir", readv_wrapper, dir_path, dir_modes,
1212 	    dir_modes_count);
1213 	check_read("check_readv_file", readv_wrapper, file_path,
1214 	    file_modes, file_modes_count);
1215 
1216 	check_read("check_pread_dir", pread_wrapper, dir_path, dir_modes,
1217 	    dir_modes_count);
1218 	check_read("check_pread_file", pread_wrapper, file_path,
1219 	    file_modes, file_modes_count);
1220 
1221 	check_read("check_preadv_dir", preadv_wrapper, dir_path,
1222 	    dir_modes, dir_modes_count);
1223 	check_read("check_preadv_file", preadv_wrapper, file_path,
1224 	    file_modes, file_modes_count);
1225 
1226 	if (aio_present) {
1227 		check_read("check_aio_read_dir", aio_read_wrapper, dir_path,
1228 		    dir_modes, dir_modes_count);
1229 		check_read("check_aio_read_file", aio_read_wrapper,
1230 		    file_path, file_modes, file_modes_count);
1231 	}
1232 
1233 	check_mmap_read("check_mmap_read_dir", dir_path, 1, dir_modes,
1234 	    dir_modes_count);
1235 	check_mmap_read("check_mmap_read_file", file_path, 0, file_modes,
1236 	    file_modes_count);
1237 
1238 	check_mmap_write("check_mmap_write_dir", dir_path, dir_modes,
1239 	    dir_modes_count);
1240 	check_mmap_write("check_mmap_write_file", file_path, file_modes,
1241 	    file_modes_count);
1242 
1243 	check_mmap_exec("check_mmap_exec_dir", dir_path, 1, dir_modes,
1244 	    dir_modes_count);
1245 	check_mmap_exec("check_mmap_exec_file", file_path, 0, file_modes,
1246 	    file_modes_count);
1247 
1248 	check_mmap_write_private("check_mmap_write_private_dir", dir_path, 1,
1249 	    dir_modes, dir_modes_count);
1250 	check_mmap_write_private("check_mmap_write_private_file", file_path,
1251 	    0, file_modes, file_modes_count);
1252 
1253 	(void)unlink(file_path);
1254 	(void)rmdir(dir_path);
1255 	exit(0);
1256 }
1257