summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorrwatson <rwatson@FreeBSD.org>2008-02-14 20:57:38 +0000
committerrwatson <rwatson@FreeBSD.org>2008-02-14 20:57:38 +0000
commite71de3c9ad013267735cf051f1a258c8d5d60068 (patch)
tree8bec59956dd05d2fe85b43840635e3df9039c6bb /tools
parent8249be02c2a839362eaeb918ae467c842c75b899 (diff)
downloadFreeBSD-src-e71de3c9ad013267735cf051f1a258c8d5d60068.zip
FreeBSD-src-e71de3c9ad013267735cf051f1a258c8d5d60068.tar.gz
Add open_to_operation, a security regression test that opens files with
various open flags and then tests various operations to make sure that they are properly constrained by open flags. Various I/O mechansms are tried, including aio if compiled into the kernel or loaded as a module. There's more to be done here but it's a useful start, running about 220 individual tests. This is in support of FreeBSD-SA-08:03.sendfile.
Diffstat (limited to 'tools')
-rw-r--r--tools/regression/security/open_to_operation/Makefile7
-rw-r--r--tools/regression/security/open_to_operation/open_to_operation.c1256
2 files changed, 1263 insertions, 0 deletions
diff --git a/tools/regression/security/open_to_operation/Makefile b/tools/regression/security/open_to_operation/Makefile
new file mode 100644
index 0000000..6ac2cc9
--- /dev/null
+++ b/tools/regression/security/open_to_operation/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= open_to_operation
+NO_MAN=
+#WARNS= 3
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/security/open_to_operation/open_to_operation.c b/tools/regression/security/open_to_operation/open_to_operation.c
new file mode 100644
index 0000000..358e118
--- /dev/null
+++ b/tools/regression/security/open_to_operation/open_to_operation.c
@@ -0,0 +1,1256 @@
+/*-
+ * Copyright (c) 2008 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*-
+ * This regression test attempts to confirm that the flags used at open-time
+ * for a file descriptor properly limit system calls that should be affected
+ * by those flags. Currently:
+ *
+ * System call Policy Tested
+ * __acl_aclcheck_fd(2) any no
+ * __acl_delete_fd(2) any no
+ * __acl_get_fd(2) any no
+ * __acl_set_fd(2) any no
+ * aio_fsync(2) any no
+ * aio_read(2) O_RDONLY or O_RDWR yes
+ * aio_write(2) O_WRONLY or O_RDWR yes
+ * dup(2) any yes
+ * dup2(2) any yes
+ * extattr_delete_fd(2) O_WRONLY or O_RDWR no
+ * extattr_get_fd(2) O_RDONLY or O_RDWR no
+ * extattr_list_fd(2) O_RDONLY or O_RDWR no
+ * extattr_set_fd(2) O_WRONLY or O_RDWR no
+ * fchdir(2) any directory yes
+ * fchflags(2) any yes
+ * fchmod(2) any yes
+ * fchown(2) any yes
+ * flock(2) any yes
+ * fpathconf(2) any yes
+ * fstat(2) any yes
+ * fstatfs(2) any yes
+ * fsync(2) any yes
+ * ftruncate(2) O_WRONLY or O_RDWR yes
+ * futimes(2) any yes
+ * getdents(2) O_RDONLY directory yes
+ * lseek(2) any yes
+ * mmap(2) PROT_READ O_RDONLY or O_RDWR yes
+ * mmap(2) PROT_WRITE O_WRONLY or O_RDWR yes
+ * mmap(2) PROT_WRITE + MAP_PRIV O_RDONLY or O_RDWR yes
+ * mmap(2) PROT_EXEC O_RDONLY or O_RDWR yes
+ * pread(2) O_RDONLY or O_RDWR yes
+ * preadv(2) O_RDONLY or O_RDWR yes
+ * pwrite(2) O_WRONLY or O_RDWR yes
+ * pwritev(2) O_WRONLY or O_RDWR yes
+ * read(2) O_RDONLY or O_RDWR yes
+ * readv(2) O_RDONLY or O_RDWR yes
+ * sendfile(2) O_RDONLY or O_RDWR on file yes
+ * write(2) O_WRONLY or O_RDWR yes
+ * writev(2) O_WRONLY or O_RDWR yes
+ *
+ * These checks do not verify that original permissions would allow the
+ * operation or that open is properly impacted by permissions, just that once
+ * a file descriptor is held, open-time limitations are implemented.
+ *
+ * We do, however, test that directories cannot be opened as writable.
+ *
+ * XXXRW: Arguably we should also test combinations of bits to mmap(2).
+ *
+ * XXXRW: Should verify mprotect() remapping limits.
+ *
+ * XXXRW: kqueue(2)/kevent(2), poll(2), select(2)
+ *
+ * XXXRW: oaio_read(2), oaio_write(2), freebsd6_*(2).
+ *
+ * XXXRW: __mac*(2)
+ *
+ * XXXRW: message queue and shared memory fds?
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+
+#include <aio.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define PERM_FILE 0644 /* Allow read, write. Someday exec? */
+#define PERM_DIR 0755 /* Allow read, write, exec. */
+
+/*
+ * Modes to try all tests with.
+ */
+static const int file_modes[] = { O_RDONLY, O_WRONLY, O_RDWR,
+ O_RDONLY | O_TRUNC, O_WRONLY | O_TRUNC, O_RDWR | O_TRUNC };
+static const int file_modes_count = sizeof(file_modes) / sizeof(int);
+
+static const int dir_modes[] = { O_RDONLY };
+static const int dir_modes_count = sizeof(dir_modes) / sizeof(int);
+
+static int testnum;
+static int aio_present;
+
+static void
+ok_mode(const char *testname, const char *comment, int mode)
+{
+
+ testnum++;
+ if (comment == NULL)
+ printf("ok %d - %s # mode 0x%x\n", testnum, testname, mode);
+ else
+ printf("ok %d - %s # mode 0x%x - %s\n", testnum, testname,
+ mode, comment);
+}
+
+static void
+notok_mode(const char *testname, const char *comment, int mode)
+{
+
+ testnum++;
+ if (comment == NULL)
+ printf("not ok %d - %s # mode 0x%x\n", testnum, testname,
+ mode);
+ else
+ printf("not ok %d - %s # mode 0x%x - %s\n", testnum, testname,
+ mode, comment);
+}
+
+/*
+ * Before we get started, confirm that we can't open directories writable.
+ */
+static void
+try_directory_open(const char *testname, const char *directory,
+ int mode, int expected_errno)
+{
+ int dfd;
+
+ dfd = open(directory, mode);
+ if (dfd >= 0) {
+ if (expected_errno)
+ notok_mode(testname, "opened", mode);
+ else
+ ok_mode(testname, NULL, mode);
+ close(dfd);
+ } else {
+ if (expected_errno && expected_errno == expected_errno)
+ ok_mode(testname, NULL, mode);
+ else if (expected_errno)
+ notok_mode(testname, "wrong errno", mode);
+ else
+ notok_mode(testname, "failed", mode);
+ }
+}
+
+static void
+check_directory_open_modes(const char *directory, const int *modes,
+ int modes_count)
+{
+ int expected_errno, i, mode;
+
+ /*
+ * Directories should only open with O_RDONLY. Notice that we use
+ * file_modes and not dirmodes.
+ */
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ if (mode == O_RDONLY)
+ expected_errno = 0;
+ else
+ expected_errno = EISDIR;
+ try_directory_open(__func__, directory, mode,
+ expected_errno);
+ }
+}
+
+static void
+check_dup(const char *testname, const char *path, const int *modes,
+ int modes_count)
+{
+ int dfd, fd, i, mode;
+
+ /*
+ * dup() should work regardless of open mode.
+ */
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ fd = open(path, mode);
+ if (fd < 0) {
+ notok_mode(testname, "open", mode);
+ continue;
+ }
+ dfd = dup(fd);
+ if (dfd >= 0) {
+ ok_mode(testname, NULL, mode);
+ close(dfd);
+ } else
+ notok_mode(testname, NULL, mode);
+ close(fd);
+ }
+}
+
+static void
+check_dup2(const char *testname, const char *path, const int *modes,
+ int modes_count)
+{
+ int dfd, fd, i, mode;
+
+ /*
+ * dup2() should work regardless of open mode.
+ */
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ fd = open(path, mode);
+ if (fd < 0) {
+ notok_mode(testname, "open", mode);
+ continue;
+ }
+ dfd = dup2(fd, 500); /* Arbitrary but high number. */
+ if (dfd >= 0) {
+ ok_mode(testname, NULL, mode);
+ close(dfd);
+ } else
+ notok_mode(testname, NULL, mode);
+ close(fd);
+ }
+}
+
+static void
+check_fchdir(const char *testname, const char *path, const int *modes,
+ int modes_count)
+{
+ int fd, i, mode;
+
+ /*
+ * fchdir() should work regardless of open mode.
+ */
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ fd = open(path, mode);
+ if (fd < 0) {
+ notok_mode(testname, "open", mode);
+ continue;
+ }
+ if (fchdir(fd) == 0)
+ ok_mode(testname, NULL, mode);
+ else
+ notok_mode(testname, "failed", mode);
+ close(fd);
+ }
+}
+
+static void
+check_fchflags(const char *testname, const char *path, const int *modes,
+ int modes_count)
+{
+ int fd, i, mode;
+
+ /*
+ * fchflags() should work regardless of open mode.
+ */
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ fd = open(path, mode);
+ if (fd < 0) {
+ notok_mode(testname, "open", mode);
+ continue;
+ }
+ if (fchflags(fd, UF_NODUMP) == 0)
+ ok_mode(testname, NULL, mode);
+ else
+ notok_mode(testname, "failed", mode);
+ close(fd);
+ }
+}
+
+static void
+check_fchmod(const char *testname, const char *path, int setmode,
+ const int *modes, int modes_count)
+{
+ int fd, i, mode;
+
+ /*
+ * fchmod() should work regardless of open mode.
+ */
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ fd = open(path, mode);
+ if (fd < 0) {
+ notok_mode(testname, "open", mode);
+ continue;
+ }
+ if (fchmod(fd, setmode) == 0)
+ ok_mode(testname, NULL, mode);
+ else
+ notok_mode(testname, "failed", mode);
+ close(fd);
+ }
+}
+
+static void
+check_fchown(const char *testname, const char *path, const int *modes,
+ int modes_count)
+{
+ int fd, i, mode;
+
+ /*
+ * fchown() should work regardless of open mode.
+ */
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ fd = open(path, mode);
+ if (fd < 0) {
+ notok_mode(testname, "open", mode);
+ continue;
+ }
+ if (fchown(fd, -1, -1) == 0)
+ ok_mode(testname, NULL, mode);
+ else
+ notok_mode(testname, "failed", mode);
+ close(fd);
+ }
+}
+
+static void
+check_flock(const char *testname, const char *path, const int *modes,
+ int modes_count)
+{
+ int fd, i, mode;
+
+ /*
+ * flock() should work regardless of open mode.
+ */
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ fd = open(path, mode);
+ if (fd < 0) {
+ notok_mode(testname, "open", mode);
+ continue;
+ }
+ if (flock(fd, LOCK_EX) == 0)
+ ok_mode(testname, NULL, mode);
+ else
+ notok_mode(testname, "failed", mode);
+ close(fd);
+ }
+}
+
+static void
+check_fpathconf(const char *testname, const char *path, const int *modes,
+ int modes_count)
+{
+ int fd, i, mode;
+ long l;
+
+ /*
+ * fpathconf() should work regardless of open mode.
+ */
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ fd = open(path, mode);
+ if (fd < 0) {
+ notok_mode(testname, "open", mode);
+ continue;
+ }
+ l = fpathconf(fd, _PC_FILESIZEBITS);
+ if (l >= 0)
+ ok_mode(testname, NULL, mode);
+ else
+ notok_mode(testname, "failed", mode);
+ close(fd);
+ }
+}
+
+static void
+check_fstat(const char *testname, const char *path, const int *modes,
+ int modes_count)
+{
+ struct stat sb;
+ int fd, i, mode;
+
+ /*
+ * fstat() should work regardless of open mode.
+ */
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ fd = open(path, mode);
+ if (fd < 0) {
+ notok_mode(testname, "open", mode);
+ continue;
+ }
+ if (fstat(fd, &sb) == 0)
+ ok_mode(testname, NULL, mode);
+ else
+ notok_mode(testname, "failed", mode);
+ close(fd);
+ }
+}
+
+static void
+check_fstatfs(const char *testname, const char *path, const int *modes,
+ int modes_count)
+{
+ struct statfs statfs;
+ int fd, i, mode;
+
+ /*
+ * fstatfs() should work regardless of open mode.
+ */
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ fd = open(path, mode);
+ if (fd < 0) {
+ notok_mode(testname, "open", mode);
+ continue;
+ }
+ if (fstatfs(fd, &statfs) == 0)
+ ok_mode(testname, NULL, mode);
+ else
+ notok_mode(testname, "failed", mode);
+ close(fd);
+ }
+}
+
+static void
+check_fsync(const char *testname, const char *path, const int *modes,
+ int modes_count)
+{
+ int fd, i, mode;
+
+ /*
+ * fstatfs() should work regardless of open mode.
+ */
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ fd = open(path, mode);
+ if (fd < 0) {
+ notok_mode(testname, "open", mode);
+ continue;
+ }
+ if (fsync(fd) == 0)
+ ok_mode(testname, NULL, mode);
+ else
+ notok_mode(testname, "failed", mode);
+ close(fd);
+ }
+}
+
+static void
+check_ftruncate(const char *testname, const char *path, const int *modes,
+ int modes_count)
+{
+ struct stat sb;
+ int fd, i, mode;
+
+ /*
+ * ftruncate() should work as long as long as (mode & O_ACCMODE) is
+ * O_RDWR or O_WRONLY.
+ *
+ * Directories should never be writable, so this test should always
+ * pass for directories...
+ */
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ fd = open(path, mode);
+ if (fd < 0) {
+ notok_mode(testname, "open", mode);
+ notok_mode(testname, "truncate1 skipped", mode);
+ notok_mode(testname, "truncate2 skipped", mode);
+ notok_mode(testname, "truncate3 skipped", mode);
+ continue;
+ }
+ if (fstat(fd, &sb) < 0) {
+ notok_mode(testname, "fstat", mode);
+ notok_mode(testname, "truncate1 skipped", mode);
+ notok_mode(testname, "truncate2 skipped", mode);
+ notok_mode(testname, "truncate3 skipped", mode);
+ close(fd);
+ continue;
+ }
+ ok_mode(testname, "setup", mode);
+
+ /* Truncate to grow file. */
+ if (ftruncate(fd, sb.st_size + 1) == 0) {
+ if (((mode & O_ACCMODE) == O_WRONLY) ||
+ ((mode & O_ACCMODE) == O_RDWR))
+ ok_mode(testname, "truncate1 succeeded",
+ mode);
+ else {
+ notok_mode(testname, "truncate1 succeeded",
+ mode);
+ notok_mode(testname, "truncate2 skipped",
+ mode);
+ notok_mode(testname, "truncate3 skipped",
+ mode);
+ close(fd);
+ continue;
+ }
+ } else {
+ if (((mode & O_ACCMODE) == O_WRONLY) ||
+ ((mode & O_ACCMODE) == O_RDWR)) {
+ notok_mode(testname, "truncate1 failed",
+ mode);
+ notok_mode(testname, "truncate2 skipped",
+ mode);
+ notok_mode(testname, "truncate3 skipped",
+ mode);
+ close(fd);
+ continue;
+ } else
+ ok_mode(testname, "truncate1 failed", mode);
+ }
+
+ /* Truncate to same size. */
+ if (ftruncate(fd, sb.st_size + 1) == 0) {
+ if (((mode & O_ACCMODE) == O_WRONLY) ||
+ ((mode & O_ACCMODE) == O_RDWR))
+ ok_mode(testname, "truncate2 succeeded",
+ mode);
+ else {
+ notok_mode(testname, "truncate2 succeeded",
+ mode);
+ notok_mode(testname, "truncate3 skipped",
+ mode);
+ close(fd);
+ continue;
+ }
+ } else {
+ if (((mode & O_ACCMODE) == O_WRONLY) ||
+ ((mode & O_ACCMODE) == O_RDWR)) {
+ notok_mode(testname, "truncate2 failed",
+ mode);
+ notok_mode(testname, "truncate3 skipped",
+ mode);
+ close(fd);
+ continue;
+ } else
+ ok_mode(testname, "truncate2 failed", mode);
+ }
+
+ /* Truncate to shrink. */
+ if (ftruncate(fd, sb.st_size) == 0) {
+ if (((mode & O_ACCMODE) == O_WRONLY) ||
+ ((mode & O_ACCMODE) == O_RDWR))
+ ok_mode(testname, "truncate3 succeeded",
+ mode);
+ else
+ notok_mode(testname, "truncate3 succeeded",
+ mode);
+ } else {
+ if (((mode & O_ACCMODE) == O_WRONLY) ||
+ ((mode & O_ACCMODE) == O_RDWR))
+ notok_mode(testname, "truncate3 failed",
+ mode);
+ else
+ ok_mode(testname, "truncate3 failed", mode);
+ }
+ close(fd);
+ }
+}
+
+static void
+check_futimes(const char *testname, const char *path, const int *modes,
+ int modes_count)
+{
+ int fd, i, mode;
+
+ /*
+ * futimes() should work regardless of open mode.
+ */
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ fd = open(path, mode);
+ if (fd < 0) {
+ notok_mode(testname, "open", mode);
+ continue;
+ }
+ if (futimes(fd, NULL) == 0)
+ ok_mode(testname, NULL, mode);
+ else
+ notok_mode(testname, "failed", mode);
+ close(fd);
+ }
+}
+
+static void
+check_lseek(const char *testname, const char *path, const int *modes,
+ int modes_count)
+{
+ int fd, i, mode;
+
+ /*
+ * lseek() should work regardless of open mode.
+ */
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ fd = open(path, mode);
+ if (fd < 0) {
+ notok_mode(testname, "open", mode);
+ continue;
+ }
+ if (lseek(fd, 100, SEEK_SET) == 100)
+ ok_mode(testname, NULL, mode);
+ else
+ notok_mode(testname, "failed", mode);
+ close(fd);
+ }
+}
+
+static void
+check_getdents(const char *testname, const char *path, int isdir,
+ const int *modes, int modes_count)
+{
+ int fd, i, mode;
+ char buf[8192];
+
+ /*
+ * getdents() should always work on directories and never on files,
+ * assuming directories are always opened for read (which they are).
+ */
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ fd = open(path, mode);
+ if (fd < 0) {
+ notok_mode(testname, "open", mode);
+ continue;
+ }
+ if (getdents(fd, buf, sizeof(buf)) >= 0) {
+ if (isdir && ((mode & O_ACCMODE) == O_RDONLY))
+ ok_mode(testname, "directory succeeded",
+ mode);
+ else if (isdir)
+ notok_mode(testname, "directory succeeded",
+ mode);
+ else
+ notok_mode(testname, "file succeeded", mode);
+ } else {
+ if (isdir && ((mode & O_ACCMODE) == O_RDONLY))
+ notok_mode(testname, "directory failed",
+ mode);
+ else if (isdir)
+ ok_mode(testname, "directory failed", mode);
+ else
+ ok_mode(testname, "file failed", mode);
+ }
+ close(fd);
+ }
+}
+
+static void
+check_sendfile(const char *testname, const char *path, int isdir,
+ const int *modes, int modes_count)
+{
+ int fd, i, mode, sv[2];
+ off_t sent;
+
+ /*
+ * sendfile() should work only on files, and only when the access mode
+ * is O_RDONLY or O_RDWR.
+ */
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ fd = open(path, mode);
+ if (fd < 0) {
+ notok_mode(testname, "open", mode);
+ continue;
+ }
+ if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sv) < 0) {
+ notok_mode(testname, "socketpair", mode);
+ continue;
+ }
+ if (sendfile(fd, sv[0], 0, 1, NULL, &sent, 0) == 0) {
+ if (isdir)
+ notok_mode(testname, "directory succeeded",
+ mode);
+ else if (((mode & O_ACCMODE) == O_RDONLY) ||
+ ((mode & O_ACCMODE) == O_RDWR))
+ ok_mode(testname, "succeeded", mode);
+ else
+ notok_mode(testname, "succeeded", mode);
+ } else {
+ if (isdir)
+ ok_mode(testname, "directory failed", mode);
+ else if (((mode & O_ACCMODE) == O_RDONLY) ||
+ ((mode & O_ACCMODE) == O_RDWR))
+ notok_mode(testname, "failed", mode);
+ else
+ ok_mode(testname, "failed", mode);
+ }
+ close(sv[0]);
+ close(sv[1]);
+ close(fd);
+ }
+}
+
+/*
+ * Various functions write, so just make write-like wrappers for them.
+ */
+typedef ssize_t (*write_fn)(int d, const void *buf, size_t nbytes);
+
+static ssize_t
+writev_wrapper(int d, const void *buf, size_t nbytes)
+{
+ struct iovec iov;
+
+ iov.iov_base = (void *)buf;
+ iov.iov_len = nbytes;
+ return (writev(d, &iov, 1));
+}
+
+static ssize_t
+pwrite_wrapper(int d, const void *buf, size_t nbytes)
+{
+
+ return (pwrite(d, buf, nbytes, 0));
+}
+
+static ssize_t
+pwritev_wrapper(int d, const void *buf, size_t nbytes)
+{
+ struct iovec iov;
+
+ iov.iov_base = (void *)buf;
+ iov.iov_len = nbytes;
+ return (pwritev(d, &iov, 1, 0));
+}
+
+static ssize_t
+aio_write_wrapper(int d, const void *buf, size_t nbytes)
+{
+ struct aiocb aiocb, *aiocb_array[1];
+
+ bzero(&aiocb, sizeof(aiocb));
+ aiocb.aio_fildes = d;
+ aiocb.aio_buf = (void *)buf;
+ aiocb.aio_nbytes = nbytes;
+ if (aio_write(&aiocb) < 0)
+ return (-1);
+ aiocb_array[0] = &aiocb;
+ if (aio_suspend(aiocb_array, 1, NULL) < 0)
+ return (-1);
+ return (aio_return(&aiocb));
+}
+
+static void
+check_write(const char *testname, write_fn fn, const char *path,
+ const int *modes, int modes_count)
+{
+ int fd, i, mode;
+ char ch;
+
+ /*
+ * write() should never succeed for directories, but especially
+ * because they can only be opened read-only. write() on files
+ * should succeed for O_WRONLY and O_RDWR descriptors.
+ */
+
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ fd = open(path, mode);
+ if (fd < 0) {
+ notok_mode(testname, "open", mode);
+ continue;
+ }
+ if (fn(fd, &ch, sizeof(ch)) < 0) {
+ if ((mode & O_ACCMODE) == O_WRONLY ||
+ (mode & O_ACCMODE) == O_RDWR)
+ notok_mode(testname, "write failed", mode);
+ else
+ ok_mode(testname, "write failed", mode);
+ } else {
+ if (!((mode & O_ACCMODE) == O_WRONLY ||
+ (mode & O_ACCMODE) == O_RDWR))
+ notok_mode(testname, "write suceeded", mode);
+ else
+ ok_mode(testname, "write succeeded", mode);
+ }
+ close(fd);
+ }
+}
+
+/*
+ * Various functions read, so just make read-like wrappers for them.
+ */
+typedef ssize_t (*read_fn)(int d, void *buf, size_t nbytes);
+
+static ssize_t
+readv_wrapper(int d, void *buf, size_t nbytes)
+{
+ struct iovec iov;
+
+ iov.iov_base = buf;
+ iov.iov_len = nbytes;
+ return (readv(d, &iov, 1));
+}
+
+static ssize_t
+pread_wrapper(int d, void *buf, size_t nbytes)
+{
+
+ return (pread(d, buf, nbytes, 0));
+}
+
+static ssize_t
+preadv_wrapper(int d, void *buf, size_t nbytes)
+{
+ struct iovec iov;
+
+ iov.iov_base = buf;
+ iov.iov_len = nbytes;
+ return (preadv(d, &iov, 1, 0));
+}
+
+static ssize_t
+aio_read_wrapper(int d, void *buf, size_t nbytes)
+{
+ struct aiocb aiocb, *aiocb_array[1];
+
+ bzero(&aiocb, sizeof(aiocb));
+ aiocb.aio_fildes = d;
+ aiocb.aio_buf = buf;
+ aiocb.aio_nbytes = nbytes;
+ if (aio_read(&aiocb) < 0)
+ return (-1);
+ aiocb_array[0] = &aiocb;
+ if (aio_suspend(aiocb_array, 1, NULL) < 0)
+ return (-1);
+ return (aio_return(&aiocb));
+}
+
+static void
+check_read(const char *testname, read_fn fn, const char *path,
+ const int *modes, int modes_count)
+{
+ int fd, i, mode;
+ char ch;
+
+ /*
+ * read() should (generally) succeeded on directories. read() on
+ * files should succeed for O_RDONLY and O_RDWR descriptors.
+ */
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ fd = open(path, mode);
+ if (fd < 0) {
+ notok_mode(testname, "open", mode);
+ continue;
+ }
+ if (fn(fd, &ch, sizeof(ch)) < 0) {
+ if ((mode & O_ACCMODE) == O_RDONLY ||
+ (mode & O_ACCMODE) == O_RDWR)
+ notok_mode(testname, "read failed", mode);
+ else
+ ok_mode(testname, "read failed", mode);
+ } else {
+ if (!((mode & O_ACCMODE) == O_RDONLY ||
+ (mode & O_ACCMODE) == O_RDWR))
+ notok_mode(testname, "read suceeded", mode);
+ else
+ ok_mode(testname, "read succeeded", mode);
+ }
+ close(fd);
+ }
+}
+
+static void
+check_mmap_read(const char *testname, const char *path, int isdir,
+ const int *modes, int modes_count)
+{
+ int fd, i, mode;
+ char *addr;
+
+ /*
+ * mmap() read should fail for directories (ideally?) but succeed for
+ * O_RDONLY and O_RDWR file descriptors.
+ */
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ fd = open(path, mode);
+ if (fd < 0) {
+ notok_mode(testname, "open", mode);
+ continue;
+ }
+ addr = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd,
+ 0);
+ if (addr == MAP_FAILED) {
+ if (isdir)
+ ok_mode(testname, "mmap dir failed", mode);
+ else if ((mode & O_ACCMODE) == O_RDONLY ||
+ (mode & O_ACCMODE) == O_RDWR)
+ notok_mode(testname, "mmap file failed",
+ mode);
+ else
+ ok_mode(testname, "mmap file failed", mode);
+ } else {
+ if (isdir)
+ notok_mode(testname, "mmap dir succeeded",
+ mode);
+ else if ((mode & O_ACCMODE) == O_RDONLY ||
+ (mode & O_ACCMODE) == O_RDWR)
+ ok_mode(testname, "mmap file succeeded",
+ mode);
+ else
+ notok_mode(testname, "mmap file succeeded",
+ mode);
+ (void)munmap(addr, getpagesize());
+ }
+ close(fd);
+ }
+}
+
+static void
+check_mmap_write(const char *testname, const char *path, const int *modes,
+ int modes_count)
+{
+ int fd, i, mode;
+ char *addr;
+
+ /*
+ * mmap() will always fail for directories (ideally) as they are
+ * always open O_RDONLY. Check for O_WRONLY or O_RDWR to permit a
+ * write mapping. This variant does a MAP_SHARED mapping, but we
+ * are also interested in MAP_PRIVATE.
+ */
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ fd = open(path, mode);
+ if (fd < 0) {
+ notok_mode(testname, "open", mode);
+ continue;
+ }
+ addr = mmap(NULL, getpagesize(), PROT_WRITE, MAP_SHARED, fd,
+ 0);
+ if (addr == MAP_FAILED) {
+ if ((mode & O_ACCMODE) == O_WRONLY ||
+ (mode & O_ACCMODE) == O_RDWR)
+ notok_mode(testname, "mmap failed",
+ mode);
+ else
+ ok_mode(testname, "mmap failed", mode);
+ } else {
+ if ((mode & O_ACCMODE) == O_WRONLY ||
+ (mode & O_ACCMODE) == O_RDWR)
+ ok_mode(testname, "mmap succeeded",
+ mode);
+ else
+ notok_mode(testname, "mmap succeeded", mode);
+ (void)munmap(addr, getpagesize());
+ }
+ close(fd);
+ }
+}
+
+static void
+check_mmap_exec(const char *testname, const char *path, int isdir,
+ const int *modes, int modes_count)
+{
+ int fd, i, mode;
+ char *addr;
+
+ /*
+ * mmap() exec should fail for directories (ideally?) but succeed for
+ * O_RDONLY and O_RDWR file descriptors.
+ */
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ fd = open(path, mode);
+ if (fd < 0) {
+ notok_mode(testname, "open", mode);
+ continue;
+ }
+ addr = mmap(NULL, getpagesize(), PROT_EXEC, MAP_SHARED, fd,
+ 0);
+ if (addr == MAP_FAILED) {
+ if (isdir)
+ ok_mode(testname, "mmap dir failed", mode);
+ else if ((mode & O_ACCMODE) == O_RDONLY ||
+ (mode & O_ACCMODE) == O_RDWR)
+ notok_mode(testname, "mmap file failed",
+ mode);
+ else
+ ok_mode(testname, "mmap file failed", mode);
+ } else {
+ if (isdir)
+ notok_mode(testname, "mmap dir succeeded",
+ mode);
+ else if ((mode & O_ACCMODE) == O_RDONLY ||
+ (mode & O_ACCMODE) == O_RDWR)
+ ok_mode(testname, "mmap file succeeded",
+ mode);
+ else
+ notok_mode(testname, "mmap file succeeded",
+ mode);
+ (void)munmap(addr, getpagesize());
+ }
+ close(fd);
+ }
+}
+
+static void
+check_mmap_write_private(const char *testname, const char *path, int isdir,
+ const int *modes, int modes_count)
+{
+ int fd, i, mode;
+ char *addr;
+
+ /*
+ * mmap() write private should succeed for readable descriptors
+ * except for directories.
+ */
+ for (i = 0; i < modes_count; i++) {
+ mode = modes[i];
+ fd = open(path, mode);
+ if (fd < 0) {
+ notok_mode(testname, "open", mode);
+ continue;
+ }
+ addr = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE,
+ MAP_PRIVATE, fd, 0);
+ if (addr == MAP_FAILED) {
+ if (isdir)
+ ok_mode(testname, "mmap dir failed", mode);
+ else if ((mode & O_ACCMODE) == O_RDONLY ||
+ (mode & O_ACCMODE) == O_RDWR)
+ notok_mode(testname, "mmap file failed",
+ mode);
+ else
+ ok_mode(testname, "mmap file failed", mode);
+ } else {
+ if (isdir)
+ notok_mode(testname, "mmap dir succeeded",
+ mode);
+ else if ((mode & O_ACCMODE) == O_RDONLY ||
+ (mode & O_ACCMODE) == O_RDWR)
+ ok_mode(testname, "mmap file succeeded",
+ mode);
+ else
+ notok_mode(testname, "mmap file succeeded",
+ mode);
+ (void)munmap(addr, getpagesize());
+ }
+ close(fd);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ char dir_path[PATH_MAX], file_path[PATH_MAX];
+ int dummy, fd;
+ size_t size;
+
+ aio_present = 0;
+ size = sizeof(dummy);
+ if (sysctlbyname("vfs.aio", &dummy, &size, NULL, 0) < 0) {
+ if (errno == EISDIR)
+ aio_present = 1;
+ }
+
+ strlcpy(dir_path, "/tmp/open-dir.XXXXXXXXXXX", sizeof(dir_path));
+ if (mkdtemp(dir_path) == NULL)
+ err(-1, "mkdtemp");
+ if (chmod(dir_path, PERM_DIR) < 0) {
+ warn("chmod %s", dir_path);
+ (void)rmdir(dir_path);
+ exit(-1);
+ }
+ strlcpy(file_path, "/tmp/open-file.XXXXXXXXXXX", sizeof(file_path));
+ fd = mkstemp(file_path);
+ if (fd < 0) {
+ warn("mkstemp");
+ (void)rmdir(dir_path);
+ exit(-1);
+ }
+ close(fd);
+ if (chmod(file_path, PERM_FILE) < 0) {
+ warn("chmod %s", file_path);
+ (void)unlink(file_path);
+ (void)rmdir(dir_path);
+ exit(-1);
+ }
+ check_directory_open_modes(dir_path, file_modes, file_modes_count);
+
+ check_dup("check_dup_dir", dir_path, dir_modes, dir_modes_count);
+ check_dup("check_dup_file", file_path, file_modes, file_modes_count);
+
+ check_dup2("check_dup2_dir", dir_path, dir_modes, dir_modes_count);
+ check_dup2("check_dup2_file", file_path, file_modes,
+ file_modes_count);
+
+ check_fchdir("check_fchdir", dir_path, dir_modes, dir_modes_count);
+
+ check_fchflags("check_fchflags_dir", dir_path, dir_modes,
+ dir_modes_count);
+ check_fchflags("check_fchflags_file", file_path, file_modes,
+ file_modes_count);
+
+ check_fchmod("check_fchmod_dir", dir_path, PERM_DIR, dir_modes,
+ dir_modes_count);
+ check_fchmod("check_fchmod_file", file_path, PERM_FILE, file_modes,
+ file_modes_count);
+
+ check_fchown("check_fchown_dir", dir_path, dir_modes,
+ dir_modes_count);
+ check_fchown("check_fchown_file", file_path, file_modes,
+ file_modes_count);
+
+ check_flock("check_flock_dir", dir_path, dir_modes, dir_modes_count);
+ check_flock("check_flock_file", file_path, file_modes,
+ file_modes_count);
+
+ check_fpathconf("check_fpathconf_dir", dir_path, dir_modes,
+ dir_modes_count);
+ check_fpathconf("check_fpathconf_file", file_path, file_modes,
+ file_modes_count);
+
+ check_fstat("check_fstat_dir", dir_path, dir_modes, dir_modes_count);
+ check_fstat("check_fstat_file", file_path, file_modes,
+ file_modes_count);
+
+ check_fstatfs("check_fstatfs_dir", dir_path, dir_modes,
+ dir_modes_count);
+ check_fstatfs("check_fstatfs_file", file_path, file_modes,
+ file_modes_count);
+
+ check_fsync("check_fsync_dir", dir_path, dir_modes, dir_modes_count);
+ check_fsync("check_fsync_file", file_path, file_modes,
+ file_modes_count);
+
+ check_ftruncate("check_ftruncate_dir", dir_path, dir_modes,
+ dir_modes_count);
+ check_ftruncate("check_ftruncate_file", file_path, file_modes,
+ file_modes_count);
+
+ check_futimes("check_futimes_dir", dir_path, dir_modes,
+ dir_modes_count);
+ check_futimes("check_futimes_file", file_path, file_modes,
+ file_modes_count);
+
+ check_lseek("check_lseek_dir", dir_path, dir_modes, dir_modes_count);
+ check_lseek("check_lseek_file", file_path, file_modes,
+ file_modes_count);
+
+ check_getdents("check_getdents_dir", dir_path, 1, dir_modes,
+ dir_modes_count);
+ check_getdents("check_getdents_file", file_path, 0, file_modes,
+ file_modes_count);
+
+ check_sendfile("check_sendfile_dir", dir_path, 1, dir_modes,
+ dir_modes_count);
+ check_sendfile("check_sendfile_file", file_path, 0, file_modes,
+ file_modes_count);
+
+ check_write("check_write_dir", write, dir_path, dir_modes,
+ dir_modes_count);
+ check_write("check_write_file", write, file_path, file_modes,
+ file_modes_count);
+
+ check_write("check_writev_dir", writev_wrapper, dir_path, dir_modes,
+ dir_modes_count);
+ check_write("check_writev_file", writev_wrapper, file_path,
+ file_modes, file_modes_count);
+
+ check_write("check_pwrite_dir", pwrite_wrapper, dir_path, dir_modes,
+ dir_modes_count);
+ check_write("check_pwrite_file", pwrite_wrapper, file_path,
+ file_modes, file_modes_count);
+
+ check_write("check_pwritev_dir", pwritev_wrapper, dir_path,
+ dir_modes, dir_modes_count);
+ check_write("check_pwritev_file", pwritev_wrapper, file_path,
+ file_modes, file_modes_count);
+
+ if (aio_present) {
+ check_write("check_aio_write_dir", aio_write_wrapper,
+ dir_path, dir_modes, dir_modes_count);
+ check_write("check_aio_write_file", aio_write_wrapper,
+ file_path, file_modes, file_modes_count);
+ }
+
+ check_read("check_read_dir", read, dir_path, dir_modes,
+ dir_modes_count);
+ check_read("check_read_file", read, file_path, file_modes,
+ file_modes_count);
+
+ check_read("check_readv_dir", readv_wrapper, dir_path, dir_modes,
+ dir_modes_count);
+ check_read("check_readv_file", readv_wrapper, file_path,
+ file_modes, file_modes_count);
+
+ check_read("check_pread_dir", pread_wrapper, dir_path, dir_modes,
+ dir_modes_count);
+ check_read("check_pread_file", pread_wrapper, file_path,
+ file_modes, file_modes_count);
+
+ check_read("check_preadv_dir", preadv_wrapper, dir_path,
+ dir_modes, dir_modes_count);
+ check_read("check_preadv_file", preadv_wrapper, file_path,
+ file_modes, file_modes_count);
+
+ if (aio_present) {
+ check_read("check_aio_read_dir", aio_read_wrapper, dir_path,
+ dir_modes, dir_modes_count);
+ check_read("check_aio_read_file", aio_read_wrapper,
+ file_path, file_modes, file_modes_count);
+ }
+
+ check_mmap_read("check_mmap_read_dir", dir_path, 1, dir_modes,
+ dir_modes_count);
+ check_mmap_read("check_mmap_read_file", file_path, 0, file_modes,
+ file_modes_count);
+
+ check_mmap_write("check_mmap_write_dir", dir_path, dir_modes,
+ dir_modes_count);
+ check_mmap_write("check_mmap_write_file", file_path, file_modes,
+ file_modes_count);
+
+ check_mmap_exec("check_mmap_exec_dir", dir_path, 1, dir_modes,
+ dir_modes_count);
+ check_mmap_exec("check_mmap_exec_file", file_path, 0, file_modes,
+ file_modes_count);
+
+ check_mmap_write_private("check_mmap_write_private_dir", dir_path, 1,
+ dir_modes, dir_modes_count);
+ check_mmap_write_private("check_mmap_write_private_file", file_path,
+ 0, file_modes, file_modes_count);
+
+ (void)unlink(file_path);
+ (void)rmdir(dir_path);
+ exit(0);
+}
OpenPOWER on IntegriCloud