13a248c84SMark Johnston /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
33a248c84SMark Johnston *
43a248c84SMark Johnston * Copyright (c) 2021 The FreeBSD Foundation
53a248c84SMark Johnston *
63a248c84SMark Johnston * This software was developed by Mark Johnston under sponsorship from
73a248c84SMark Johnston * the FreeBSD Foundation.
83a248c84SMark Johnston *
93a248c84SMark Johnston * Redistribution and use in source and binary forms, with or without
103a248c84SMark Johnston * modification, are permitted provided that the following conditions are
113a248c84SMark Johnston * met:
123a248c84SMark Johnston * 1. Redistributions of source code must retain the above copyright
133a248c84SMark Johnston * notice, this list of conditions and the following disclaimer.
143a248c84SMark Johnston * 2. Redistributions in binary form must reproduce the above copyright
153a248c84SMark Johnston * notice, this list of conditions and the following disclaimer in
163a248c84SMark Johnston * the documentation and/or other materials provided with the distribution.
173a248c84SMark Johnston *
183a248c84SMark Johnston * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
193a248c84SMark Johnston * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
203a248c84SMark Johnston * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
213a248c84SMark Johnston * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
223a248c84SMark Johnston * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
233a248c84SMark Johnston * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
243a248c84SMark Johnston * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
253a248c84SMark Johnston * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
263a248c84SMark Johnston * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
273a248c84SMark Johnston * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
283a248c84SMark Johnston * SUCH DAMAGE.
293a248c84SMark Johnston */
303a248c84SMark Johnston
313a248c84SMark Johnston /*
323a248c84SMark Johnston * Basic regression tests for handling of O_PATH descriptors.
333a248c84SMark Johnston */
343a248c84SMark Johnston
353a248c84SMark Johnston #include <sys/param.h>
363a248c84SMark Johnston #include <sys/capsicum.h>
373a248c84SMark Johnston #include <sys/event.h>
383a248c84SMark Johnston #include <sys/ioctl.h>
393a248c84SMark Johnston #include <sys/memrange.h>
403a248c84SMark Johnston #include <sys/mman.h>
41b59851e9SMark Johnston #include <sys/ptrace.h>
423a248c84SMark Johnston #include <sys/socket.h>
433a248c84SMark Johnston #include <sys/stat.h>
443a248c84SMark Johnston #include <sys/time.h>
453a248c84SMark Johnston #include <sys/uio.h>
46b59851e9SMark Johnston #include <sys/un.h>
473a248c84SMark Johnston #include <sys/wait.h>
483a248c84SMark Johnston
493a248c84SMark Johnston #include <aio.h>
503a248c84SMark Johnston #include <dirent.h>
513a248c84SMark Johnston #include <errno.h>
523a248c84SMark Johnston #include <fcntl.h>
533a248c84SMark Johnston #include <poll.h>
54b59851e9SMark Johnston #include <signal.h>
553a248c84SMark Johnston #include <stdio.h>
563a248c84SMark Johnston #include <stdlib.h>
573a248c84SMark Johnston #include <unistd.h>
583a248c84SMark Johnston
593a248c84SMark Johnston #include <atf-c.h>
603a248c84SMark Johnston
613a248c84SMark Johnston #define FMT_ERR(s) s ": %s", strerror(errno)
623a248c84SMark Johnston
633a248c84SMark Johnston #define CHECKED_CLOSE(fd) \
643a248c84SMark Johnston ATF_REQUIRE_MSG(close(fd) == 0, FMT_ERR("close"))
653a248c84SMark Johnston
663a248c84SMark Johnston /* Create a temporary regular file containing some data. */
673a248c84SMark Johnston static void
mktfile(char path[PATH_MAX],const char * template)683a248c84SMark Johnston mktfile(char path[PATH_MAX], const char *template)
693a248c84SMark Johnston {
703a248c84SMark Johnston char buf[BUFSIZ];
713a248c84SMark Johnston int fd;
723a248c84SMark Johnston
733a248c84SMark Johnston snprintf(path, PATH_MAX, "%s", template);
743a248c84SMark Johnston fd = mkstemp(path);
753a248c84SMark Johnston ATF_REQUIRE_MSG(fd >= 0, FMT_ERR("mkstemp"));
763a248c84SMark Johnston memset(buf, 0, sizeof(buf));
773a248c84SMark Johnston ATF_REQUIRE_MSG(write(fd, buf, sizeof(buf)) == sizeof(buf),
783a248c84SMark Johnston FMT_ERR("write"));
793a248c84SMark Johnston CHECKED_CLOSE(fd);
803a248c84SMark Johnston }
813a248c84SMark Johnston
823a248c84SMark Johnston /* Make a temporary directory. */
833a248c84SMark Johnston static void
mktdir(char path[PATH_MAX],const char * template)843a248c84SMark Johnston mktdir(char path[PATH_MAX], const char *template)
853a248c84SMark Johnston {
863a248c84SMark Johnston snprintf(path, PATH_MAX, "%s", template);
873a248c84SMark Johnston ATF_REQUIRE_MSG(mkdtemp(path) == path, FMT_ERR("mkdtemp"));
883a248c84SMark Johnston }
893a248c84SMark Johnston
903a248c84SMark Johnston /* Wait for a child process to exit with status 0. */
913a248c84SMark Johnston static void
waitchild(pid_t child,int exstatus)923a248c84SMark Johnston waitchild(pid_t child, int exstatus)
933a248c84SMark Johnston {
943a248c84SMark Johnston int error, status;
953a248c84SMark Johnston
963a248c84SMark Johnston error = waitpid(child, &status, 0);
973a248c84SMark Johnston ATF_REQUIRE_MSG(error != -1, FMT_ERR("waitpid"));
983a248c84SMark Johnston ATF_REQUIRE_MSG(WIFEXITED(status), "child exited abnormally, status %d",
993a248c84SMark Johnston status);
1003a248c84SMark Johnston ATF_REQUIRE_MSG(WEXITSTATUS(status) == exstatus,
1013a248c84SMark Johnston "child exit status is %d, expected %d",
1023a248c84SMark Johnston WEXITSTATUS(status), exstatus);
1033a248c84SMark Johnston }
1043a248c84SMark Johnston
1053a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_access);
ATF_TC_BODY(path_access,tc)1063a248c84SMark Johnston ATF_TC_BODY(path_access, tc)
1073a248c84SMark Johnston {
1083a248c84SMark Johnston char path[PATH_MAX];
1093a248c84SMark Johnston struct stat sb;
1103a248c84SMark Johnston struct timespec ts[2];
1113a248c84SMark Johnston struct timeval tv[2];
1123a248c84SMark Johnston int pathfd;
1133a248c84SMark Johnston
1143a248c84SMark Johnston mktfile(path, "path_access.XXXXXX");
1153a248c84SMark Johnston
1163a248c84SMark Johnston pathfd = open(path, O_PATH);
1173a248c84SMark Johnston ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
1183a248c84SMark Johnston
1193a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, fchmod(pathfd, 0666) == -1);
1203a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, fchown(pathfd, getuid(), getgid()) == -1);
1213a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, fchflags(pathfd, UF_NODUMP) == -1);
1223a248c84SMark Johnston memset(tv, 0, sizeof(tv));
1233a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, futimes(pathfd, tv) == -1);
1243a248c84SMark Johnston memset(ts, 0, sizeof(ts));
1253a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, futimens(pathfd, ts) == -1);
1263a248c84SMark Johnston
1273a248c84SMark Johnston /* fpathconf(2) and fstat(2) are permitted. */
1283a248c84SMark Johnston ATF_REQUIRE_MSG(fstat(pathfd, &sb) == 0, FMT_ERR("fstat"));
1293a248c84SMark Johnston ATF_REQUIRE_MSG(fpathconf(pathfd, _PC_LINK_MAX) != -1,
1303a248c84SMark Johnston FMT_ERR("fpathconf"));
1313a248c84SMark Johnston
1323a248c84SMark Johnston CHECKED_CLOSE(pathfd);
1333a248c84SMark Johnston }
1343a248c84SMark Johnston
1353a248c84SMark Johnston /* Basic tests to verify that AIO operations fail. */
1363a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_aio);
ATF_TC_BODY(path_aio,tc)1373a248c84SMark Johnston ATF_TC_BODY(path_aio, tc)
1383a248c84SMark Johnston {
1393a248c84SMark Johnston struct aiocb aio;
1403a248c84SMark Johnston char buf[BUFSIZ], path[PATH_MAX];
1413a248c84SMark Johnston int pathfd;
1423a248c84SMark Johnston
1433a248c84SMark Johnston mktfile(path, "path_aio.XXXXXX");
1443a248c84SMark Johnston
1453a248c84SMark Johnston pathfd = open(path, O_PATH);
1463a248c84SMark Johnston ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
1473a248c84SMark Johnston
1483a248c84SMark Johnston memset(&aio, 0, sizeof(aio));
1493a248c84SMark Johnston aio.aio_buf = buf;
1503a248c84SMark Johnston aio.aio_nbytes = sizeof(buf);
1513a248c84SMark Johnston aio.aio_fildes = pathfd;
1523a248c84SMark Johnston aio.aio_offset = 0;
1533a248c84SMark Johnston
1543a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, aio_read(&aio) == -1);
1553a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, aio_write(&aio) == -1);
1563a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, aio_fsync(O_SYNC, &aio) == -1);
1573a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, aio_fsync(O_DSYNC, &aio) == -1);
1583a248c84SMark Johnston
1593a248c84SMark Johnston CHECKED_CLOSE(pathfd);
1603a248c84SMark Johnston }
1613a248c84SMark Johnston
1623a248c84SMark Johnston /* Basic tests to verify that Capsicum restrictions apply to path fds. */
1633a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_capsicum);
ATF_TC_BODY(path_capsicum,tc)1643a248c84SMark Johnston ATF_TC_BODY(path_capsicum, tc)
1653a248c84SMark Johnston {
1663a248c84SMark Johnston char path[PATH_MAX];
1673a248c84SMark Johnston cap_rights_t rights;
1683a248c84SMark Johnston int truefd;
1693a248c84SMark Johnston pid_t child;
1703a248c84SMark Johnston
1713a248c84SMark Johnston mktfile(path, "path_capsicum.XXXXXX");
1723a248c84SMark Johnston
1733a248c84SMark Johnston /* Make sure that filesystem namespace restrictions apply to O_PATH. */
1743a248c84SMark Johnston child = fork();
1753a248c84SMark Johnston ATF_REQUIRE_MSG(child != -1, FMT_ERR("fork"));
1763a248c84SMark Johnston if (child == 0) {
1773a248c84SMark Johnston if (cap_enter() != 0)
1783a248c84SMark Johnston _exit(1);
1793a248c84SMark Johnston if (open(path, O_PATH) >= 0)
1803a248c84SMark Johnston _exit(2);
1813a248c84SMark Johnston if (errno != ECAPMODE)
1823a248c84SMark Johnston _exit(3);
1833a248c84SMark Johnston if (open("/usr/bin/true", O_PATH | O_EXEC) >= 0)
1843a248c84SMark Johnston _exit(4);
1853a248c84SMark Johnston if (errno != ECAPMODE)
1863a248c84SMark Johnston _exit(5);
1873a248c84SMark Johnston _exit(0);
1883a248c84SMark Johnston }
1893a248c84SMark Johnston waitchild(child, 0);
1903a248c84SMark Johnston
1913a248c84SMark Johnston /* Make sure that CAP_FEXECVE is required. */
1923a248c84SMark Johnston child = fork();
1933a248c84SMark Johnston ATF_REQUIRE_MSG(child != -1, FMT_ERR("fork"));
1943a248c84SMark Johnston if (child == 0) {
1953a248c84SMark Johnston truefd = open("/usr/bin/true", O_PATH | O_EXEC);
1963a248c84SMark Johnston if (truefd < 0)
1973a248c84SMark Johnston _exit(1);
1983a248c84SMark Johnston cap_rights_init(&rights);
1993a248c84SMark Johnston if (cap_rights_limit(truefd, &rights) != 0)
2003a248c84SMark Johnston _exit(2);
2013a248c84SMark Johnston (void)fexecve(truefd,
2023a248c84SMark Johnston (char * const[]){__DECONST(char *, "/usr/bin/true"), NULL},
2033a248c84SMark Johnston NULL);
2043a248c84SMark Johnston if (errno != ENOTCAPABLE)
2053a248c84SMark Johnston _exit(3);
2063a248c84SMark Johnston _exit(4);
2073a248c84SMark Johnston }
2083a248c84SMark Johnston waitchild(child, 4);
2093a248c84SMark Johnston }
2103a248c84SMark Johnston
211e5e1d9c7SMark Johnston /*
212e5e1d9c7SMark Johnston * Check that a pathfd can be converted to a regular fd using openat() in
213e5e1d9c7SMark Johnston * capability mode, but that rights on the pathfd are respected.
214e5e1d9c7SMark Johnston */
215e5e1d9c7SMark Johnston ATF_TC_WITHOUT_HEAD(path_capsicum_empty);
ATF_TC_BODY(path_capsicum_empty,tc)216e5e1d9c7SMark Johnston ATF_TC_BODY(path_capsicum_empty, tc)
217e5e1d9c7SMark Johnston {
218e5e1d9c7SMark Johnston char path[PATH_MAX];
219e5e1d9c7SMark Johnston cap_rights_t rights;
220e5e1d9c7SMark Johnston int dfd, fd, pathfd, pathdfd;
221e5e1d9c7SMark Johnston
222e5e1d9c7SMark Johnston mktfile(path, "path_capsicum.XXXXXX");
223e5e1d9c7SMark Johnston
224e5e1d9c7SMark Johnston pathdfd = open(".", O_PATH);
225e5e1d9c7SMark Johnston ATF_REQUIRE_MSG(pathdfd >= 0, FMT_ERR("open"));
226e5e1d9c7SMark Johnston pathfd = open(path, O_PATH);
227e5e1d9c7SMark Johnston ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
228e5e1d9c7SMark Johnston
229e5e1d9c7SMark Johnston ATF_REQUIRE(cap_enter() == 0);
230e5e1d9c7SMark Johnston
231e5e1d9c7SMark Johnston dfd = openat(pathdfd, "", O_DIRECTORY | O_EMPTY_PATH);
232e5e1d9c7SMark Johnston ATF_REQUIRE(dfd >= 0);
233e5e1d9c7SMark Johnston CHECKED_CLOSE(dfd);
234e5e1d9c7SMark Johnston
235e5e1d9c7SMark Johnston /*
236e5e1d9c7SMark Johnston * CAP_READ and CAP_LOOKUP should be sufficient to open a directory.
237e5e1d9c7SMark Johnston */
2388d1348f5SEd Maste cap_rights_init(&rights, CAP_READ, CAP_LOOKUP);
239e5e1d9c7SMark Johnston ATF_REQUIRE(cap_rights_limit(pathdfd, &rights) == 0);
240e5e1d9c7SMark Johnston dfd = openat(pathdfd, "", O_DIRECTORY | O_EMPTY_PATH);
241e5e1d9c7SMark Johnston ATF_REQUIRE(dfd >= 0);
242e5e1d9c7SMark Johnston CHECKED_CLOSE(dfd);
243e5e1d9c7SMark Johnston
244e5e1d9c7SMark Johnston /*
245e5e1d9c7SMark Johnston * ... CAP_READ on its own is not.
246e5e1d9c7SMark Johnston */
247e5e1d9c7SMark Johnston cap_rights_init(&rights, CAP_READ);
248e5e1d9c7SMark Johnston ATF_REQUIRE(cap_rights_limit(pathdfd, &rights) == 0);
249e5e1d9c7SMark Johnston dfd = openat(pathdfd, "", O_DIRECTORY | O_EMPTY_PATH);
250e5e1d9c7SMark Johnston ATF_REQUIRE_ERRNO(ENOTCAPABLE, dfd == -1);
251e5e1d9c7SMark Johnston
252e5e1d9c7SMark Johnston /*
253e5e1d9c7SMark Johnston * Now try with a regular file.
254e5e1d9c7SMark Johnston */
255e5e1d9c7SMark Johnston fd = openat(pathfd, "", O_RDWR | O_EMPTY_PATH);
256e5e1d9c7SMark Johnston ATF_REQUIRE(fd >= 0);
257e5e1d9c7SMark Johnston CHECKED_CLOSE(fd);
258e5e1d9c7SMark Johnston
2598d1348f5SEd Maste cap_rights_init(&rights, CAP_READ, CAP_LOOKUP, CAP_WRITE);
260e5e1d9c7SMark Johnston ATF_REQUIRE(cap_rights_limit(pathfd, &rights) == 0);
261e5e1d9c7SMark Johnston fd = openat(pathfd, "", O_RDWR | O_EMPTY_PATH | O_APPEND);
262e5e1d9c7SMark Johnston ATF_REQUIRE(fd >= 0);
263e5e1d9c7SMark Johnston CHECKED_CLOSE(fd);
264e5e1d9c7SMark Johnston
265e5e1d9c7SMark Johnston /*
266e5e1d9c7SMark Johnston * CAP_SEEK is needed to open a file for writing without O_APPEND.
267e5e1d9c7SMark Johnston */
2688d1348f5SEd Maste cap_rights_init(&rights, CAP_READ, CAP_LOOKUP, CAP_WRITE);
269e5e1d9c7SMark Johnston ATF_REQUIRE(cap_rights_limit(pathfd, &rights) == 0);
270e5e1d9c7SMark Johnston fd = openat(pathfd, "", O_RDWR | O_EMPTY_PATH);
271e5e1d9c7SMark Johnston ATF_REQUIRE_ERRNO(ENOTCAPABLE, fd == -1);
272e5e1d9c7SMark Johnston
273e5e1d9c7SMark Johnston /*
274e5e1d9c7SMark Johnston * CAP_LOOKUP isn't sufficient to open a file for reading.
275e5e1d9c7SMark Johnston */
276e5e1d9c7SMark Johnston cap_rights_init(&rights, CAP_LOOKUP);
277e5e1d9c7SMark Johnston ATF_REQUIRE(cap_rights_limit(pathfd, &rights) == 0);
278e5e1d9c7SMark Johnston fd = openat(pathfd, "", O_RDONLY | O_EMPTY_PATH);
279e5e1d9c7SMark Johnston ATF_REQUIRE_ERRNO(ENOTCAPABLE, fd == -1);
280e5e1d9c7SMark Johnston
281e5e1d9c7SMark Johnston CHECKED_CLOSE(pathfd);
282e5e1d9c7SMark Johnston CHECKED_CLOSE(pathdfd);
283e5e1d9c7SMark Johnston }
284e5e1d9c7SMark Johnston
285b59851e9SMark Johnston /* Make sure that ptrace(PT_COREDUMP) cannot be used to write to a path fd. */
286b59851e9SMark Johnston ATF_TC_WITHOUT_HEAD(path_coredump);
ATF_TC_BODY(path_coredump,tc)287b59851e9SMark Johnston ATF_TC_BODY(path_coredump, tc)
288b59851e9SMark Johnston {
289b59851e9SMark Johnston char path[PATH_MAX];
290b59851e9SMark Johnston struct ptrace_coredump pc;
291b59851e9SMark Johnston int error, pathfd, status;
292b59851e9SMark Johnston pid_t child;
293b59851e9SMark Johnston
294b59851e9SMark Johnston mktdir(path, "path_coredump.XXXXXX");
295b59851e9SMark Johnston
296b59851e9SMark Johnston child = fork();
297b59851e9SMark Johnston ATF_REQUIRE_MSG(child != -1, FMT_ERR("fork"));
298b59851e9SMark Johnston if (child == 0) {
299b59851e9SMark Johnston while (true)
300b59851e9SMark Johnston (void)sleep(1);
301b59851e9SMark Johnston }
302b59851e9SMark Johnston
303b59851e9SMark Johnston pathfd = open(path, O_PATH);
304b59851e9SMark Johnston ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
305b59851e9SMark Johnston
306b59851e9SMark Johnston error = ptrace(PT_ATTACH, child, 0, 0);
307b59851e9SMark Johnston ATF_REQUIRE_MSG(error == 0, FMT_ERR("ptrace"));
308b59851e9SMark Johnston error = waitpid(child, &status, 0);
309b59851e9SMark Johnston ATF_REQUIRE_MSG(error != -1, FMT_ERR("waitpid"));
310b59851e9SMark Johnston ATF_REQUIRE_MSG(WIFSTOPPED(status), "unexpected status %d", status);
311b59851e9SMark Johnston
312b59851e9SMark Johnston pc.pc_fd = pathfd;
313b59851e9SMark Johnston pc.pc_flags = 0;
314b59851e9SMark Johnston pc.pc_limit = 0;
315b59851e9SMark Johnston error = ptrace(PT_COREDUMP, child, (void *)&pc, sizeof(pc));
316b59851e9SMark Johnston ATF_REQUIRE_ERRNO(EBADF, error == -1);
317b59851e9SMark Johnston
318b59851e9SMark Johnston error = ptrace(PT_DETACH, child, 0, 0);
319b59851e9SMark Johnston ATF_REQUIRE_MSG(error == 0, FMT_ERR("ptrace"));
320b59851e9SMark Johnston
321b59851e9SMark Johnston ATF_REQUIRE_MSG(kill(child, SIGKILL) == 0, FMT_ERR("kill"));
322b59851e9SMark Johnston
323b59851e9SMark Johnston CHECKED_CLOSE(pathfd);
324b59851e9SMark Johnston }
325b59851e9SMark Johnston
3263a248c84SMark Johnston /* Verify operations on directory path descriptors. */
3273a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_directory);
ATF_TC_BODY(path_directory,tc)3283a248c84SMark Johnston ATF_TC_BODY(path_directory, tc)
3293a248c84SMark Johnston {
3303a248c84SMark Johnston struct dirent de;
3313a248c84SMark Johnston struct stat sb;
3323a248c84SMark Johnston char path[PATH_MAX];
3333a248c84SMark Johnston int fd, pathfd;
3343a248c84SMark Johnston
3353a248c84SMark Johnston mktdir(path, "path_directory.XXXXXX");
3363a248c84SMark Johnston
3373a248c84SMark Johnston pathfd = open(path, O_PATH | O_DIRECTORY);
3383a248c84SMark Johnston ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
3393a248c84SMark Johnston
3403a248c84SMark Johnston /* Should not be possible to list directory entries. */
3413a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF,
3423a248c84SMark Johnston getdirentries(pathfd, (char *)&de, sizeof(de), NULL) == -1);
3433a248c84SMark Johnston
3443a248c84SMark Johnston /* It should be possible to create files under pathfd. */
3453a248c84SMark Johnston fd = openat(pathfd, "test", O_RDWR | O_CREAT, 0600);
3463a248c84SMark Johnston ATF_REQUIRE_MSG(fd >= 0, FMT_ERR("open"));
3473a248c84SMark Johnston ATF_REQUIRE_MSG(fstatat(pathfd, "test", &sb, 0) == 0,
3483a248c84SMark Johnston FMT_ERR("fstatat"));
3493a248c84SMark Johnston CHECKED_CLOSE(fd);
3503a248c84SMark Johnston
3513a248c84SMark Johnston /* ... but doing so requires write access. */
3523a248c84SMark Johnston if (geteuid() != 0) {
3533a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, fchmod(pathfd, 0500) == -1);
3543a248c84SMark Johnston ATF_REQUIRE_MSG(chmod(path, 0500) == 0, FMT_ERR("chmod"));
3553a248c84SMark Johnston ATF_REQUIRE_ERRNO(EACCES,
3563a248c84SMark Johnston openat(pathfd, "test2", O_RDWR | O_CREAT, 0600) < 0);
3573a248c84SMark Johnston }
3583a248c84SMark Johnston
3593a248c84SMark Johnston /* fchdir(2) is permitted. */
3603a248c84SMark Johnston ATF_REQUIRE_MSG(fchdir(pathfd) == 0, FMT_ERR("fchdir"));
3613a248c84SMark Johnston
3623a248c84SMark Johnston CHECKED_CLOSE(pathfd);
3633a248c84SMark Johnston }
3643a248c84SMark Johnston
3653a248c84SMark Johnston /* Verify access permission checking for a directory path fd. */
3663a248c84SMark Johnston ATF_TC_WITH_CLEANUP(path_directory_not_root);
ATF_TC_HEAD(path_directory_not_root,tc)3673a248c84SMark Johnston ATF_TC_HEAD(path_directory_not_root, tc)
3683a248c84SMark Johnston {
3693a248c84SMark Johnston atf_tc_set_md_var(tc, "require.user", "unprivileged");
3703a248c84SMark Johnston }
ATF_TC_BODY(path_directory_not_root,tc)3713a248c84SMark Johnston ATF_TC_BODY(path_directory_not_root, tc)
3723a248c84SMark Johnston {
3733a248c84SMark Johnston char path[PATH_MAX];
3743a248c84SMark Johnston int pathfd;
3753a248c84SMark Johnston
3763a248c84SMark Johnston mktdir(path, "path_directory.XXXXXX");
3773a248c84SMark Johnston
3783a248c84SMark Johnston pathfd = open(path, O_PATH | O_DIRECTORY);
3793a248c84SMark Johnston ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
3803a248c84SMark Johnston
3813a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, fchmod(pathfd, 0500) == -1);
3823a248c84SMark Johnston ATF_REQUIRE_MSG(chmod(path, 0500) == 0, FMT_ERR("chmod"));
3833a248c84SMark Johnston ATF_REQUIRE_ERRNO(EACCES,
3843a248c84SMark Johnston openat(pathfd, "test2", O_RDWR | O_CREAT, 0600) < 0);
3853a248c84SMark Johnston
3863a248c84SMark Johnston CHECKED_CLOSE(pathfd);
3873a248c84SMark Johnston }
ATF_TC_CLEANUP(path_directory_not_root,tc)3883a248c84SMark Johnston ATF_TC_CLEANUP(path_directory_not_root, tc)
3893a248c84SMark Johnston {
3903a248c84SMark Johnston }
3913a248c84SMark Johnston
3923a248c84SMark Johnston /* Validate system calls that handle AT_EMPTY_PATH. */
3933a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_empty);
ATF_TC_BODY(path_empty,tc)3943a248c84SMark Johnston ATF_TC_BODY(path_empty, tc)
3953a248c84SMark Johnston {
3963a248c84SMark Johnston char path[PATH_MAX];
3973a248c84SMark Johnston struct timespec ts[2];
3983a248c84SMark Johnston struct stat sb;
3993a248c84SMark Johnston int pathfd;
4003a248c84SMark Johnston
4013a248c84SMark Johnston mktfile(path, "path_empty.XXXXXX");
4023a248c84SMark Johnston
4033a248c84SMark Johnston pathfd = open(path, O_PATH);
4043a248c84SMark Johnston ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
4053a248c84SMark Johnston
4063a248c84SMark Johnston /* Various *at operations should work on path fds. */
4073a248c84SMark Johnston ATF_REQUIRE_MSG(faccessat(pathfd, "", F_OK, AT_EMPTY_PATH) == 0,
4083a248c84SMark Johnston FMT_ERR("faccessat"));
4093a248c84SMark Johnston ATF_REQUIRE_MSG(chflagsat(pathfd, "", UF_NODUMP, AT_EMPTY_PATH) == 0,
4103a248c84SMark Johnston FMT_ERR("chflagsat"));
4113a248c84SMark Johnston ATF_REQUIRE_MSG(fchmodat(pathfd, "", 0600, AT_EMPTY_PATH) == 0,
4123a248c84SMark Johnston FMT_ERR("fchmodat"));
4133a248c84SMark Johnston ATF_REQUIRE_MSG(fchownat(pathfd, "", getuid(), getgid(),
4143a248c84SMark Johnston AT_EMPTY_PATH) == 0, FMT_ERR("fchownat"));
4153a248c84SMark Johnston ATF_REQUIRE_MSG(fstatat(pathfd, "", &sb, AT_EMPTY_PATH) == 0,
4163a248c84SMark Johnston FMT_ERR("fstatat"));
4173a248c84SMark Johnston ATF_REQUIRE_MSG(sb.st_size == BUFSIZ,
4183a248c84SMark Johnston "unexpected size %ju", (uintmax_t)sb.st_size);
4193a248c84SMark Johnston memset(ts, 0, sizeof(ts));
4203a248c84SMark Johnston ATF_REQUIRE_MSG(utimensat(pathfd, "", ts, AT_EMPTY_PATH) == 0,
4213a248c84SMark Johnston FMT_ERR("utimensat"));
4223a248c84SMark Johnston
4233a248c84SMark Johnston CHECKED_CLOSE(pathfd);
4243a248c84SMark Johnston }
4253a248c84SMark Johnston
4263a248c84SMark Johnston /* Verify that various operations on a path fd have access checks. */
4273a248c84SMark Johnston ATF_TC_WITH_CLEANUP(path_empty_not_root);
ATF_TC_HEAD(path_empty_not_root,tc)4283a248c84SMark Johnston ATF_TC_HEAD(path_empty_not_root, tc)
4293a248c84SMark Johnston {
4303a248c84SMark Johnston atf_tc_set_md_var(tc, "require.user", "unprivileged");
4313a248c84SMark Johnston }
ATF_TC_BODY(path_empty_not_root,tc)4323a248c84SMark Johnston ATF_TC_BODY(path_empty_not_root, tc)
4333a248c84SMark Johnston {
4343a248c84SMark Johnston int pathfd;
4353a248c84SMark Johnston
4363a248c84SMark Johnston pathfd = open("/dev/null", O_PATH);
4373a248c84SMark Johnston ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
4383a248c84SMark Johnston
4393a248c84SMark Johnston ATF_REQUIRE_ERRNO(EPERM,
4403a248c84SMark Johnston chflagsat(pathfd, "", UF_NODUMP, AT_EMPTY_PATH) == -1);
4413a248c84SMark Johnston ATF_REQUIRE_ERRNO(EPERM,
4423a248c84SMark Johnston fchownat(pathfd, "", getuid(), getgid(), AT_EMPTY_PATH) == -1);
4433a248c84SMark Johnston ATF_REQUIRE_ERRNO(EPERM,
4443a248c84SMark Johnston fchmodat(pathfd, "", 0600, AT_EMPTY_PATH) == -1);
4453a248c84SMark Johnston ATF_REQUIRE_ERRNO(EPERM,
4463a248c84SMark Johnston linkat(pathfd, "", AT_FDCWD, "test", AT_EMPTY_PATH) == -1);
4473a248c84SMark Johnston
4483a248c84SMark Johnston CHECKED_CLOSE(pathfd);
4493a248c84SMark Johnston }
ATF_TC_CLEANUP(path_empty_not_root,tc)4503a248c84SMark Johnston ATF_TC_CLEANUP(path_empty_not_root, tc)
4513a248c84SMark Johnston {
4523a248c84SMark Johnston }
4533a248c84SMark Johnston
4543a248c84SMark Johnston /* Test linkat(2) with AT_EMPTY_PATH, which requires privileges. */
4553a248c84SMark Johnston ATF_TC_WITH_CLEANUP(path_empty_root);
ATF_TC_HEAD(path_empty_root,tc)4563a248c84SMark Johnston ATF_TC_HEAD(path_empty_root, tc)
4573a248c84SMark Johnston {
4583a248c84SMark Johnston atf_tc_set_md_var(tc, "require.user", "root");
4593a248c84SMark Johnston }
ATF_TC_BODY(path_empty_root,tc)4603a248c84SMark Johnston ATF_TC_BODY(path_empty_root, tc)
4613a248c84SMark Johnston {
4623a248c84SMark Johnston char path[PATH_MAX];
4633a248c84SMark Johnston struct stat sb, sb2;
4643a248c84SMark Johnston int pathfd;
4653a248c84SMark Johnston
4663a248c84SMark Johnston mktfile(path, "path_empty_root.XXXXXX");
4673a248c84SMark Johnston
4683a248c84SMark Johnston pathfd = open(path, O_PATH);
4693a248c84SMark Johnston ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
4703a248c84SMark Johnston ATF_REQUIRE_MSG(fstatat(pathfd, "", &sb, AT_EMPTY_PATH) == 0,
4713a248c84SMark Johnston FMT_ERR("fstatat"));
4723a248c84SMark Johnston
4733a248c84SMark Johnston ATF_REQUIRE_MSG(linkat(pathfd, "", AT_FDCWD, "test", AT_EMPTY_PATH) ==
4743a248c84SMark Johnston 0, FMT_ERR("linkat"));
4753a248c84SMark Johnston ATF_REQUIRE_MSG(fstatat(AT_FDCWD, "test", &sb2, 0) == 0,
4763a248c84SMark Johnston FMT_ERR("fstatat"));
4773a248c84SMark Johnston ATF_REQUIRE_MSG(sb.st_dev == sb2.st_dev, "st_dev mismatch");
4783a248c84SMark Johnston ATF_REQUIRE_MSG(sb.st_ino == sb2.st_ino, "st_ino mismatch");
4793a248c84SMark Johnston
4803a248c84SMark Johnston CHECKED_CLOSE(pathfd);
4813a248c84SMark Johnston
4823a248c84SMark Johnston }
ATF_TC_CLEANUP(path_empty_root,tc)4833a248c84SMark Johnston ATF_TC_CLEANUP(path_empty_root, tc)
4843a248c84SMark Johnston {
4853a248c84SMark Johnston }
4863a248c84SMark Johnston
4873a248c84SMark Johnston /* poll(2) never returns an event for path fds, but kevent(2) does. */
4883a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_event);
ATF_TC_BODY(path_event,tc)4893a248c84SMark Johnston ATF_TC_BODY(path_event, tc)
4903a248c84SMark Johnston {
4913a248c84SMark Johnston char buf[BUFSIZ], path[PATH_MAX];
4923a248c84SMark Johnston struct kevent ev;
4933a248c84SMark Johnston struct pollfd pollfd;
4943a248c84SMark Johnston int kq, pathfd;
4953a248c84SMark Johnston
4963a248c84SMark Johnston mktfile(path, "path_event.XXXXXX");
4973a248c84SMark Johnston
4983a248c84SMark Johnston pathfd = open(path, O_PATH);
4993a248c84SMark Johnston ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
5003a248c84SMark Johnston
5013a248c84SMark Johnston /* poll(2) should return POLLNVAL. */
5023a248c84SMark Johnston pollfd.fd = pathfd;
5033a248c84SMark Johnston pollfd.events = POLLIN;
5043a248c84SMark Johnston pollfd.revents = 0;
5053a248c84SMark Johnston ATF_REQUIRE_MSG(poll(&pollfd, 1, 0) == 1, FMT_ERR("poll"));
5063a248c84SMark Johnston ATF_REQUIRE_MSG(pollfd.revents == POLLNVAL, "unexpected revents %x",
5073a248c84SMark Johnston pollfd.revents);
5083a248c84SMark Johnston pollfd.events = POLLOUT;
5093a248c84SMark Johnston pollfd.revents = 0;
5103a248c84SMark Johnston ATF_REQUIRE_MSG(poll(&pollfd, 1, 0) == 1, FMT_ERR("poll"));
5113a248c84SMark Johnston ATF_REQUIRE_MSG(pollfd.revents == POLLNVAL, "unexpected revents %x",
5123a248c84SMark Johnston pollfd.revents);
5133a248c84SMark Johnston
5143a248c84SMark Johnston /* Try to get a EVFILT_READ event through a path fd. */
5153a248c84SMark Johnston kq = kqueue();
5163a248c84SMark Johnston ATF_REQUIRE_MSG(kq >= 0, FMT_ERR("kqueue"));
5173a248c84SMark Johnston EV_SET(&ev, pathfd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
5183a248c84SMark Johnston ATF_REQUIRE_MSG(kevent(kq, &ev, 1, NULL, 0, NULL) == 0,
5193a248c84SMark Johnston FMT_ERR("kevent"));
5203a248c84SMark Johnston ATF_REQUIRE_MSG(kevent(kq, NULL, 0, &ev, 1, NULL) == 1,
5213a248c84SMark Johnston FMT_ERR("kevent"));
5223a248c84SMark Johnston ATF_REQUIRE_MSG((ev.flags & EV_ERROR) == 0, "EV_ERROR is set");
5233a248c84SMark Johnston ATF_REQUIRE_MSG(ev.data == sizeof(buf),
5243a248c84SMark Johnston "data is %jd", (intmax_t)ev.data);
5253a248c84SMark Johnston EV_SET(&ev, pathfd, EVFILT_READ, EV_DELETE, 0, 0, 0);
5263a248c84SMark Johnston ATF_REQUIRE_MSG(kevent(kq, &ev, 1, NULL, 0, NULL) == 0,
5273a248c84SMark Johnston FMT_ERR("kevent"));
5283a248c84SMark Johnston
529333f6684SMark Johnston /* Try to get a EVFILT_VNODE/NOTE_DELETE event through a path fd. */
530333f6684SMark Johnston EV_SET(&ev, pathfd, EVFILT_VNODE, EV_ADD | EV_ENABLE, NOTE_DELETE, 0,
531333f6684SMark Johnston 0);
5323a248c84SMark Johnston ATF_REQUIRE_MSG(kevent(kq, &ev, 1, NULL, 0, NULL) == 0,
5333a248c84SMark Johnston FMT_ERR("kevent"));
5343a248c84SMark Johnston ATF_REQUIRE_MSG(funlinkat(AT_FDCWD, path, pathfd, 0) == 0,
5353a248c84SMark Johnston FMT_ERR("funlinkat"));
5363a248c84SMark Johnston ATF_REQUIRE_MSG(kevent(kq, NULL, 0, &ev, 1, NULL) == 1,
5373a248c84SMark Johnston FMT_ERR("kevent"));
538333f6684SMark Johnston ATF_REQUIRE_MSG(ev.fflags == NOTE_DELETE,
539333f6684SMark Johnston "unexpected fflags %#x", ev.fflags);
5403a248c84SMark Johnston EV_SET(&ev, pathfd, EVFILT_VNODE, EV_DELETE, 0, 0, 0);
5413a248c84SMark Johnston ATF_REQUIRE_MSG(kevent(kq, &ev, 1, NULL, 0, NULL) == 0,
5423a248c84SMark Johnston FMT_ERR("kevent"));
5433a248c84SMark Johnston
5443a248c84SMark Johnston CHECKED_CLOSE(kq);
5453a248c84SMark Johnston CHECKED_CLOSE(pathfd);
5463a248c84SMark Johnston }
5473a248c84SMark Johnston
5483a248c84SMark Johnston /* Check various fcntl(2) operations on a path desriptor. */
5493a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_fcntl);
ATF_TC_BODY(path_fcntl,tc)5503a248c84SMark Johnston ATF_TC_BODY(path_fcntl, tc)
5513a248c84SMark Johnston {
5523a248c84SMark Johnston char path[PATH_MAX];
5533a248c84SMark Johnston int flags, pathfd, pathfd2;
5543a248c84SMark Johnston
5553a248c84SMark Johnston mktfile(path, "path_fcntl.XXXXXX");
5563a248c84SMark Johnston
5573a248c84SMark Johnston pathfd = open(path, O_PATH);
5583a248c84SMark Johnston ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
5593a248c84SMark Johnston
5603a248c84SMark Johnston /* O_PATH should appear in the fd flags. */
5613a248c84SMark Johnston flags = fcntl(pathfd, F_GETFL);
5623a248c84SMark Johnston ATF_REQUIRE_MSG(flags != -1, FMT_ERR("fcntl"));
5633a248c84SMark Johnston ATF_REQUIRE_MSG((flags & O_PATH) != 0, "O_PATH not set");
5643a248c84SMark Johnston
5653a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF,
5663a248c84SMark Johnston fcntl(pathfd, F_SETFL, flags & ~O_PATH));
5673a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF,
5683a248c84SMark Johnston fcntl(pathfd, F_SETFL, flags | O_APPEND));
5693a248c84SMark Johnston
5703a248c84SMark Johnston /* A dup'ed O_PATH fd had better have O_PATH set too. */
5713a248c84SMark Johnston pathfd2 = fcntl(pathfd, F_DUPFD, 0);
5723a248c84SMark Johnston ATF_REQUIRE_MSG(pathfd2 >= 0, FMT_ERR("fcntl"));
5733a248c84SMark Johnston flags = fcntl(pathfd2, F_GETFL);
5743a248c84SMark Johnston ATF_REQUIRE_MSG(flags != -1, FMT_ERR("fcntl"));
5753a248c84SMark Johnston ATF_REQUIRE_MSG((flags & O_PATH) != 0, "O_PATH not set");
5763a248c84SMark Johnston CHECKED_CLOSE(pathfd2);
5773a248c84SMark Johnston
5783a248c84SMark Johnston /* Double check with dup(2). */
5793a248c84SMark Johnston pathfd2 = dup(pathfd);
5803a248c84SMark Johnston ATF_REQUIRE_MSG(pathfd2 >= 0, FMT_ERR("dup"));
5813a248c84SMark Johnston flags = fcntl(pathfd2, F_GETFL);
5823a248c84SMark Johnston ATF_REQUIRE_MSG(flags != -1, FMT_ERR("fcntl"));
5833a248c84SMark Johnston ATF_REQUIRE_MSG((flags & O_PATH) != 0, "O_PATH not set");
5843a248c84SMark Johnston CHECKED_CLOSE(pathfd2);
5853a248c84SMark Johnston
5863a248c84SMark Johnston /* It should be possible to set O_CLOEXEC. */
5873a248c84SMark Johnston ATF_REQUIRE_MSG(fcntl(pathfd, F_SETFD, FD_CLOEXEC) == 0,
5883a248c84SMark Johnston FMT_ERR("fcntl"));
5893a248c84SMark Johnston ATF_REQUIRE_MSG(fcntl(pathfd, F_GETFD) == FD_CLOEXEC,
5903a248c84SMark Johnston FMT_ERR("fcntl"));
5913a248c84SMark Johnston
5923a248c84SMark Johnston CHECKED_CLOSE(pathfd);
5933a248c84SMark Johnston }
5943a248c84SMark Johnston
5953a248c84SMark Johnston /* Verify that we can execute a file opened with O_PATH. */
5963a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_fexecve);
ATF_TC_BODY(path_fexecve,tc)5973a248c84SMark Johnston ATF_TC_BODY(path_fexecve, tc)
5983a248c84SMark Johnston {
5993a248c84SMark Johnston char path[PATH_MAX];
6003a248c84SMark Johnston pid_t child;
6013a248c84SMark Johnston int fd, pathfd;
6023a248c84SMark Johnston
6033a248c84SMark Johnston child = fork();
6043a248c84SMark Johnston ATF_REQUIRE_MSG(child != -1, FMT_ERR("fork"));
6053a248c84SMark Johnston if (child == 0) {
6063a248c84SMark Johnston pathfd = open("/usr/bin/true", O_PATH | O_EXEC);
6073a248c84SMark Johnston if (pathfd < 0)
6083a248c84SMark Johnston _exit(1);
6093a248c84SMark Johnston fexecve(pathfd,
6103a248c84SMark Johnston (char * const[]){__DECONST(char *, "/usr/bin/true"), NULL},
6113a248c84SMark Johnston NULL);
6123a248c84SMark Johnston _exit(2);
6133a248c84SMark Johnston }
6143a248c84SMark Johnston waitchild(child, 0);
6153a248c84SMark Johnston
6163a248c84SMark Johnston /*
6173a248c84SMark Johnston * Also verify that access permissions are checked when opening with
6183a248c84SMark Johnston * O_PATH.
6193a248c84SMark Johnston */
6203a248c84SMark Johnston snprintf(path, sizeof(path), "path_fexecve.XXXXXX");
6213a248c84SMark Johnston ATF_REQUIRE_MSG(mktemp(path) == path, FMT_ERR("mktemp"));
6223a248c84SMark Johnston
6233a248c84SMark Johnston fd = open(path, O_CREAT | O_RDONLY, 0600);
6243a248c84SMark Johnston ATF_REQUIRE_MSG(fd >= 0, FMT_ERR("open"));
6253a248c84SMark Johnston
6263a248c84SMark Johnston pathfd = open(path, O_PATH | O_EXEC);
6273a248c84SMark Johnston ATF_REQUIRE_ERRNO(EACCES, pathfd < 0);
6283a248c84SMark Johnston }
6293a248c84SMark Johnston
630b59851e9SMark Johnston /* Make sure that O_PATH restrictions apply to named pipes as well. */
631b59851e9SMark Johnston ATF_TC_WITHOUT_HEAD(path_fifo);
ATF_TC_BODY(path_fifo,tc)632b59851e9SMark Johnston ATF_TC_BODY(path_fifo, tc)
633b59851e9SMark Johnston {
634b59851e9SMark Johnston char path[PATH_MAX], buf[BUFSIZ];
635b59851e9SMark Johnston struct kevent ev;
636b59851e9SMark Johnston int kq, pathfd;
637b59851e9SMark Johnston
638b59851e9SMark Johnston snprintf(path, sizeof(path), "path_fifo.XXXXXX");
639b59851e9SMark Johnston ATF_REQUIRE_MSG(mktemp(path) == path, FMT_ERR("mktemp"));
640b59851e9SMark Johnston
641b59851e9SMark Johnston ATF_REQUIRE_MSG(mkfifo(path, 0666) == 0, FMT_ERR("mkfifo"));
642b59851e9SMark Johnston
643b59851e9SMark Johnston pathfd = open(path, O_PATH);
644b59851e9SMark Johnston ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
645b59851e9SMark Johnston memset(buf, 0, sizeof(buf));
646b59851e9SMark Johnston ATF_REQUIRE_ERRNO(EBADF, write(pathfd, buf, sizeof(buf)));
647b59851e9SMark Johnston ATF_REQUIRE_ERRNO(EBADF, read(pathfd, buf, sizeof(buf)));
648b59851e9SMark Johnston
649b59851e9SMark Johnston kq = kqueue();
650b59851e9SMark Johnston ATF_REQUIRE_MSG(kq >= 0, FMT_ERR("kqueue"));
651b59851e9SMark Johnston EV_SET(&ev, pathfd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
652b59851e9SMark Johnston ATF_REQUIRE_ERRNO(EBADF, kevent(kq, &ev, 1, NULL, 0, NULL) == -1);
653b59851e9SMark Johnston
654b59851e9SMark Johnston CHECKED_CLOSE(pathfd);
655b59851e9SMark Johnston }
656b59851e9SMark Johnston
6573a248c84SMark Johnston /* Files may be unlinked using a path fd. */
6583a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_funlinkat);
ATF_TC_BODY(path_funlinkat,tc)6593a248c84SMark Johnston ATF_TC_BODY(path_funlinkat, tc)
6603a248c84SMark Johnston {
6613a248c84SMark Johnston char path[PATH_MAX];
6623a248c84SMark Johnston struct stat sb;
6633a248c84SMark Johnston int pathfd;
6643a248c84SMark Johnston
6653a248c84SMark Johnston mktfile(path, "path_rights.XXXXXX");
6663a248c84SMark Johnston
6673a248c84SMark Johnston pathfd = open(path, O_PATH);
6683a248c84SMark Johnston ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
6693a248c84SMark Johnston
6703a248c84SMark Johnston ATF_REQUIRE_MSG(funlinkat(AT_FDCWD, path, pathfd, 0) == 0,
6713a248c84SMark Johnston FMT_ERR("funlinkat"));
6723a248c84SMark Johnston ATF_REQUIRE_ERRNO(ENOENT, stat(path, &sb) == -1);
6733a248c84SMark Johnston
6743a248c84SMark Johnston CHECKED_CLOSE(pathfd);
6753a248c84SMark Johnston }
6763a248c84SMark Johnston
6773a248c84SMark Johnston /* Verify that various I/O operations fail on an O_PATH descriptor. */
6783a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_io);
ATF_TC_BODY(path_io,tc)6793a248c84SMark Johnston ATF_TC_BODY(path_io, tc)
6803a248c84SMark Johnston {
6813a248c84SMark Johnston char path[PATH_MAX], path2[PATH_MAX];
6823a248c84SMark Johnston char buf[BUFSIZ];
6833a248c84SMark Johnston struct iovec iov;
6848d40ee59SAndrew Turner size_t page_size;
6853a248c84SMark Johnston int error, fd, pathfd, sd[2];
6863a248c84SMark Johnston
687*749b3b2cSKonstantin Belousov /* It is allowed to create new files with O_PATH. */
6883a248c84SMark Johnston snprintf(path, sizeof(path), "path_io.XXXXXX");
6893a248c84SMark Johnston ATF_REQUIRE_MSG(mktemp(path) == path, FMT_ERR("mktemp"));
690*749b3b2cSKonstantin Belousov pathfd = open(path, O_PATH | O_CREAT, 0600);
691*749b3b2cSKonstantin Belousov ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open(O_PATH|O_CREAT)"));
692*749b3b2cSKonstantin Belousov /* Ensure that this is indeed O_PATH fd */
693*749b3b2cSKonstantin Belousov ATF_REQUIRE_ERRNO(EBADF, write(pathfd, path, strlen(path)) == -1);
694*749b3b2cSKonstantin Belousov CHECKED_CLOSE(pathfd);
6953a248c84SMark Johnston
6963a248c84SMark Johnston /* Create a non-empty file for use in the rest of the tests. */
6973a248c84SMark Johnston mktfile(path, "path_io.XXXXXX");
6983a248c84SMark Johnston
6993a248c84SMark Johnston pathfd = open(path, O_PATH);
7003a248c84SMark Johnston ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
7013a248c84SMark Johnston
7023a248c84SMark Johnston /* Make sure that basic I/O operations aren't possible. */
7033a248c84SMark Johnston iov.iov_base = path;
7043a248c84SMark Johnston iov.iov_len = strlen(path);
7053a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF,
7063a248c84SMark Johnston write(pathfd, iov.iov_base, iov.iov_len) == -1);
7073a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF,
7083a248c84SMark Johnston pwrite(pathfd, iov.iov_base, iov.iov_len, 0) == -1);
7093a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF,
7103a248c84SMark Johnston writev(pathfd, &iov, 1) == -1);
7113a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF,
7123a248c84SMark Johnston pwritev(pathfd, &iov, 1, 0) == -1);
7133a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF,
7143a248c84SMark Johnston read(pathfd, path, 1) == -1);
7153a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF,
7163a248c84SMark Johnston pread(pathfd, path, 1, 0) == -1);
7173a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF,
7183a248c84SMark Johnston readv(pathfd, &iov, 1) == -1);
7193a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF,
7203a248c84SMark Johnston preadv(pathfd, &iov, 1, 0) == -1);
7213a248c84SMark Johnston
7223a248c84SMark Johnston /* copy_file_range() should not be permitted. */
7233a248c84SMark Johnston mktfile(path2, "path_io.XXXXXX");
7243a248c84SMark Johnston fd = open(path2, O_RDWR);
7253a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF,
7263a248c84SMark Johnston copy_file_range(fd, NULL, pathfd, NULL, sizeof(buf), 0) == -1);
7273a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF,
7283a248c84SMark Johnston copy_file_range(pathfd, NULL, fd, NULL, sizeof(buf), 0) == -1);
7293a248c84SMark Johnston CHECKED_CLOSE(fd);
7303a248c84SMark Johnston
7313a248c84SMark Johnston /* sendfile() should not be permitted. */
7323a248c84SMark Johnston ATF_REQUIRE_MSG(socketpair(PF_LOCAL, SOCK_STREAM, 0, sd) == 0,
7333a248c84SMark Johnston FMT_ERR("socketpair"));
7343a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF,
7353a248c84SMark Johnston sendfile(pathfd, sd[0], 0, 0, NULL, NULL, 0));
7363a248c84SMark Johnston CHECKED_CLOSE(sd[0]);
7373a248c84SMark Johnston CHECKED_CLOSE(sd[1]);
7383a248c84SMark Johnston
7393a248c84SMark Johnston /* No seeking. */
7403a248c84SMark Johnston ATF_REQUIRE_ERRNO(ESPIPE,
7413a248c84SMark Johnston lseek(pathfd, 0, SEEK_SET) == -1);
7423a248c84SMark Johnston
7433a248c84SMark Johnston /* No operations on the file extent. */
7443a248c84SMark Johnston ATF_REQUIRE_ERRNO(EINVAL,
7453a248c84SMark Johnston ftruncate(pathfd, 0) == -1);
7463a248c84SMark Johnston error = posix_fallocate(pathfd, 0, sizeof(buf) * 2);
7473a248c84SMark Johnston ATF_REQUIRE_MSG(error == ESPIPE, "posix_fallocate() returned %d", error);
7483a248c84SMark Johnston error = posix_fadvise(pathfd, 0, sizeof(buf), POSIX_FADV_NORMAL);
7493a248c84SMark Johnston ATF_REQUIRE_MSG(error == ESPIPE, "posix_fadvise() returned %d", error);
7503a248c84SMark Johnston
7513a248c84SMark Johnston /* mmap() is not allowed. */
7528d40ee59SAndrew Turner page_size = getpagesize();
7533a248c84SMark Johnston ATF_REQUIRE_ERRNO(ENODEV,
7548d40ee59SAndrew Turner mmap(NULL, page_size, PROT_READ, MAP_SHARED, pathfd, 0) ==
7553a248c84SMark Johnston MAP_FAILED);
7563a248c84SMark Johnston ATF_REQUIRE_ERRNO(ENODEV,
7578d40ee59SAndrew Turner mmap(NULL, page_size, PROT_NONE, MAP_SHARED, pathfd, 0) ==
7583a248c84SMark Johnston MAP_FAILED);
7593a248c84SMark Johnston ATF_REQUIRE_ERRNO(ENODEV,
7608d40ee59SAndrew Turner mmap(NULL, page_size, PROT_READ, MAP_PRIVATE, pathfd, 0) ==
7613a248c84SMark Johnston MAP_FAILED);
7623a248c84SMark Johnston
7633a248c84SMark Johnston /* No fsync() or fdatasync(). */
7643a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, fsync(pathfd) == -1);
7653a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, fdatasync(pathfd) == -1);
7663a248c84SMark Johnston
7673a248c84SMark Johnston CHECKED_CLOSE(pathfd);
7683a248c84SMark Johnston }
7693a248c84SMark Johnston
7703a248c84SMark Johnston /* ioctl(2) is not permitted on path fds. */
7713a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_ioctl);
ATF_TC_BODY(path_ioctl,tc)7723a248c84SMark Johnston ATF_TC_BODY(path_ioctl, tc)
7733a248c84SMark Johnston {
7743a248c84SMark Johnston char path[PATH_MAX];
7753a248c84SMark Johnston struct mem_extract me;
7763a248c84SMark Johnston int pathfd, val;
7773a248c84SMark Johnston
7783a248c84SMark Johnston mktfile(path, "path_ioctl.XXXXXX");
7793a248c84SMark Johnston
7803a248c84SMark Johnston /* Standard file descriptor ioctls should fail. */
7813a248c84SMark Johnston pathfd = open(path, O_PATH);
7823a248c84SMark Johnston ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
7833a248c84SMark Johnston
7843a248c84SMark Johnston val = 0;
7853a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, ioctl(pathfd, FIONBIO, &val) == -1);
7863a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, ioctl(pathfd, FIONREAD, &val) == -1);
7873a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, ioctl(pathfd, FIONWRITE, &val) == -1);
7883a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, ioctl(pathfd, FIONSPACE, &val) == -1);
7893a248c84SMark Johnston
7903a248c84SMark Johnston CHECKED_CLOSE(pathfd);
7913a248c84SMark Johnston
7923a248c84SMark Johnston /* Device ioctls should fail. */
7933a248c84SMark Johnston pathfd = open("/dev/mem", O_PATH);
7943a248c84SMark Johnston ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
7953a248c84SMark Johnston
7963a248c84SMark Johnston me.me_vaddr = (uintptr_t)&me;
7973a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, ioctl(pathfd, MEM_EXTRACT_PADDR, &me) == -1);
7983a248c84SMark Johnston
7993a248c84SMark Johnston CHECKED_CLOSE(pathfd);
8003a248c84SMark Johnston }
8013a248c84SMark Johnston
8023a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_lock);
ATF_TC_BODY(path_lock,tc)8033a248c84SMark Johnston ATF_TC_BODY(path_lock, tc)
8043a248c84SMark Johnston {
8053a248c84SMark Johnston char buf[BUFSIZ], path[PATH_MAX];
8063a248c84SMark Johnston struct flock flk;
8073a248c84SMark Johnston int fd, pathfd;
8083a248c84SMark Johnston
8093a248c84SMark Johnston snprintf(path, sizeof(path), "path_rights.XXXXXX");
8103a248c84SMark Johnston fd = mkostemp(path, O_SHLOCK);
8113a248c84SMark Johnston ATF_REQUIRE_MSG(fd >= 0, FMT_ERR("mkostemp"));
8123a248c84SMark Johnston memset(buf, 0, sizeof(buf));
8133a248c84SMark Johnston ATF_REQUIRE_MSG(write(fd, buf, sizeof(buf)) == sizeof(buf),
8143a248c84SMark Johnston FMT_ERR("write()"));
8153a248c84SMark Johnston
8163a248c84SMark Johnston /* Verify that O_EXLOCK is ignored when combined with O_PATH. */
8173a248c84SMark Johnston pathfd = open(path, O_PATH | O_EXLOCK);
8183a248c84SMark Johnston ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
8193a248c84SMark Johnston
8203a248c84SMark Johnston CHECKED_CLOSE(fd);
8213a248c84SMark Johnston
8223a248c84SMark Johnston /* flock(2) is prohibited. */
8233a248c84SMark Johnston ATF_REQUIRE_ERRNO(EOPNOTSUPP, flock(pathfd, LOCK_SH) == -1);
8243a248c84SMark Johnston ATF_REQUIRE_ERRNO(EOPNOTSUPP, flock(pathfd, LOCK_EX) == -1);
8253a248c84SMark Johnston ATF_REQUIRE_ERRNO(EOPNOTSUPP, flock(pathfd, LOCK_SH | LOCK_NB) == -1);
8263a248c84SMark Johnston ATF_REQUIRE_ERRNO(EOPNOTSUPP, flock(pathfd, LOCK_EX | LOCK_NB) == -1);
8273a248c84SMark Johnston ATF_REQUIRE_ERRNO(EOPNOTSUPP, flock(pathfd, LOCK_UN) == -1);
8283a248c84SMark Johnston
8293a248c84SMark Johnston /* fcntl(2) file locks are prohibited. */
8303a248c84SMark Johnston memset(&flk, 0, sizeof(flk));
8313a248c84SMark Johnston flk.l_whence = SEEK_CUR;
8323a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, fcntl(pathfd, F_GETLK, &flk) == -1);
8333a248c84SMark Johnston flk.l_type = F_RDLCK;
8343a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, fcntl(pathfd, F_SETLK, &flk) == -1);
8353a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, fcntl(pathfd, F_SETLKW, &flk) == -1);
8363a248c84SMark Johnston flk.l_type = F_WRLCK;
8373a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, fcntl(pathfd, F_SETLK, &flk) == -1);
8383a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF, fcntl(pathfd, F_SETLKW, &flk) == -1);
8393a248c84SMark Johnston
8403a248c84SMark Johnston CHECKED_CLOSE(pathfd);
8413a248c84SMark Johnston }
8423a248c84SMark Johnston
843c4c66153SMark Johnston /*
844c4c66153SMark Johnston * Verify fstatat(AT_EMPTY_PATH) on non-regular dirfd.
845c4c66153SMark Johnston * Verify that fstatat(AT_EMPTY_PATH) on NULL path returns EFAULT.
846c4c66153SMark Johnston */
847c4c66153SMark Johnston ATF_TC_WITHOUT_HEAD(path_pipe_fstatat);
ATF_TC_BODY(path_pipe_fstatat,tc)848c4c66153SMark Johnston ATF_TC_BODY(path_pipe_fstatat, tc)
849c4c66153SMark Johnston {
850c4c66153SMark Johnston struct stat sb;
851c4c66153SMark Johnston int fd[2];
852c4c66153SMark Johnston
853c4c66153SMark Johnston ATF_REQUIRE_MSG(pipe(fd) == 0, FMT_ERR("pipe"));
854c4c66153SMark Johnston ATF_REQUIRE_MSG(fstatat(fd[0], "", &sb, AT_EMPTY_PATH) == 0,
855c4c66153SMark Johnston FMT_ERR("fstatat pipe"));
856c4c66153SMark Johnston ATF_REQUIRE_ERRNO(EFAULT, fstatat(fd[0], NULL, &sb,
857c4c66153SMark Johnston AT_EMPTY_PATH) == -1);
858c4c66153SMark Johnston CHECKED_CLOSE(fd[0]);
859c4c66153SMark Johnston CHECKED_CLOSE(fd[1]);
860c4c66153SMark Johnston }
861c4c66153SMark Johnston
8623a248c84SMark Johnston /* Verify that we can send an O_PATH descriptor over a unix socket. */
8633a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_rights);
ATF_TC_BODY(path_rights,tc)8643a248c84SMark Johnston ATF_TC_BODY(path_rights, tc)
8653a248c84SMark Johnston {
8663a248c84SMark Johnston char path[PATH_MAX];
8673a248c84SMark Johnston struct cmsghdr *cmsg;
8683a248c84SMark Johnston struct msghdr msg;
8693a248c84SMark Johnston struct iovec iov;
8703a248c84SMark Johnston int flags, pathfd, pathfd_copy, sd[2];
8713a248c84SMark Johnston char c;
8723a248c84SMark Johnston
8733a248c84SMark Johnston ATF_REQUIRE_MSG(socketpair(PF_LOCAL, SOCK_STREAM, 0, sd) == 0,
8743a248c84SMark Johnston FMT_ERR("socketpair"));
8753a248c84SMark Johnston
8763a248c84SMark Johnston mktfile(path, "path_rights.XXXXXX");
8773a248c84SMark Johnston
8783a248c84SMark Johnston pathfd = open(path, O_PATH);
8793a248c84SMark Johnston ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
8803a248c84SMark Johnston
8813a248c84SMark Johnston /* Package up the O_PATH and send it over the socket pair. */
8823a248c84SMark Johnston cmsg = malloc(CMSG_SPACE(sizeof(pathfd)));
8833a248c84SMark Johnston ATF_REQUIRE_MSG(cmsg != NULL, FMT_ERR("malloc"));
8843a248c84SMark Johnston
8853a248c84SMark Johnston cmsg->cmsg_len = CMSG_LEN(sizeof(pathfd));
8863a248c84SMark Johnston cmsg->cmsg_level = SOL_SOCKET;
8873a248c84SMark Johnston cmsg->cmsg_type = SCM_RIGHTS;
8883a248c84SMark Johnston *(int *)(void *)CMSG_DATA(cmsg) = pathfd;
8893a248c84SMark Johnston
8903a248c84SMark Johnston c = 0;
8913a248c84SMark Johnston iov.iov_base = &c;
8923a248c84SMark Johnston iov.iov_len = 1;
8933a248c84SMark Johnston
8943a248c84SMark Johnston memset(&msg, 0, sizeof(msg));
8953a248c84SMark Johnston msg.msg_iov = &iov;
8963a248c84SMark Johnston msg.msg_iovlen = 1;
8973a248c84SMark Johnston msg.msg_control = cmsg;
8983a248c84SMark Johnston msg.msg_controllen = CMSG_SPACE(sizeof(pathfd));
8993a248c84SMark Johnston
9003a248c84SMark Johnston ATF_REQUIRE_MSG(sendmsg(sd[0], &msg, 0) == sizeof(c),
9013a248c84SMark Johnston FMT_ERR("sendmsg"));
9023a248c84SMark Johnston
9033a248c84SMark Johnston /* Grab the pathfd copy from the other end of the pair. */
9043a248c84SMark Johnston memset(&msg, 0, sizeof(msg));
9053a248c84SMark Johnston msg.msg_iov = &iov;
9063a248c84SMark Johnston msg.msg_iovlen = 1;
9073a248c84SMark Johnston msg.msg_control = cmsg;
9083a248c84SMark Johnston msg.msg_controllen = CMSG_SPACE(sizeof(pathfd));
9093a248c84SMark Johnston
9103a248c84SMark Johnston ATF_REQUIRE_MSG(recvmsg(sd[1], &msg, 0) == 1,
9113a248c84SMark Johnston FMT_ERR("recvmsg"));
9123a248c84SMark Johnston pathfd_copy = *(int *)(void *)CMSG_DATA(cmsg);
9133a248c84SMark Johnston ATF_REQUIRE_MSG(pathfd_copy != pathfd,
9143a248c84SMark Johnston "pathfd and pathfd_copy are equal");
9153a248c84SMark Johnston
9163a248c84SMark Johnston /* Verify that the copy has O_PATH properties. */
9173a248c84SMark Johnston flags = fcntl(pathfd_copy, F_GETFL);
9183a248c84SMark Johnston ATF_REQUIRE_MSG(flags != -1, FMT_ERR("fcntl"));
9193a248c84SMark Johnston ATF_REQUIRE_MSG((flags & O_PATH) != 0, "O_PATH is not set");
9203a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF,
9213a248c84SMark Johnston read(pathfd_copy, &c, 1) == -1);
9223a248c84SMark Johnston ATF_REQUIRE_ERRNO(EBADF,
9233a248c84SMark Johnston write(pathfd_copy, &c, 1) == -1);
9243a248c84SMark Johnston
9253a248c84SMark Johnston CHECKED_CLOSE(pathfd);
9263a248c84SMark Johnston CHECKED_CLOSE(pathfd_copy);
9273a248c84SMark Johnston CHECKED_CLOSE(sd[0]);
9283a248c84SMark Johnston CHECKED_CLOSE(sd[1]);
9293a248c84SMark Johnston }
9303a248c84SMark Johnston
9312bd98269SMark Johnston /* Verify that a local socket can be opened with O_PATH. */
932b59851e9SMark Johnston ATF_TC_WITHOUT_HEAD(path_unix);
ATF_TC_BODY(path_unix,tc)933b59851e9SMark Johnston ATF_TC_BODY(path_unix, tc)
934b59851e9SMark Johnston {
9352bd98269SMark Johnston char buf[BUFSIZ], path[PATH_MAX];
9362bd98269SMark Johnston struct kevent ev;
937b59851e9SMark Johnston struct sockaddr_un sun;
9382bd98269SMark Johnston struct stat sb;
9392bd98269SMark Johnston int kq, pathfd, sd;
940b59851e9SMark Johnston
941b59851e9SMark Johnston snprintf(path, sizeof(path), "path_unix.XXXXXX");
942b59851e9SMark Johnston ATF_REQUIRE_MSG(mktemp(path) == path, FMT_ERR("mktemp"));
943b59851e9SMark Johnston
944b59851e9SMark Johnston sd = socket(PF_LOCAL, SOCK_STREAM, 0);
945b59851e9SMark Johnston ATF_REQUIRE_MSG(sd >= 0, FMT_ERR("socket"));
946b59851e9SMark Johnston
947b59851e9SMark Johnston memset(&sun, 0, sizeof(sun));
948b59851e9SMark Johnston sun.sun_family = PF_LOCAL;
949b59851e9SMark Johnston (void)strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
950b59851e9SMark Johnston ATF_REQUIRE_MSG(bind(sd, (struct sockaddr *)&sun, SUN_LEN(&sun)) == 0,
951b59851e9SMark Johnston FMT_ERR("bind"));
952b59851e9SMark Johnston
9538b83b656SMark Johnston pathfd = open(path, O_PATH);
9542bd98269SMark Johnston ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
9552bd98269SMark Johnston
9562bd98269SMark Johnston ATF_REQUIRE_MSG(fstatat(pathfd, "", &sb, AT_EMPTY_PATH) == 0,
9572bd98269SMark Johnston FMT_ERR("fstatat"));
9582bd98269SMark Johnston ATF_REQUIRE_MSG(sb.st_mode & S_IFSOCK, "socket mode %#x", sb.st_mode);
9592bd98269SMark Johnston ATF_REQUIRE_MSG(sb.st_ino != 0, "socket has inode number 0");
9602bd98269SMark Johnston
9612bd98269SMark Johnston memset(buf, 0, sizeof(buf));
9622bd98269SMark Johnston ATF_REQUIRE_ERRNO(EBADF, write(pathfd, buf, sizeof(buf)));
9632bd98269SMark Johnston ATF_REQUIRE_ERRNO(EBADF, read(pathfd, buf, sizeof(buf)));
9642bd98269SMark Johnston
9652bd98269SMark Johnston /* kevent() is disallowed with sockets. */
9662bd98269SMark Johnston kq = kqueue();
9672bd98269SMark Johnston ATF_REQUIRE_MSG(kq >= 0, FMT_ERR("kqueue"));
9682bd98269SMark Johnston EV_SET(&ev, pathfd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
9692bd98269SMark Johnston ATF_REQUIRE_ERRNO(EBADF, kevent(kq, &ev, 1, NULL, 0, NULL) == -1);
9702bd98269SMark Johnston
9712bd98269SMark Johnston /* Should not be able to open a socket without O_PATH. */
9722bd98269SMark Johnston ATF_REQUIRE_ERRNO(EOPNOTSUPP, openat(pathfd, "", O_EMPTY_PATH) == -1);
9732bd98269SMark Johnston
9742bd98269SMark Johnston ATF_REQUIRE_MSG(funlinkat(AT_FDCWD, path, pathfd, 0) == 0,
9752bd98269SMark Johnston FMT_ERR("funlinkat"));
976b59851e9SMark Johnston
977b59851e9SMark Johnston CHECKED_CLOSE(sd);
9782bd98269SMark Johnston CHECKED_CLOSE(pathfd);
979b59851e9SMark Johnston }
980b59851e9SMark Johnston
981b13ac678SMark Johnston /*
982b13ac678SMark Johnston * Check that we can perform operations using an O_PATH fd for an unlinked file.
983b13ac678SMark Johnston */
984b13ac678SMark Johnston ATF_TC_WITHOUT_HEAD(path_unlinked);
ATF_TC_BODY(path_unlinked,tc)985b13ac678SMark Johnston ATF_TC_BODY(path_unlinked, tc)
986b13ac678SMark Johnston {
987b13ac678SMark Johnston char path[PATH_MAX];
988b13ac678SMark Johnston struct stat sb;
989b13ac678SMark Johnston int pathfd;
990b13ac678SMark Johnston
991b13ac678SMark Johnston mktfile(path, "path_rights.XXXXXX");
992b13ac678SMark Johnston
993b13ac678SMark Johnston pathfd = open(path, O_PATH);
994b13ac678SMark Johnston ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
995b13ac678SMark Johnston
996b13ac678SMark Johnston ATF_REQUIRE_MSG(fstatat(pathfd, "", &sb, AT_EMPTY_PATH) == 0,
997b13ac678SMark Johnston FMT_ERR("fstatat"));
998b13ac678SMark Johnston ATF_REQUIRE(sb.st_nlink == 1);
999b13ac678SMark Johnston ATF_REQUIRE_MSG(fstat(pathfd, &sb) == 0, FMT_ERR("fstat"));
1000b13ac678SMark Johnston ATF_REQUIRE(sb.st_nlink == 1);
1001b13ac678SMark Johnston
1002b13ac678SMark Johnston ATF_REQUIRE_MSG(unlink(path) == 0, FMT_ERR("unlink"));
1003b13ac678SMark Johnston
1004b13ac678SMark Johnston ATF_REQUIRE_MSG(fstatat(pathfd, "", &sb, AT_EMPTY_PATH) == 0,
1005b13ac678SMark Johnston FMT_ERR("fstatat"));
1006b13ac678SMark Johnston ATF_REQUIRE(sb.st_nlink == 0);
1007b13ac678SMark Johnston ATF_REQUIRE_MSG(fstat(pathfd, &sb) == 0, FMT_ERR("fstat"));
1008b13ac678SMark Johnston ATF_REQUIRE(sb.st_nlink == 0);
1009b13ac678SMark Johnston
1010b13ac678SMark Johnston CHECKED_CLOSE(pathfd);
1011b13ac678SMark Johnston }
1012b13ac678SMark Johnston
ATF_TP_ADD_TCS(tp)10133a248c84SMark Johnston ATF_TP_ADD_TCS(tp)
10143a248c84SMark Johnston {
10153a248c84SMark Johnston ATF_TP_ADD_TC(tp, path_access);
10163a248c84SMark Johnston ATF_TP_ADD_TC(tp, path_aio);
10173a248c84SMark Johnston ATF_TP_ADD_TC(tp, path_capsicum);
1018e5e1d9c7SMark Johnston ATF_TP_ADD_TC(tp, path_capsicum_empty);
1019b59851e9SMark Johnston ATF_TP_ADD_TC(tp, path_coredump);
10203a248c84SMark Johnston ATF_TP_ADD_TC(tp, path_directory);
10213a248c84SMark Johnston ATF_TP_ADD_TC(tp, path_directory_not_root);
10223a248c84SMark Johnston ATF_TP_ADD_TC(tp, path_empty);
10233a248c84SMark Johnston ATF_TP_ADD_TC(tp, path_empty_not_root);
10243a248c84SMark Johnston ATF_TP_ADD_TC(tp, path_empty_root);
10253a248c84SMark Johnston ATF_TP_ADD_TC(tp, path_event);
10263a248c84SMark Johnston ATF_TP_ADD_TC(tp, path_fcntl);
10273a248c84SMark Johnston ATF_TP_ADD_TC(tp, path_fexecve);
1028b59851e9SMark Johnston ATF_TP_ADD_TC(tp, path_fifo);
10293a248c84SMark Johnston ATF_TP_ADD_TC(tp, path_funlinkat);
10303a248c84SMark Johnston ATF_TP_ADD_TC(tp, path_io);
10313a248c84SMark Johnston ATF_TP_ADD_TC(tp, path_ioctl);
10323a248c84SMark Johnston ATF_TP_ADD_TC(tp, path_lock);
1033c4c66153SMark Johnston ATF_TP_ADD_TC(tp, path_pipe_fstatat);
10343a248c84SMark Johnston ATF_TP_ADD_TC(tp, path_rights);
1035b59851e9SMark Johnston ATF_TP_ADD_TC(tp, path_unix);
1036b13ac678SMark Johnston ATF_TP_ADD_TC(tp, path_unlinked);
10373a248c84SMark Johnston
10383a248c84SMark Johnston return (atf_no_error());
10393a248c84SMark Johnston }
1040