Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F103208235
D29719.id87304.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
20 KB
Referenced Files
None
Subscribers
None
D29719.id87304.diff
View Options
Index: tests/sys/file/Makefile
===================================================================
--- tests/sys/file/Makefile
+++ tests/sys/file/Makefile
@@ -4,6 +4,7 @@
BINDIR= ${TESTSDIR}
+ATF_TESTS_C+= path_test
TAP_TESTS_C+= closefrom_test
TAP_TESTS_C+= dup_test
TAP_TESTS_C+= fcntlflags_test
Index: tests/sys/file/path_test.c
===================================================================
--- /dev/null
+++ tests/sys/file/path_test.c
@@ -0,0 +1,673 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 The FreeBSD Foundation
+ *
+ * This software was developed by Mark Johnston under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * 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.
+ */
+
+/*
+ * Basic regression tests for handling of O_PATH descriptors.
+ */
+
+#include <sys/param.h>
+#include <sys/event.h>
+#include <sys/ioctl.h>
+#include <sys/memrange.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+
+#include <aio.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#define FMT_ERR(s) s ": %s", strerror(errno)
+
+#define CHECKED_CLOSE(fd) \
+ ATF_REQUIRE_MSG(close(fd) == 0, FMT_ERR("close"))
+
+/* Create a temporary regular file containing some data. */
+static void
+mktfile(char path[PATH_MAX], const char *template)
+{
+ char buf[BUFSIZ];
+ int fd;
+
+ snprintf(path, PATH_MAX, "%s", template);
+ fd = mkstemp(path);
+ ATF_REQUIRE_MSG(fd >= 0, FMT_ERR("mkstemp"));
+ memset(buf, 0, sizeof(buf));
+ ATF_REQUIRE_MSG(write(fd, buf, sizeof(buf)) == sizeof(buf),
+ FMT_ERR("write"));
+ CHECKED_CLOSE(fd);
+}
+
+/* Make a temporary directory. */
+static void
+mktdir(char path[PATH_MAX], const char *template)
+{
+ snprintf(path, PATH_MAX, "%s", template);
+ ATF_REQUIRE_MSG(mkdtemp(path) == path, FMT_ERR("mkdtemp"));
+}
+
+ATF_TC_WITHOUT_HEAD(path_access);
+ATF_TC_BODY(path_access, tc)
+{
+ char path[PATH_MAX];
+ struct stat sb;
+ struct timespec ts[2];
+ struct timeval tv[2];
+ int pathfd;
+
+ mktfile(path, "path_access.XXXXXX");
+
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ ATF_REQUIRE_ERRNO(EBADF, fchmod(pathfd, 0666) == -1);
+ ATF_REQUIRE_ERRNO(EBADF, fchown(pathfd, getuid(), getgid()) == -1);
+ ATF_REQUIRE_ERRNO(EBADF, fchflags(pathfd, UF_NODUMP) == -1);
+ memset(tv, 0, sizeof(tv));
+ ATF_REQUIRE_ERRNO(EBADF, futimes(pathfd, tv) == -1);
+ memset(ts, 0, sizeof(ts));
+ ATF_REQUIRE_ERRNO(EBADF, futimens(pathfd, ts) == -1);
+
+ /* fpathconf(2) and fstat(2) are permitted. */
+ ATF_REQUIRE_MSG(fstat(pathfd, &sb) == 0, FMT_ERR("fstat"));
+ ATF_REQUIRE_MSG(fpathconf(pathfd, _PC_LINK_MAX) != -1,
+ FMT_ERR("fpathconf"));
+
+ CHECKED_CLOSE(pathfd);
+}
+
+/* Basic tests to verify that AIO operations fail. */
+ATF_TC_WITHOUT_HEAD(path_aio);
+ATF_TC_BODY(path_aio, tc)
+{
+ struct aiocb aio;
+ char buf[BUFSIZ], path[PATH_MAX];
+ int pathfd;
+
+ mktfile(path, "path_aio.XXXXXX");
+
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ memset(&aio, 0, sizeof(aio));
+ aio.aio_buf = buf;
+ aio.aio_nbytes = sizeof(buf);
+ aio.aio_fildes = pathfd;
+ aio.aio_offset = 0;
+
+ ATF_REQUIRE_ERRNO(EBADF, aio_read(&aio) == -1);
+ ATF_REQUIRE_ERRNO(EBADF, aio_write(&aio) == -1);
+ ATF_REQUIRE_ERRNO(EBADF, aio_fsync(O_SYNC, &aio) == -1);
+ ATF_REQUIRE_ERRNO(EBADF, aio_fsync(O_DSYNC, &aio) == -1);
+
+ CHECKED_CLOSE(pathfd);
+}
+
+/* Verify operations on directory path descriptors. */
+ATF_TC_WITHOUT_HEAD(path_directory);
+ATF_TC_BODY(path_directory, tc)
+{
+ struct dirent de;
+ struct stat sb;
+ char path[PATH_MAX];
+ int fd, pathfd;
+
+ mktdir(path, "path_directory.XXXXXX");
+
+ pathfd = open(path, O_PATH | O_DIRECTORY);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ /* Should not be possible to list directory entries. */
+ ATF_REQUIRE_ERRNO(EBADF,
+ getdirentries(pathfd, (char *)&de, sizeof(de), NULL) == -1);
+
+ /* It should be possible to create files under pathfd. */
+ fd = openat(pathfd, "test", O_RDWR | O_CREAT, 0600);
+ ATF_REQUIRE_MSG(fd >= 0, FMT_ERR("open"));
+ ATF_REQUIRE_MSG(fstatat(pathfd, "test", &sb, 0) == 0,
+ FMT_ERR("fstatat"));
+ CHECKED_CLOSE(fd);
+
+ /* ... but doing so requires write access. */
+ if (geteuid() != 0) {
+ ATF_REQUIRE_ERRNO(EBADF, fchmod(pathfd, 0500) == -1);
+ ATF_REQUIRE_MSG(chmod(path, 0500) == 0, FMT_ERR("chmod"));
+ ATF_REQUIRE_ERRNO(EACCES,
+ openat(pathfd, "test2", O_RDWR | O_CREAT, 0600) < 0);
+ }
+
+ /* fchdir(2) is permitted. */
+ ATF_REQUIRE_MSG(fchdir(pathfd) == 0, FMT_ERR("fchdir"));
+
+ CHECKED_CLOSE(pathfd);
+}
+
+/* Validate system calls that handle AT_EMPTY_PATH. */
+ATF_TC_WITHOUT_HEAD(path_empty);
+ATF_TC_BODY(path_empty, tc)
+{
+ char path[PATH_MAX];
+ struct timespec ts[2];
+ struct stat sb;
+ int pathfd;
+
+ mktfile(path, "path_empty.XXXXXX");
+
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ /* Various *at operations should work on path fds. */
+ ATF_REQUIRE_MSG(faccessat(pathfd, "", F_OK, AT_EMPTY_PATH) == 0,
+ FMT_ERR("faccessat"));
+ ATF_REQUIRE_MSG(chflagsat(pathfd, "", UF_NODUMP, AT_EMPTY_PATH) == 0,
+ FMT_ERR("chflagsat"));
+ ATF_REQUIRE_MSG(fchmodat(pathfd, "", 0600, AT_EMPTY_PATH) == 0,
+ FMT_ERR("fchmodat"));
+ ATF_REQUIRE_MSG(fchownat(pathfd, "", getuid(), getgid(),
+ AT_EMPTY_PATH) == 0, FMT_ERR("fchownat"));
+ ATF_REQUIRE_MSG(fstatat(pathfd, "", &sb, AT_EMPTY_PATH) == 0,
+ FMT_ERR("fstatat"));
+ ATF_REQUIRE_MSG(sb.st_size == BUFSIZ,
+ "unexpected size %ju", (uintmax_t)sb.st_size);
+ memset(ts, 0, sizeof(ts));
+ ATF_REQUIRE_MSG(utimensat(pathfd, "", ts, AT_EMPTY_PATH) == 0,
+ FMT_ERR("utimensat"));
+
+ CHECKED_CLOSE(pathfd);
+
+ /* Try to verify that we can't operate on privileged files. */
+ if (geteuid() != 0) {
+ pathfd = open("/dev/null", O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+ ATF_REQUIRE_ERRNO(EPERM,
+ fchownat(pathfd, "", getuid(), getgid(), AT_EMPTY_PATH) ==
+ -1);
+ ATF_REQUIRE_ERRNO(EPERM,
+ linkat(pathfd, "", AT_FDCWD, "test", AT_EMPTY_PATH) == -1);
+ CHECKED_CLOSE(pathfd);
+ }
+}
+
+/* Test linkat(2) with AT_EMPTY_PATH, which requires privileges. */
+ATF_TC_WITH_CLEANUP(path_empty_root);
+ATF_TC_HEAD(path_empty_root, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(path_empty_root, tc)
+{
+ char path[PATH_MAX];
+ struct stat sb, sb2;
+ int pathfd;
+
+ mktfile(path, "path_empty_root.XXXXXX");
+
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+ ATF_REQUIRE_MSG(fstatat(pathfd, "", &sb, AT_EMPTY_PATH) == 0,
+ FMT_ERR("fstatat"));
+
+ ATF_REQUIRE_MSG(linkat(pathfd, "", AT_FDCWD, "test", AT_EMPTY_PATH) ==
+ 0, FMT_ERR("linkat"));
+ ATF_REQUIRE_MSG(fstatat(AT_FDCWD, "test", &sb2, 0) == 0,
+ FMT_ERR("fstatat"));
+ ATF_REQUIRE_MSG(sb.st_dev == sb2.st_dev, "st_dev mismatch");
+ ATF_REQUIRE_MSG(sb.st_ino == sb2.st_ino, "st_ino mismatch");
+
+ CHECKED_CLOSE(pathfd);
+
+}
+ATF_TC_CLEANUP(path_empty_root, tc)
+{
+}
+
+/* poll(2) never returns an event for path fds, but kevent(2) does. */
+ATF_TC_WITHOUT_HEAD(path_event);
+ATF_TC_BODY(path_event, tc)
+{
+ char buf[BUFSIZ], path[PATH_MAX];
+ struct kevent ev;
+ struct pollfd pollfd;
+ int kq, pathfd;
+
+ mktfile(path, "path_event.XXXXXX");
+
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ /* poll(2) should return POLLNVAL. */
+ pollfd.fd = pathfd;
+ pollfd.events = POLLIN;
+ pollfd.revents = 0;
+ ATF_REQUIRE_MSG(poll(&pollfd, 1, 0) == 1, FMT_ERR("poll"));
+ ATF_REQUIRE_MSG(pollfd.revents == POLLNVAL, "unexpected revents %x",
+ pollfd.revents);
+ pollfd.events = POLLOUT;
+ pollfd.revents = 0;
+ ATF_REQUIRE_MSG(poll(&pollfd, 1, 0) == 1, FMT_ERR("poll"));
+ ATF_REQUIRE_MSG(pollfd.revents == POLLNVAL, "unexpected revents %x",
+ pollfd.revents);
+
+ /* Try to get a EVFILT_READ event through a path fd. */
+ kq = kqueue();
+ ATF_REQUIRE_MSG(kq >= 0, FMT_ERR("kqueue"));
+ EV_SET(&ev, pathfd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
+ ATF_REQUIRE_MSG(kevent(kq, &ev, 1, NULL, 0, NULL) == 0,
+ FMT_ERR("kevent"));
+ ATF_REQUIRE_MSG(kevent(kq, NULL, 0, &ev, 1, NULL) == 1,
+ FMT_ERR("kevent"));
+ ATF_REQUIRE_MSG((ev.flags & EV_ERROR) == 0, "EV_ERROR is set");
+ ATF_REQUIRE_MSG(ev.data == sizeof(buf),
+ "data is %jd", (intmax_t)ev.data);
+ EV_SET(&ev, pathfd, EVFILT_READ, EV_DELETE, 0, 0, 0);
+ ATF_REQUIRE_MSG(kevent(kq, &ev, 1, NULL, 0, NULL) == 0,
+ FMT_ERR("kevent"));
+
+ /* Try to get a EVFILT_VNODE/NOTE_LINK event through a path fd. */
+ EV_SET(&ev, pathfd, EVFILT_VNODE, EV_ADD | EV_ENABLE, NOTE_LINK, 0, 0);
+ ATF_REQUIRE_MSG(kevent(kq, &ev, 1, NULL, 0, NULL) == 0,
+ FMT_ERR("kevent"));
+ ATF_REQUIRE_MSG(funlinkat(AT_FDCWD, path, pathfd, 0) == 0,
+ FMT_ERR("funlinkat"));
+ ATF_REQUIRE_MSG(kevent(kq, NULL, 0, &ev, 1, NULL) == 1,
+ FMT_ERR("kevent"));
+ EV_SET(&ev, pathfd, EVFILT_VNODE, EV_DELETE, 0, 0, 0);
+ ATF_REQUIRE_MSG(kevent(kq, &ev, 1, NULL, 0, NULL) == 0,
+ FMT_ERR("kevent"));
+
+ CHECKED_CLOSE(kq);
+ CHECKED_CLOSE(pathfd);
+}
+
+/* Check various fcntl(2) operations on a path desriptor. */
+ATF_TC_WITHOUT_HEAD(path_fcntl);
+ATF_TC_BODY(path_fcntl, tc)
+{
+ char path[PATH_MAX];
+ int flags, pathfd, pathfd2;
+
+ mktfile(path, "path_fcntl.XXXXXX");
+
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ /* O_PATH should appear in the fd flags. */
+ flags = fcntl(pathfd, F_GETFL);
+ ATF_REQUIRE_MSG(flags != -1, FMT_ERR("fcntl"));
+ ATF_REQUIRE_MSG((flags & O_PATH) != 0, "O_PATH not set");
+
+ ATF_REQUIRE_ERRNO(EBADF,
+ fcntl(pathfd, F_SETFL, flags & ~O_PATH));
+ ATF_REQUIRE_ERRNO(EBADF,
+ fcntl(pathfd, F_SETFL, flags | O_APPEND));
+
+ /* A dup'ed O_PATH fd had better have O_PATH set too. */
+ pathfd2 = fcntl(pathfd, F_DUPFD, 0);
+ ATF_REQUIRE_MSG(pathfd2 >= 0, FMT_ERR("fcntl"));
+ flags = fcntl(pathfd2, F_GETFL);
+ ATF_REQUIRE_MSG(flags != -1, FMT_ERR("fcntl"));
+ ATF_REQUIRE_MSG((flags & O_PATH) != 0, "O_PATH not set");
+ CHECKED_CLOSE(pathfd2);
+
+ /* Double check with dup(2). */
+ pathfd2 = dup(pathfd);
+ ATF_REQUIRE_MSG(pathfd2 >= 0, FMT_ERR("dup"));
+ flags = fcntl(pathfd2, F_GETFL);
+ ATF_REQUIRE_MSG(flags != -1, FMT_ERR("fcntl"));
+ ATF_REQUIRE_MSG((flags & O_PATH) != 0, "O_PATH not set");
+ CHECKED_CLOSE(pathfd2);
+
+ /* It should be possible to set O_CLOEXEC. */
+ ATF_REQUIRE_MSG(fcntl(pathfd, F_SETFD, FD_CLOEXEC) == 0,
+ FMT_ERR("fcntl"));
+ ATF_REQUIRE_MSG(fcntl(pathfd, F_GETFD) == FD_CLOEXEC,
+ FMT_ERR("fcntl"));
+
+ CHECKED_CLOSE(pathfd);
+}
+
+/* Verify that we can execute a file opened with O_PATH. */
+ATF_TC_WITHOUT_HEAD(path_fexecve);
+ATF_TC_BODY(path_fexecve, tc)
+{
+ char path[PATH_MAX];
+ pid_t child;
+ int error, fd, pathfd, status;
+
+ child = fork();
+ ATF_REQUIRE_MSG(child != -1, FMT_ERR("fork"));
+ if (child == 0) {
+ pathfd = open("/usr/bin/true", O_PATH | O_EXEC);
+ if (pathfd < 0)
+ _exit(1);
+ fexecve(pathfd,
+ (char * const[]){__DECONST(char *, "/usr/bin/true"), NULL},
+ NULL);
+ _exit(2);
+ }
+
+ error = waitpid(child, &status, 0);
+ ATF_REQUIRE_MSG(error != -1, FMT_ERR("waitpid"));
+ ATF_REQUIRE_MSG(WIFEXITED(status), "child exited abnormally, status %d",
+ status);
+ ATF_REQUIRE_MSG(WEXITSTATUS(status) == 0, "child exit status is %d",
+ WEXITSTATUS(status));
+
+ /*
+ * Also verify that access permissions are checked when opening with
+ * O_PATH.
+ */
+ snprintf(path, sizeof(path), "path_fexecve.XXXXXX");
+ ATF_REQUIRE_MSG(mktemp(path) == path, FMT_ERR("mktemp"));
+
+ fd = open(path, O_CREAT | O_RDONLY, 0600);
+ ATF_REQUIRE_MSG(fd >= 0, FMT_ERR("open"));
+
+ pathfd = open(path, O_PATH | O_EXEC);
+ ATF_REQUIRE_ERRNO(EACCES, pathfd < 0);
+}
+
+/* Files may be unlinked using a path fd. */
+ATF_TC_WITHOUT_HEAD(path_funlinkat);
+ATF_TC_BODY(path_funlinkat, tc)
+{
+ char path[PATH_MAX];
+ struct stat sb;
+ int pathfd;
+
+ mktfile(path, "path_rights.XXXXXX");
+
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ ATF_REQUIRE_MSG(funlinkat(AT_FDCWD, path, pathfd, 0) == 0,
+ FMT_ERR("funlinkat"));
+ ATF_REQUIRE_ERRNO(ENOENT, stat(path, &sb) == -1);
+
+ CHECKED_CLOSE(pathfd);
+}
+
+/* Verify that various I/O operations fail on an O_PATH descriptor. */
+ATF_TC_WITHOUT_HEAD(path_io);
+ATF_TC_BODY(path_io, tc)
+{
+ char path[PATH_MAX], path2[PATH_MAX];
+ char buf[BUFSIZ];
+ struct iovec iov;
+ int error, fd, pathfd, sd[2];
+
+ /* It shouldn't be possible to create new files with O_PATH. */
+ snprintf(path, sizeof(path), "path_io.XXXXXX");
+ ATF_REQUIRE_MSG(mktemp(path) == path, FMT_ERR("mktemp"));
+ ATF_REQUIRE_ERRNO(ENOENT, open(path, O_PATH | O_CREAT, 0600) < 0);
+
+ /* Create a non-empty file for use in the rest of the tests. */
+ mktfile(path, "path_io.XXXXXX");
+
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ /* Make sure that basic I/O operations aren't possible. */
+ iov.iov_base = path;
+ iov.iov_len = strlen(path);
+ ATF_REQUIRE_ERRNO(EBADF,
+ write(pathfd, iov.iov_base, iov.iov_len) == -1);
+ ATF_REQUIRE_ERRNO(EBADF,
+ pwrite(pathfd, iov.iov_base, iov.iov_len, 0) == -1);
+ ATF_REQUIRE_ERRNO(EBADF,
+ writev(pathfd, &iov, 1) == -1);
+ ATF_REQUIRE_ERRNO(EBADF,
+ pwritev(pathfd, &iov, 1, 0) == -1);
+ ATF_REQUIRE_ERRNO(EBADF,
+ read(pathfd, path, 1) == -1);
+ ATF_REQUIRE_ERRNO(EBADF,
+ pread(pathfd, path, 1, 0) == -1);
+ ATF_REQUIRE_ERRNO(EBADF,
+ readv(pathfd, &iov, 1) == -1);
+ ATF_REQUIRE_ERRNO(EBADF,
+ preadv(pathfd, &iov, 1, 0) == -1);
+
+ /* copy_file_range() should not be permitted. */
+ mktfile(path2, "path_io.XXXXXX");
+ fd = open(path2, O_RDWR);
+ ATF_REQUIRE_ERRNO(EBADF,
+ copy_file_range(fd, NULL, pathfd, NULL, sizeof(buf), 0) == -1);
+ ATF_REQUIRE_ERRNO(EBADF,
+ copy_file_range(pathfd, NULL, fd, NULL, sizeof(buf), 0) == -1);
+ CHECKED_CLOSE(fd);
+
+ /* sendfile() should not be permitted. */
+ ATF_REQUIRE_MSG(socketpair(PF_LOCAL, SOCK_STREAM, 0, sd) == 0,
+ FMT_ERR("socketpair"));
+ ATF_REQUIRE_ERRNO(EBADF,
+ sendfile(pathfd, sd[0], 0, 0, NULL, NULL, 0));
+ CHECKED_CLOSE(sd[0]);
+ CHECKED_CLOSE(sd[1]);
+
+ /* No seeking. */
+ ATF_REQUIRE_ERRNO(ESPIPE,
+ lseek(pathfd, 0, SEEK_SET) == -1);
+
+ /* No operations on the file extent. */
+ ATF_REQUIRE_ERRNO(EINVAL,
+ ftruncate(pathfd, 0) == -1);
+ error = posix_fallocate(pathfd, 0, sizeof(buf) * 2);
+ ATF_REQUIRE_MSG(error == ESPIPE, "posix_fallocate() returned %d", error);
+ error = posix_fadvise(pathfd, 0, sizeof(buf), POSIX_FADV_NORMAL);
+ ATF_REQUIRE_MSG(error == ESPIPE, "posix_fadvise() returned %d", error);
+
+ /* mmap() is not allowed. */
+ ATF_REQUIRE_ERRNO(ENODEV,
+ mmap(NULL, PAGE_SIZE, PROT_READ, MAP_SHARED, pathfd, 0) ==
+ MAP_FAILED);
+ ATF_REQUIRE_ERRNO(ENODEV,
+ mmap(NULL, PAGE_SIZE, PROT_NONE, MAP_SHARED, pathfd, 0) ==
+ MAP_FAILED);
+ ATF_REQUIRE_ERRNO(ENODEV,
+ mmap(NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, pathfd, 0) ==
+ MAP_FAILED);
+
+ /* No fsync() or fdatasync(). */
+ ATF_REQUIRE_ERRNO(EBADF, fsync(pathfd) == -1);
+ ATF_REQUIRE_ERRNO(EBADF, fdatasync(pathfd) == -1);
+
+ CHECKED_CLOSE(pathfd);
+}
+
+/* ioctl(2) is not permitted on path fds. */
+ATF_TC_WITHOUT_HEAD(path_ioctl);
+ATF_TC_BODY(path_ioctl, tc)
+{
+ char path[PATH_MAX];
+ struct mem_extract me;
+ int pathfd, val;
+
+ mktfile(path, "path_ioctl.XXXXXX");
+
+ /* Standard file descriptor ioctls should fail. */
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ val = 0;
+ ATF_REQUIRE_ERRNO(EBADF, ioctl(pathfd, FIONBIO, &val) == -1);
+ ATF_REQUIRE_ERRNO(EBADF, ioctl(pathfd, FIONREAD, &val) == -1);
+ ATF_REQUIRE_ERRNO(EBADF, ioctl(pathfd, FIONWRITE, &val) == -1);
+ ATF_REQUIRE_ERRNO(EBADF, ioctl(pathfd, FIONSPACE, &val) == -1);
+
+ CHECKED_CLOSE(pathfd);
+
+ /* Device ioctls should fail. */
+ pathfd = open("/dev/mem", O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ me.me_vaddr = (uintptr_t)&me;
+ ATF_REQUIRE_ERRNO(EBADF, ioctl(pathfd, MEM_EXTRACT_PADDR, &me) == -1);
+
+ CHECKED_CLOSE(pathfd);
+}
+
+ATF_TC_WITHOUT_HEAD(path_lock);
+ATF_TC_BODY(path_lock, tc)
+{
+ char buf[BUFSIZ], path[PATH_MAX];
+ struct flock flk;
+ int fd, pathfd;
+
+ snprintf(path, sizeof(path), "path_rights.XXXXXX");
+ fd = mkostemp(path, O_SHLOCK);
+ ATF_REQUIRE_MSG(fd >= 0, FMT_ERR("mkostemp"));
+ memset(buf, 0, sizeof(buf));
+ ATF_REQUIRE_MSG(write(fd, buf, sizeof(buf)) == sizeof(buf),
+ FMT_ERR("write()"));
+
+ /* Verify that O_EXLOCK is ignored when combined with O_PATH. */
+ pathfd = open(path, O_PATH | O_EXLOCK);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ CHECKED_CLOSE(fd);
+
+ /* flock(2) is prohibited. */
+ ATF_REQUIRE_ERRNO(EOPNOTSUPP, flock(pathfd, LOCK_SH) == -1);
+ ATF_REQUIRE_ERRNO(EOPNOTSUPP, flock(pathfd, LOCK_EX) == -1);
+ ATF_REQUIRE_ERRNO(EOPNOTSUPP, flock(pathfd, LOCK_SH | LOCK_NB) == -1);
+ ATF_REQUIRE_ERRNO(EOPNOTSUPP, flock(pathfd, LOCK_EX | LOCK_NB) == -1);
+ ATF_REQUIRE_ERRNO(EOPNOTSUPP, flock(pathfd, LOCK_UN) == -1);
+
+ /* fcntl(2) file locks are prohibited. */
+ memset(&flk, 0, sizeof(flk));
+ flk.l_whence = SEEK_CUR;
+ ATF_REQUIRE_ERRNO(EBADF, fcntl(pathfd, F_GETLK, &flk) == -1);
+ flk.l_type = F_RDLCK;
+ ATF_REQUIRE_ERRNO(EBADF, fcntl(pathfd, F_SETLK, &flk) == -1);
+ ATF_REQUIRE_ERRNO(EBADF, fcntl(pathfd, F_SETLKW, &flk) == -1);
+ flk.l_type = F_WRLCK;
+ ATF_REQUIRE_ERRNO(EBADF, fcntl(pathfd, F_SETLK, &flk) == -1);
+ ATF_REQUIRE_ERRNO(EBADF, fcntl(pathfd, F_SETLKW, &flk) == -1);
+
+ CHECKED_CLOSE(pathfd);
+}
+
+/* Verify that we can send an O_PATH descriptor over a unix socket. */
+ATF_TC_WITHOUT_HEAD(path_rights);
+ATF_TC_BODY(path_rights, tc)
+{
+ char path[PATH_MAX];
+ struct cmsghdr *cmsg;
+ struct msghdr msg;
+ struct iovec iov;
+ int flags, pathfd, pathfd_copy, sd[2];
+ char c;
+
+ ATF_REQUIRE_MSG(socketpair(PF_LOCAL, SOCK_STREAM, 0, sd) == 0,
+ FMT_ERR("socketpair"));
+
+ mktfile(path, "path_rights.XXXXXX");
+
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ /* Package up the O_PATH and send it over the socket pair. */
+ cmsg = malloc(CMSG_SPACE(sizeof(pathfd)));
+ ATF_REQUIRE_MSG(cmsg != NULL, FMT_ERR("malloc"));
+
+ cmsg->cmsg_len = CMSG_LEN(sizeof(pathfd));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ *(int *)(void *)CMSG_DATA(cmsg) = pathfd;
+
+ c = 0;
+ iov.iov_base = &c;
+ iov.iov_len = 1;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsg;
+ msg.msg_controllen = CMSG_SPACE(sizeof(pathfd));
+
+ ATF_REQUIRE_MSG(sendmsg(sd[0], &msg, 0) == sizeof(c),
+ FMT_ERR("sendmsg"));
+
+ /* Grab the pathfd copy from the other end of the pair. */
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsg;
+ msg.msg_controllen = CMSG_SPACE(sizeof(pathfd));
+
+ ATF_REQUIRE_MSG(recvmsg(sd[1], &msg, 0) == 1,
+ FMT_ERR("recvmsg"));
+ pathfd_copy = *(int *)(void *)CMSG_DATA(cmsg);
+ ATF_REQUIRE_MSG(pathfd_copy != pathfd,
+ "pathfd and pathfd_copy are equal");
+
+ /* Verify that the copy has O_PATH properties. */
+ flags = fcntl(pathfd_copy, F_GETFL);
+ ATF_REQUIRE_MSG(flags != -1, FMT_ERR("fcntl"));
+ ATF_REQUIRE_MSG((flags & O_PATH) != 0, "O_PATH is not set");
+ ATF_REQUIRE_ERRNO(EBADF,
+ read(pathfd_copy, &c, 1) == -1);
+ ATF_REQUIRE_ERRNO(EBADF,
+ write(pathfd_copy, &c, 1) == -1);
+
+ CHECKED_CLOSE(pathfd);
+ CHECKED_CLOSE(pathfd_copy);
+ CHECKED_CLOSE(sd[0]);
+ CHECKED_CLOSE(sd[1]);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, path_access);
+ ATF_TP_ADD_TC(tp, path_aio);
+ ATF_TP_ADD_TC(tp, path_directory);
+ ATF_TP_ADD_TC(tp, path_empty);
+ ATF_TP_ADD_TC(tp, path_empty_root);
+ ATF_TP_ADD_TC(tp, path_event);
+ ATF_TP_ADD_TC(tp, path_fcntl);
+ ATF_TP_ADD_TC(tp, path_fexecve);
+ ATF_TP_ADD_TC(tp, path_funlinkat);
+ ATF_TP_ADD_TC(tp, path_io);
+ ATF_TP_ADD_TC(tp, path_ioctl);
+ ATF_TP_ADD_TC(tp, path_lock);
+ ATF_TP_ADD_TC(tp, path_rights);
+
+ return (atf_no_error());
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Nov 23, 6:22 AM (12 h, 48 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14774346
Default Alt Text
D29719.id87304.diff (20 KB)
Attached To
Mode
D29719: Simple regression tests for O_PATH/AT_EMPTY_PATH
Attached
Detach File
Event Timeline
Log In to Comment