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 = nitems(file_modes);
123
124 static const int dir_modes[] = { O_RDONLY };
125 static const int dir_modes_count = nitems(dir_modes);
126
127 static int testnum;
128 static int aio_present;
129
130 static void
ok_mode(const char * testname,const char * comment,int mode)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
notok_mode(const char * testname,const char * comment,int mode)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
try_directory_open(const char * testname,const char * directory,int mode,int expected_errno)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 == errno)
173 ok_mode(testname, NULL, mode);
174 else if (expected_errno != 0)
175 notok_mode(testname, "wrong errno", mode);
176 else
177 notok_mode(testname, "failed", mode);
178 }
179 }
180
181 static void
check_directory_open_modes(const char * directory,const int * modes,int modes_count)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
check_dup(const char * testname,const char * path,const int * modes,int modes_count)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
check_dup2(const char * testname,const char * path,const int * modes,int modes_count)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
check_fchdir(const char * testname,const char * path,const int * modes,int modes_count)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
check_fchflags(const char * testname,const char * path,const int * modes,int modes_count)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
check_fchmod(const char * testname,const char * path,int setmode,const int * modes,int modes_count)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
check_fchown(const char * testname,const char * path,const int * modes,int modes_count)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
check_flock(const char * testname,const char * path,const int * modes,int modes_count)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
check_fpathconf(const char * testname,const char * path,const int * modes,int modes_count)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
check_fstat(const char * testname,const char * path,const int * modes,int modes_count)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
check_fstatfs(const char * testname,const char * path,const int * modes,int modes_count)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
check_fsync(const char * testname,const char * path,const int * modes,int modes_count)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
check_ftruncate(const char * testname,const char * path,const int * modes,int modes_count)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
check_futimes(const char * testname,const char * path,const int * modes,int modes_count)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
check_lseek(const char * testname,const char * path,const int * modes,int modes_count)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
check_getdents(const char * testname,const char * path,int isdir,const int * modes,int modes_count)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
check_sendfile(const char * testname,const char * path,int isdir,const int * modes,int modes_count)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
writev_wrapper(int d,const void * buf,size_t nbytes)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
pwrite_wrapper(int d,const void * buf,size_t nbytes)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
pwritev_wrapper(int d,const void * buf,size_t nbytes)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
aio_write_wrapper(int d,const void * buf,size_t nbytes)754 aio_write_wrapper(int d, const void *buf, size_t nbytes)
755 {
756 struct aiocb aiocb;
757 struct aiocb const *aiocb_array[] = { &aiocb };
758
759 bzero(&aiocb, sizeof(aiocb));
760 aiocb.aio_fildes = d;
761 aiocb.aio_buf = (void *)buf;
762 aiocb.aio_nbytes = nbytes;
763 if (aio_write(&aiocb) < 0)
764 return (-1);
765 aiocb_array[0] = &aiocb;
766 if (aio_suspend(aiocb_array, 1, NULL) < 0)
767 return (-1);
768 return (aio_return(&aiocb));
769 }
770
771 static void
check_write(const char * testname,write_fn fn,const char * path,const int * modes,int modes_count)772 check_write(const char *testname, write_fn fn, const char *path,
773 const int *modes, int modes_count)
774 {
775 int fd, i, mode;
776 char ch;
777
778 /*
779 * write() should never succeed for directories, but especially
780 * because they can only be opened read-only. write() on files
781 * should succeed for O_WRONLY and O_RDWR descriptors.
782 */
783
784 for (i = 0; i < modes_count; i++) {
785 mode = modes[i];
786 fd = open(path, mode);
787 if (fd < 0) {
788 notok_mode(testname, "open", mode);
789 continue;
790 }
791 if (fn(fd, &ch, sizeof(ch)) < 0) {
792 if ((mode & O_ACCMODE) == O_WRONLY ||
793 (mode & O_ACCMODE) == O_RDWR)
794 notok_mode(testname, "write failed", mode);
795 else
796 ok_mode(testname, "write failed", mode);
797 } else {
798 if (!((mode & O_ACCMODE) == O_WRONLY ||
799 (mode & O_ACCMODE) == O_RDWR))
800 notok_mode(testname, "write succeeded", mode);
801 else
802 ok_mode(testname, "write succeeded", mode);
803 }
804 close(fd);
805 }
806 }
807
808 /*
809 * Various functions read, so just make read-like wrappers for them.
810 */
811 typedef ssize_t (*read_fn)(int d, void *buf, size_t nbytes);
812
813 static ssize_t
readv_wrapper(int d,void * buf,size_t nbytes)814 readv_wrapper(int d, void *buf, size_t nbytes)
815 {
816 struct iovec iov;
817
818 iov.iov_base = buf;
819 iov.iov_len = nbytes;
820 return (readv(d, &iov, 1));
821 }
822
823 static ssize_t
pread_wrapper(int d,void * buf,size_t nbytes)824 pread_wrapper(int d, void *buf, size_t nbytes)
825 {
826
827 return (pread(d, buf, nbytes, 0));
828 }
829
830 static ssize_t
preadv_wrapper(int d,void * buf,size_t nbytes)831 preadv_wrapper(int d, void *buf, size_t nbytes)
832 {
833 struct iovec iov;
834
835 iov.iov_base = buf;
836 iov.iov_len = nbytes;
837 return (preadv(d, &iov, 1, 0));
838 }
839
840 static ssize_t
aio_read_wrapper(int d,void * buf,size_t nbytes)841 aio_read_wrapper(int d, void *buf, size_t nbytes)
842 {
843 struct aiocb aiocb;
844 struct aiocb const *aiocb_array[] = { &aiocb };
845
846 bzero(&aiocb, sizeof(aiocb));
847 aiocb.aio_fildes = d;
848 aiocb.aio_buf = buf;
849 aiocb.aio_nbytes = nbytes;
850 if (aio_read(&aiocb) < 0)
851 return (-1);
852 if (aio_suspend(aiocb_array, 1, NULL) < 0)
853 return (-1);
854 return (aio_return(&aiocb));
855 }
856
857 static void
check_read(const char * testname,read_fn fn,const char * path,const int * modes,int modes_count)858 check_read(const char *testname, read_fn fn, const char *path,
859 const int *modes, int modes_count)
860 {
861 int fd, i, mode;
862 char ch;
863
864 /*
865 * read() should (generally) succeeded on directories. read() on
866 * files should succeed for O_RDONLY and O_RDWR descriptors.
867 */
868 for (i = 0; i < modes_count; i++) {
869 mode = modes[i];
870 fd = open(path, mode);
871 if (fd < 0) {
872 notok_mode(testname, "open", mode);
873 continue;
874 }
875 if (fn(fd, &ch, sizeof(ch)) < 0) {
876 if ((mode & O_ACCMODE) == O_RDONLY ||
877 (mode & O_ACCMODE) == O_RDWR)
878 notok_mode(testname, "read failed", mode);
879 else
880 ok_mode(testname, "read failed", mode);
881 } else {
882 if (!((mode & O_ACCMODE) == O_RDONLY ||
883 (mode & O_ACCMODE) == O_RDWR))
884 notok_mode(testname, "read succeeded", mode);
885 else
886 ok_mode(testname, "read succeeded", mode);
887 }
888 close(fd);
889 }
890 }
891
892 static void
check_mmap_read(const char * testname,const char * path,int isdir,const int * modes,int modes_count)893 check_mmap_read(const char *testname, const char *path, int isdir,
894 const int *modes, int modes_count)
895 {
896 int fd, i, mode;
897 char *addr;
898
899 /*
900 * mmap() read should fail for directories (ideally?) but succeed for
901 * O_RDONLY and O_RDWR file descriptors.
902 */
903 for (i = 0; i < modes_count; i++) {
904 mode = modes[i];
905 fd = open(path, mode);
906 if (fd < 0) {
907 notok_mode(testname, "open", mode);
908 continue;
909 }
910 addr = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd,
911 0);
912 if (addr == MAP_FAILED) {
913 if (isdir)
914 ok_mode(testname, "mmap dir failed", mode);
915 else if ((mode & O_ACCMODE) == O_RDONLY ||
916 (mode & O_ACCMODE) == O_RDWR)
917 notok_mode(testname, "mmap file failed",
918 mode);
919 else
920 ok_mode(testname, "mmap file failed", mode);
921 } else {
922 if (isdir)
923 notok_mode(testname, "mmap dir succeeded",
924 mode);
925 else if ((mode & O_ACCMODE) == O_RDONLY ||
926 (mode & O_ACCMODE) == O_RDWR)
927 ok_mode(testname, "mmap file succeeded",
928 mode);
929 else
930 notok_mode(testname, "mmap file succeeded",
931 mode);
932 (void)munmap(addr, getpagesize());
933 }
934 close(fd);
935 }
936 }
937
938 static void
check_mmap_write(const char * testname,const char * path,const int * modes,int modes_count)939 check_mmap_write(const char *testname, const char *path, const int *modes,
940 int modes_count)
941 {
942 int fd, i, mode;
943 char *addr;
944
945 /*
946 * mmap() will always fail for directories (ideally) as they are
947 * always open O_RDONLY. Check for O_WRONLY or O_RDWR to permit a
948 * write mapping. This variant does a MAP_SHARED mapping, but we
949 * are also interested in MAP_PRIVATE.
950 */
951 for (i = 0; i < modes_count; i++) {
952 mode = modes[i];
953 fd = open(path, mode);
954 if (fd < 0) {
955 notok_mode(testname, "open", mode);
956 continue;
957 }
958 addr = mmap(NULL, getpagesize(), PROT_WRITE, MAP_SHARED, fd,
959 0);
960 if (addr == MAP_FAILED) {
961 if ((mode & O_ACCMODE) == O_WRONLY ||
962 (mode & O_ACCMODE) == O_RDWR)
963 notok_mode(testname, "mmap failed",
964 mode);
965 else
966 ok_mode(testname, "mmap failed", mode);
967 } else {
968 if ((mode & O_ACCMODE) == O_WRONLY ||
969 (mode & O_ACCMODE) == O_RDWR)
970 ok_mode(testname, "mmap succeeded",
971 mode);
972 else
973 notok_mode(testname, "mmap succeeded", mode);
974 (void)munmap(addr, getpagesize());
975 }
976 close(fd);
977 }
978 }
979
980 static void
check_mmap_exec(const char * testname,const char * path,int isdir,const int * modes,int modes_count)981 check_mmap_exec(const char *testname, const char *path, int isdir,
982 const int *modes, int modes_count)
983 {
984 int fd, i, mode;
985 char *addr;
986
987 /*
988 * mmap() exec should fail for directories (ideally?) but succeed for
989 * O_RDONLY and O_RDWR file descriptors.
990 */
991 for (i = 0; i < modes_count; i++) {
992 mode = modes[i];
993 fd = open(path, mode);
994 if (fd < 0) {
995 notok_mode(testname, "open", mode);
996 continue;
997 }
998 addr = mmap(NULL, getpagesize(), PROT_EXEC, MAP_SHARED, fd,
999 0);
1000 if (addr == MAP_FAILED) {
1001 if (isdir)
1002 ok_mode(testname, "mmap dir failed", mode);
1003 else if ((mode & O_ACCMODE) == O_RDONLY ||
1004 (mode & O_ACCMODE) == O_RDWR)
1005 notok_mode(testname, "mmap file failed",
1006 mode);
1007 else
1008 ok_mode(testname, "mmap file failed", mode);
1009 } else {
1010 if (isdir)
1011 notok_mode(testname, "mmap dir succeeded",
1012 mode);
1013 else
1014 ok_mode(testname, "mmap file succeeded",
1015 mode);
1016 (void)munmap(addr, getpagesize());
1017 }
1018 close(fd);
1019 }
1020 }
1021
1022 static void
check_mmap_write_private(const char * testname,const char * path,int isdir,const int * modes,int modes_count)1023 check_mmap_write_private(const char *testname, const char *path, int isdir,
1024 const int *modes, int modes_count)
1025 {
1026 int fd, i, mode;
1027 char *addr;
1028
1029 /*
1030 * mmap() write private should succeed for readable descriptors
1031 * except for directories.
1032 */
1033 for (i = 0; i < modes_count; i++) {
1034 mode = modes[i];
1035 fd = open(path, mode);
1036 if (fd < 0) {
1037 notok_mode(testname, "open", mode);
1038 continue;
1039 }
1040 addr = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE,
1041 MAP_PRIVATE, fd, 0);
1042 if (addr == MAP_FAILED) {
1043 if (isdir)
1044 ok_mode(testname, "mmap dir failed", mode);
1045 else if ((mode & O_ACCMODE) == O_RDONLY ||
1046 (mode & O_ACCMODE) == O_RDWR)
1047 notok_mode(testname, "mmap file failed",
1048 mode);
1049 else
1050 ok_mode(testname, "mmap file failed", mode);
1051 } else {
1052 if (isdir)
1053 notok_mode(testname, "mmap dir succeeded",
1054 mode);
1055 else if ((mode & O_ACCMODE) == O_RDONLY ||
1056 (mode & O_ACCMODE) == O_RDWR)
1057 ok_mode(testname, "mmap file succeeded",
1058 mode);
1059 else
1060 notok_mode(testname, "mmap file succeeded",
1061 mode);
1062 (void)munmap(addr, getpagesize());
1063 }
1064 close(fd);
1065 }
1066 }
1067
1068 int
main(void)1069 main(void)
1070 {
1071 char dir_path[PATH_MAX], file_path[PATH_MAX];
1072 int dummy, fd;
1073 size_t size;
1074
1075 aio_present = 0;
1076 size = sizeof(dummy);
1077 if (sysctlbyname("vfs.aio", &dummy, &size, NULL, 0) < 0) {
1078 if (errno == EISDIR)
1079 aio_present = 1;
1080 }
1081
1082 strlcpy(dir_path, "/tmp/open-dir.XXXXXXXXXXX", sizeof(dir_path));
1083 if (mkdtemp(dir_path) == NULL)
1084 err(1, "mkdtemp");
1085 if (chmod(dir_path, PERM_DIR) < 0) {
1086 warn("chmod %s", dir_path);
1087 (void)rmdir(dir_path);
1088 exit(1);
1089 }
1090 strlcpy(file_path, "/tmp/open-file.XXXXXXXXXXX", sizeof(file_path));
1091 fd = mkstemp(file_path);
1092 if (fd < 0) {
1093 warn("mkstemp");
1094 (void)rmdir(dir_path);
1095 exit(1);
1096 }
1097 close(fd);
1098 if (chmod(file_path, PERM_FILE) < 0) {
1099 warn("chmod %s", file_path);
1100 (void)unlink(file_path);
1101 (void)rmdir(dir_path);
1102 exit(1);
1103 }
1104 check_directory_open_modes(dir_path, file_modes, file_modes_count);
1105
1106 check_dup("check_dup_dir", dir_path, dir_modes, dir_modes_count);
1107 check_dup("check_dup_file", file_path, file_modes, file_modes_count);
1108
1109 check_dup2("check_dup2_dir", dir_path, dir_modes, dir_modes_count);
1110 check_dup2("check_dup2_file", file_path, file_modes,
1111 file_modes_count);
1112
1113 check_fchdir("check_fchdir", dir_path, dir_modes, dir_modes_count);
1114
1115 check_fchflags("check_fchflags_dir", dir_path, dir_modes,
1116 dir_modes_count);
1117 check_fchflags("check_fchflags_file", file_path, file_modes,
1118 file_modes_count);
1119
1120 check_fchmod("check_fchmod_dir", dir_path, PERM_DIR, dir_modes,
1121 dir_modes_count);
1122 check_fchmod("check_fchmod_file", file_path, PERM_FILE, file_modes,
1123 file_modes_count);
1124
1125 check_fchown("check_fchown_dir", dir_path, dir_modes,
1126 dir_modes_count);
1127 check_fchown("check_fchown_file", file_path, file_modes,
1128 file_modes_count);
1129
1130 check_flock("check_flock_dir", dir_path, dir_modes, dir_modes_count);
1131 check_flock("check_flock_file", file_path, file_modes,
1132 file_modes_count);
1133
1134 check_fpathconf("check_fpathconf_dir", dir_path, dir_modes,
1135 dir_modes_count);
1136 check_fpathconf("check_fpathconf_file", file_path, file_modes,
1137 file_modes_count);
1138
1139 check_fstat("check_fstat_dir", dir_path, dir_modes, dir_modes_count);
1140 check_fstat("check_fstat_file", file_path, file_modes,
1141 file_modes_count);
1142
1143 check_fstatfs("check_fstatfs_dir", dir_path, dir_modes,
1144 dir_modes_count);
1145 check_fstatfs("check_fstatfs_file", file_path, file_modes,
1146 file_modes_count);
1147
1148 check_fsync("check_fsync_dir", dir_path, dir_modes, dir_modes_count);
1149 check_fsync("check_fsync_file", file_path, file_modes,
1150 file_modes_count);
1151
1152 check_ftruncate("check_ftruncate_dir", dir_path, dir_modes,
1153 dir_modes_count);
1154 check_ftruncate("check_ftruncate_file", file_path, file_modes,
1155 file_modes_count);
1156
1157 check_futimes("check_futimes_dir", dir_path, dir_modes,
1158 dir_modes_count);
1159 check_futimes("check_futimes_file", file_path, file_modes,
1160 file_modes_count);
1161
1162 check_lseek("check_lseek_dir", dir_path, dir_modes, dir_modes_count);
1163 check_lseek("check_lseek_file", file_path, file_modes,
1164 file_modes_count);
1165
1166 check_getdents("check_getdents_dir", dir_path, 1, dir_modes,
1167 dir_modes_count);
1168 check_getdents("check_getdents_file", file_path, 0, file_modes,
1169 file_modes_count);
1170
1171 check_sendfile("check_sendfile_dir", dir_path, 1, dir_modes,
1172 dir_modes_count);
1173 check_sendfile("check_sendfile_file", file_path, 0, file_modes,
1174 file_modes_count);
1175
1176 check_write("check_write_dir", write, dir_path, dir_modes,
1177 dir_modes_count);
1178 check_write("check_write_file", write, file_path, file_modes,
1179 file_modes_count);
1180
1181 check_write("check_writev_dir", writev_wrapper, dir_path, dir_modes,
1182 dir_modes_count);
1183 check_write("check_writev_file", writev_wrapper, file_path,
1184 file_modes, file_modes_count);
1185
1186 check_write("check_pwrite_dir", pwrite_wrapper, dir_path, dir_modes,
1187 dir_modes_count);
1188 check_write("check_pwrite_file", pwrite_wrapper, file_path,
1189 file_modes, file_modes_count);
1190
1191 check_write("check_pwritev_dir", pwritev_wrapper, dir_path,
1192 dir_modes, dir_modes_count);
1193 check_write("check_pwritev_file", pwritev_wrapper, file_path,
1194 file_modes, file_modes_count);
1195
1196 if (aio_present) {
1197 check_write("check_aio_write_dir", aio_write_wrapper,
1198 dir_path, dir_modes, dir_modes_count);
1199 check_write("check_aio_write_file", aio_write_wrapper,
1200 file_path, file_modes, file_modes_count);
1201 }
1202
1203 check_read("check_read_dir", read, dir_path, dir_modes,
1204 dir_modes_count);
1205 check_read("check_read_file", read, file_path, file_modes,
1206 file_modes_count);
1207
1208 check_read("check_readv_dir", readv_wrapper, dir_path, dir_modes,
1209 dir_modes_count);
1210 check_read("check_readv_file", readv_wrapper, file_path,
1211 file_modes, file_modes_count);
1212
1213 check_read("check_pread_dir", pread_wrapper, dir_path, dir_modes,
1214 dir_modes_count);
1215 check_read("check_pread_file", pread_wrapper, file_path,
1216 file_modes, file_modes_count);
1217
1218 check_read("check_preadv_dir", preadv_wrapper, dir_path,
1219 dir_modes, dir_modes_count);
1220 check_read("check_preadv_file", preadv_wrapper, file_path,
1221 file_modes, file_modes_count);
1222
1223 if (aio_present) {
1224 check_read("check_aio_read_dir", aio_read_wrapper, dir_path,
1225 dir_modes, dir_modes_count);
1226 check_read("check_aio_read_file", aio_read_wrapper,
1227 file_path, file_modes, file_modes_count);
1228 }
1229
1230 check_mmap_read("check_mmap_read_dir", dir_path, 1, dir_modes,
1231 dir_modes_count);
1232 check_mmap_read("check_mmap_read_file", file_path, 0, file_modes,
1233 file_modes_count);
1234
1235 check_mmap_write("check_mmap_write_dir", dir_path, dir_modes,
1236 dir_modes_count);
1237 check_mmap_write("check_mmap_write_file", file_path, file_modes,
1238 file_modes_count);
1239
1240 check_mmap_exec("check_mmap_exec_dir", dir_path, 1, dir_modes,
1241 dir_modes_count);
1242 check_mmap_exec("check_mmap_exec_file", file_path, 0, file_modes,
1243 file_modes_count);
1244
1245 check_mmap_write_private("check_mmap_write_private_dir", dir_path, 1,
1246 dir_modes, dir_modes_count);
1247 check_mmap_write_private("check_mmap_write_private_file", file_path,
1248 0, file_modes, file_modes_count);
1249
1250 (void)unlink(file_path);
1251 (void)rmdir(dir_path);
1252 exit(0);
1253 }
1254