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