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