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