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
ok_mode(const char * testname,const char * comment,int mode)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
notok_mode(const char * testname,const char * comment,int mode)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
try_directory_open(const char * testname,const char * directory,int mode,int expected_errno)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
check_directory_open_modes(const char * directory,const int * modes,int modes_count)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
check_dup(const char * testname,const char * path,const int * modes,int modes_count)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
check_dup2(const char * testname,const char * path,const int * modes,int modes_count)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
check_fchdir(const char * testname,const char * path,const int * modes,int modes_count)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
check_fchflags(const char * testname,const char * path,const int * modes,int modes_count)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
check_fchmod(const char * testname,const char * path,int setmode,const int * modes,int modes_count)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
check_fchown(const char * testname,const char * path,const int * modes,int modes_count)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
check_flock(const char * testname,const char * path,const int * modes,int modes_count)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
check_fpathconf(const char * testname,const char * path,const int * modes,int modes_count)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
check_fstat(const char * testname,const char * path,const int * modes,int modes_count)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
check_fstatfs(const char * testname,const char * path,const int * modes,int modes_count)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
check_fsync(const char * testname,const char * path,const int * modes,int modes_count)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
check_ftruncate(const char * testname,const char * path,const int * modes,int modes_count)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
check_futimes(const char * testname,const char * path,const int * modes,int modes_count)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
check_lseek(const char * testname,const char * path,const int * modes,int modes_count)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
check_getdents(const char * testname,const char * path,int isdir,const int * modes,int modes_count)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
check_sendfile(const char * testname,const char * path,int isdir,const int * modes,int modes_count)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
writev_wrapper(int d,const void * buf,size_t nbytes)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
pwrite_wrapper(int d,const void * buf,size_t nbytes)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
pwritev_wrapper(int d,const void * buf,size_t nbytes)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
aio_write_wrapper(int d,const void * buf,size_t nbytes)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
check_write(const char * testname,write_fn fn,const char * path,const int * modes,int modes_count)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
readv_wrapper(int d,void * buf,size_t nbytes)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
pread_wrapper(int d,void * buf,size_t nbytes)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
preadv_wrapper(int d,void * buf,size_t nbytes)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
aio_read_wrapper(int d,void * buf,size_t nbytes)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
check_read(const char * testname,read_fn fn,const char * path,const int * modes,int modes_count)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
check_mmap_read(const char * testname,const char * path,int isdir,const int * modes,int modes_count)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
check_mmap_write(const char * testname,const char * path,const int * modes,int modes_count)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
check_mmap_exec(const char * testname,const char * path,int isdir,const int * modes,int modes_count)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
check_mmap_write_private(const char * testname,const char * path,int isdir,const int * modes,int modes_count)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
main(void)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