xref: /freebsd/tests/sys/file/path_test.c (revision 8b83b656a507ee767fcb6921985720d1df61101b)
13a248c84SMark Johnston /*-
23a248c84SMark Johnston  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 
66b42df9daSKonstantin Belousov /*
67b42df9daSKonstantin Belousov  * Verify fstatat(AT_EMPTY_PATH) on non-regular dirfd.
68b42df9daSKonstantin Belousov  * Verify that fstatat(AT_EMPTY_PATH) on NULL path returns EFAULT.
69b42df9daSKonstantin Belousov  */
70b42df9daSKonstantin Belousov ATF_TC_WITHOUT_HEAD(path_pipe_fstatat);
71b42df9daSKonstantin Belousov ATF_TC_BODY(path_pipe_fstatat, tc)
72b42df9daSKonstantin Belousov {
73b42df9daSKonstantin Belousov 	struct stat sb;
74b42df9daSKonstantin Belousov   	int fd[2];
75b42df9daSKonstantin Belousov 
76b42df9daSKonstantin Belousov 	ATF_REQUIRE_MSG(pipe(fd) == 0, FMT_ERR("pipe"));
77b42df9daSKonstantin Belousov 	ATF_REQUIRE_MSG(fstatat(fd[0], "", &sb, AT_EMPTY_PATH) == 0,
78b42df9daSKonstantin Belousov 	    FMT_ERR("fstatat pipe"));
79b42df9daSKonstantin Belousov 	ATF_REQUIRE_ERRNO(EFAULT, fstatat(fd[0], NULL, &sb,
80b42df9daSKonstantin Belousov 	    AT_EMPTY_PATH) == -1);
81b42df9daSKonstantin Belousov 	CHECKED_CLOSE(fd[0]);
82b42df9daSKonstantin Belousov 	CHECKED_CLOSE(fd[1]);
83b42df9daSKonstantin Belousov }
84b42df9daSKonstantin Belousov 
853a248c84SMark Johnston /* Create a temporary regular file containing some data. */
863a248c84SMark Johnston static void
873a248c84SMark Johnston mktfile(char path[PATH_MAX], const char *template)
883a248c84SMark Johnston {
893a248c84SMark Johnston 	char buf[BUFSIZ];
903a248c84SMark Johnston 	int fd;
913a248c84SMark Johnston 
923a248c84SMark Johnston 	snprintf(path, PATH_MAX, "%s", template);
933a248c84SMark Johnston 	fd = mkstemp(path);
943a248c84SMark Johnston 	ATF_REQUIRE_MSG(fd >= 0, FMT_ERR("mkstemp"));
953a248c84SMark Johnston 	memset(buf, 0, sizeof(buf));
963a248c84SMark Johnston 	ATF_REQUIRE_MSG(write(fd, buf, sizeof(buf)) == sizeof(buf),
973a248c84SMark Johnston 	    FMT_ERR("write"));
983a248c84SMark Johnston 	CHECKED_CLOSE(fd);
993a248c84SMark Johnston }
1003a248c84SMark Johnston 
1013a248c84SMark Johnston /* Make a temporary directory. */
1023a248c84SMark Johnston static void
1033a248c84SMark Johnston mktdir(char path[PATH_MAX], const char *template)
1043a248c84SMark Johnston {
1053a248c84SMark Johnston 	snprintf(path, PATH_MAX, "%s", template);
1063a248c84SMark Johnston 	ATF_REQUIRE_MSG(mkdtemp(path) == path, FMT_ERR("mkdtemp"));
1073a248c84SMark Johnston }
1083a248c84SMark Johnston 
1093a248c84SMark Johnston /* Wait for a child process to exit with status 0. */
1103a248c84SMark Johnston static void
1113a248c84SMark Johnston waitchild(pid_t child, int exstatus)
1123a248c84SMark Johnston {
1133a248c84SMark Johnston 	int error, status;
1143a248c84SMark Johnston 
1153a248c84SMark Johnston 	error = waitpid(child, &status, 0);
1163a248c84SMark Johnston 	ATF_REQUIRE_MSG(error != -1, FMT_ERR("waitpid"));
1173a248c84SMark Johnston 	ATF_REQUIRE_MSG(WIFEXITED(status), "child exited abnormally, status %d",
1183a248c84SMark Johnston 	    status);
1193a248c84SMark Johnston 	ATF_REQUIRE_MSG(WEXITSTATUS(status) == exstatus,
1203a248c84SMark Johnston 	    "child exit status is %d, expected %d",
1213a248c84SMark Johnston 	    WEXITSTATUS(status), exstatus);
1223a248c84SMark Johnston }
1233a248c84SMark Johnston 
1243a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_access);
1253a248c84SMark Johnston ATF_TC_BODY(path_access, tc)
1263a248c84SMark Johnston {
1273a248c84SMark Johnston 	char path[PATH_MAX];
1283a248c84SMark Johnston 	struct stat sb;
1293a248c84SMark Johnston 	struct timespec ts[2];
1303a248c84SMark Johnston 	struct timeval tv[2];
1313a248c84SMark Johnston 	int pathfd;
1323a248c84SMark Johnston 
1333a248c84SMark Johnston 	mktfile(path, "path_access.XXXXXX");
1343a248c84SMark Johnston 
1353a248c84SMark Johnston 	pathfd = open(path, O_PATH);
1363a248c84SMark Johnston 	ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
1373a248c84SMark Johnston 
1383a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, fchmod(pathfd, 0666) == -1);
1393a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, fchown(pathfd, getuid(), getgid()) == -1);
1403a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, fchflags(pathfd, UF_NODUMP) == -1);
1413a248c84SMark Johnston 	memset(tv, 0, sizeof(tv));
1423a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, futimes(pathfd, tv) == -1);
1433a248c84SMark Johnston 	memset(ts, 0, sizeof(ts));
1443a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, futimens(pathfd, ts) == -1);
1453a248c84SMark Johnston 
1463a248c84SMark Johnston 	/* fpathconf(2) and fstat(2) are permitted. */
1473a248c84SMark Johnston 	ATF_REQUIRE_MSG(fstat(pathfd, &sb) == 0, FMT_ERR("fstat"));
1483a248c84SMark Johnston 	ATF_REQUIRE_MSG(fpathconf(pathfd, _PC_LINK_MAX) != -1,
1493a248c84SMark Johnston 	    FMT_ERR("fpathconf"));
1503a248c84SMark Johnston 
1513a248c84SMark Johnston 	CHECKED_CLOSE(pathfd);
1523a248c84SMark Johnston }
1533a248c84SMark Johnston 
1543a248c84SMark Johnston /* Basic tests to verify that AIO operations fail. */
1553a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_aio);
1563a248c84SMark Johnston ATF_TC_BODY(path_aio, tc)
1573a248c84SMark Johnston {
1583a248c84SMark Johnston 	struct aiocb aio;
1593a248c84SMark Johnston 	char buf[BUFSIZ], path[PATH_MAX];
1603a248c84SMark Johnston 	int pathfd;
1613a248c84SMark Johnston 
1623a248c84SMark Johnston 	mktfile(path, "path_aio.XXXXXX");
1633a248c84SMark Johnston 
1643a248c84SMark Johnston 	pathfd = open(path, O_PATH);
1653a248c84SMark Johnston 	ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
1663a248c84SMark Johnston 
1673a248c84SMark Johnston 	memset(&aio, 0, sizeof(aio));
1683a248c84SMark Johnston 	aio.aio_buf = buf;
1693a248c84SMark Johnston 	aio.aio_nbytes = sizeof(buf);
1703a248c84SMark Johnston 	aio.aio_fildes = pathfd;
1713a248c84SMark Johnston 	aio.aio_offset = 0;
1723a248c84SMark Johnston 
1733a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, aio_read(&aio) == -1);
1743a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, aio_write(&aio) == -1);
1753a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, aio_fsync(O_SYNC, &aio) == -1);
1763a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, aio_fsync(O_DSYNC, &aio) == -1);
1773a248c84SMark Johnston 
1783a248c84SMark Johnston 	CHECKED_CLOSE(pathfd);
1793a248c84SMark Johnston }
1803a248c84SMark Johnston 
1813a248c84SMark Johnston /* Basic tests to verify that Capsicum restrictions apply to path fds. */
1823a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_capsicum);
1833a248c84SMark Johnston ATF_TC_BODY(path_capsicum, tc)
1843a248c84SMark Johnston {
1853a248c84SMark Johnston 	char path[PATH_MAX];
1863a248c84SMark Johnston 	cap_rights_t rights;
1873a248c84SMark Johnston 	int truefd;
1883a248c84SMark Johnston 	pid_t child;
1893a248c84SMark Johnston 
1903a248c84SMark Johnston 	mktfile(path, "path_capsicum.XXXXXX");
1913a248c84SMark Johnston 
1923a248c84SMark Johnston 	/* Make sure that filesystem namespace restrictions apply to O_PATH. */
1933a248c84SMark Johnston 	child = fork();
1943a248c84SMark Johnston 	ATF_REQUIRE_MSG(child != -1, FMT_ERR("fork"));
1953a248c84SMark Johnston 	if (child == 0) {
1963a248c84SMark Johnston 		if (cap_enter() != 0)
1973a248c84SMark Johnston 			_exit(1);
1983a248c84SMark Johnston 		if (open(path, O_PATH) >= 0)
1993a248c84SMark Johnston 			_exit(2);
2003a248c84SMark Johnston 		if (errno != ECAPMODE)
2013a248c84SMark Johnston 			_exit(3);
2023a248c84SMark Johnston 		if (open("/usr/bin/true", O_PATH | O_EXEC) >= 0)
2033a248c84SMark Johnston 			_exit(4);
2043a248c84SMark Johnston 		if (errno != ECAPMODE)
2053a248c84SMark Johnston 			_exit(5);
2063a248c84SMark Johnston 		_exit(0);
2073a248c84SMark Johnston 	}
2083a248c84SMark Johnston 	waitchild(child, 0);
2093a248c84SMark Johnston 
2103a248c84SMark Johnston 	/* Make sure that CAP_FEXECVE is required. */
2113a248c84SMark Johnston 	child = fork();
2123a248c84SMark Johnston 	ATF_REQUIRE_MSG(child != -1, FMT_ERR("fork"));
2133a248c84SMark Johnston 	if (child == 0) {
2143a248c84SMark Johnston 		truefd = open("/usr/bin/true", O_PATH | O_EXEC);
2153a248c84SMark Johnston 		if (truefd < 0)
2163a248c84SMark Johnston 			_exit(1);
2173a248c84SMark Johnston 		cap_rights_init(&rights);
2183a248c84SMark Johnston 		if (cap_rights_limit(truefd, &rights) != 0)
2193a248c84SMark Johnston 			_exit(2);
2203a248c84SMark Johnston 		(void)fexecve(truefd,
2213a248c84SMark Johnston 		    (char * const[]){__DECONST(char *, "/usr/bin/true"), NULL},
2223a248c84SMark Johnston 		    NULL);
2233a248c84SMark Johnston 		if (errno != ENOTCAPABLE)
2243a248c84SMark Johnston 			_exit(3);
2253a248c84SMark Johnston 		_exit(4);
2263a248c84SMark Johnston 	}
2273a248c84SMark Johnston 	waitchild(child, 4);
2283a248c84SMark Johnston }
2293a248c84SMark Johnston 
230b59851e9SMark Johnston /* Make sure that ptrace(PT_COREDUMP) cannot be used to write to a path fd. */
231b59851e9SMark Johnston ATF_TC_WITHOUT_HEAD(path_coredump);
232b59851e9SMark Johnston ATF_TC_BODY(path_coredump, tc)
233b59851e9SMark Johnston {
234b59851e9SMark Johnston 	char path[PATH_MAX];
235b59851e9SMark Johnston 	struct ptrace_coredump pc;
236b59851e9SMark Johnston 	int error, pathfd, status;
237b59851e9SMark Johnston 	pid_t child;
238b59851e9SMark Johnston 
239b59851e9SMark Johnston 	mktdir(path, "path_coredump.XXXXXX");
240b59851e9SMark Johnston 
241b59851e9SMark Johnston 	child = fork();
242b59851e9SMark Johnston 	ATF_REQUIRE_MSG(child != -1, FMT_ERR("fork"));
243b59851e9SMark Johnston 	if (child == 0) {
244b59851e9SMark Johnston 		while (true)
245b59851e9SMark Johnston 			(void)sleep(1);
246b59851e9SMark Johnston 	}
247b59851e9SMark Johnston 
248b59851e9SMark Johnston 	pathfd = open(path, O_PATH);
249b59851e9SMark Johnston 	ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
250b59851e9SMark Johnston 
251b59851e9SMark Johnston 	error = ptrace(PT_ATTACH, child, 0, 0);
252b59851e9SMark Johnston 	ATF_REQUIRE_MSG(error == 0, FMT_ERR("ptrace"));
253b59851e9SMark Johnston 	error = waitpid(child, &status, 0);
254b59851e9SMark Johnston 	ATF_REQUIRE_MSG(error != -1, FMT_ERR("waitpid"));
255b59851e9SMark Johnston 	ATF_REQUIRE_MSG(WIFSTOPPED(status), "unexpected status %d", status);
256b59851e9SMark Johnston 
257b59851e9SMark Johnston 	pc.pc_fd = pathfd;
258b59851e9SMark Johnston 	pc.pc_flags = 0;
259b59851e9SMark Johnston 	pc.pc_limit = 0;
260b59851e9SMark Johnston 	error = ptrace(PT_COREDUMP, child, (void *)&pc, sizeof(pc));
261b59851e9SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, error == -1);
262b59851e9SMark Johnston 
263b59851e9SMark Johnston 	error = ptrace(PT_DETACH, child, 0, 0);
264b59851e9SMark Johnston 	ATF_REQUIRE_MSG(error == 0, FMT_ERR("ptrace"));
265b59851e9SMark Johnston 
266b59851e9SMark Johnston 	ATF_REQUIRE_MSG(kill(child, SIGKILL) == 0, FMT_ERR("kill"));
267b59851e9SMark Johnston 
268b59851e9SMark Johnston 	CHECKED_CLOSE(pathfd);
269b59851e9SMark Johnston }
270b59851e9SMark Johnston 
2713a248c84SMark Johnston /* Verify operations on directory path descriptors. */
2723a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_directory);
2733a248c84SMark Johnston ATF_TC_BODY(path_directory, tc)
2743a248c84SMark Johnston {
2753a248c84SMark Johnston 	struct dirent de;
2763a248c84SMark Johnston 	struct stat sb;
2773a248c84SMark Johnston 	char path[PATH_MAX];
2783a248c84SMark Johnston 	int fd, pathfd;
2793a248c84SMark Johnston 
2803a248c84SMark Johnston 	mktdir(path, "path_directory.XXXXXX");
2813a248c84SMark Johnston 
2823a248c84SMark Johnston 	pathfd = open(path, O_PATH | O_DIRECTORY);
2833a248c84SMark Johnston 	ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
2843a248c84SMark Johnston 
2853a248c84SMark Johnston 	/* Should not be possible to list directory entries. */
2863a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF,
2873a248c84SMark Johnston 	    getdirentries(pathfd, (char *)&de, sizeof(de), NULL) == -1);
2883a248c84SMark Johnston 
2893a248c84SMark Johnston 	/* It should be possible to create files under pathfd. */
2903a248c84SMark Johnston 	fd = openat(pathfd, "test", O_RDWR | O_CREAT, 0600);
2913a248c84SMark Johnston 	ATF_REQUIRE_MSG(fd >= 0, FMT_ERR("open"));
2923a248c84SMark Johnston 	ATF_REQUIRE_MSG(fstatat(pathfd, "test", &sb, 0) == 0,
2933a248c84SMark Johnston 	    FMT_ERR("fstatat"));
2943a248c84SMark Johnston 	CHECKED_CLOSE(fd);
2953a248c84SMark Johnston 
2963a248c84SMark Johnston 	/* ... but doing so requires write access. */
2973a248c84SMark Johnston 	if (geteuid() != 0) {
2983a248c84SMark Johnston 		ATF_REQUIRE_ERRNO(EBADF, fchmod(pathfd, 0500) == -1);
2993a248c84SMark Johnston 		ATF_REQUIRE_MSG(chmod(path, 0500) == 0, FMT_ERR("chmod"));
3003a248c84SMark Johnston 		ATF_REQUIRE_ERRNO(EACCES,
3013a248c84SMark Johnston 		    openat(pathfd, "test2", O_RDWR | O_CREAT, 0600) < 0);
3023a248c84SMark Johnston 	}
3033a248c84SMark Johnston 
3043a248c84SMark Johnston 	/* fchdir(2) is permitted. */
3053a248c84SMark Johnston 	ATF_REQUIRE_MSG(fchdir(pathfd) == 0, FMT_ERR("fchdir"));
3063a248c84SMark Johnston 
3073a248c84SMark Johnston 	CHECKED_CLOSE(pathfd);
3083a248c84SMark Johnston }
3093a248c84SMark Johnston 
3103a248c84SMark Johnston /* Verify access permission checking for a directory path fd. */
3113a248c84SMark Johnston ATF_TC_WITH_CLEANUP(path_directory_not_root);
3123a248c84SMark Johnston ATF_TC_HEAD(path_directory_not_root, tc)
3133a248c84SMark Johnston {
3143a248c84SMark Johnston 	atf_tc_set_md_var(tc, "require.user", "unprivileged");
3153a248c84SMark Johnston }
3163a248c84SMark Johnston ATF_TC_BODY(path_directory_not_root, tc)
3173a248c84SMark Johnston {
3183a248c84SMark Johnston 	char path[PATH_MAX];
3193a248c84SMark Johnston 	int pathfd;
3203a248c84SMark Johnston 
3213a248c84SMark Johnston 	mktdir(path, "path_directory.XXXXXX");
3223a248c84SMark Johnston 
3233a248c84SMark Johnston 	pathfd = open(path, O_PATH | O_DIRECTORY);
3243a248c84SMark Johnston 	ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
3253a248c84SMark Johnston 
3263a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, fchmod(pathfd, 0500) == -1);
3273a248c84SMark Johnston 	ATF_REQUIRE_MSG(chmod(path, 0500) == 0, FMT_ERR("chmod"));
3283a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EACCES,
3293a248c84SMark Johnston 	    openat(pathfd, "test2", O_RDWR | O_CREAT, 0600) < 0);
3303a248c84SMark Johnston 
3313a248c84SMark Johnston 	CHECKED_CLOSE(pathfd);
3323a248c84SMark Johnston }
3333a248c84SMark Johnston ATF_TC_CLEANUP(path_directory_not_root, tc)
3343a248c84SMark Johnston {
3353a248c84SMark Johnston }
3363a248c84SMark Johnston 
3373a248c84SMark Johnston /* Validate system calls that handle AT_EMPTY_PATH. */
3383a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_empty);
3393a248c84SMark Johnston ATF_TC_BODY(path_empty, tc)
3403a248c84SMark Johnston {
3413a248c84SMark Johnston 	char path[PATH_MAX];
3423a248c84SMark Johnston 	struct timespec ts[2];
3433a248c84SMark Johnston 	struct stat sb;
3443a248c84SMark Johnston 	int pathfd;
3453a248c84SMark Johnston 
3463a248c84SMark Johnston 	mktfile(path, "path_empty.XXXXXX");
3473a248c84SMark Johnston 
3483a248c84SMark Johnston 	pathfd = open(path, O_PATH);
3493a248c84SMark Johnston 	ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
3503a248c84SMark Johnston 
3513a248c84SMark Johnston 	/* Various *at operations should work on path fds. */
3523a248c84SMark Johnston 	ATF_REQUIRE_MSG(faccessat(pathfd, "", F_OK, AT_EMPTY_PATH) == 0,
3533a248c84SMark Johnston 	    FMT_ERR("faccessat"));
3543a248c84SMark Johnston 	ATF_REQUIRE_MSG(chflagsat(pathfd, "", UF_NODUMP, AT_EMPTY_PATH) == 0,
3553a248c84SMark Johnston 	    FMT_ERR("chflagsat"));
3563a248c84SMark Johnston 	ATF_REQUIRE_MSG(fchmodat(pathfd, "", 0600, AT_EMPTY_PATH) == 0,
3573a248c84SMark Johnston 	    FMT_ERR("fchmodat"));
3583a248c84SMark Johnston 	ATF_REQUIRE_MSG(fchownat(pathfd, "", getuid(), getgid(),
3593a248c84SMark Johnston 	    AT_EMPTY_PATH) == 0, FMT_ERR("fchownat"));
3603a248c84SMark Johnston 	ATF_REQUIRE_MSG(fstatat(pathfd, "", &sb, AT_EMPTY_PATH) == 0,
3613a248c84SMark Johnston 	    FMT_ERR("fstatat"));
3623a248c84SMark Johnston 	ATF_REQUIRE_MSG(sb.st_size == BUFSIZ,
3633a248c84SMark Johnston 	    "unexpected size %ju", (uintmax_t)sb.st_size);
3643a248c84SMark Johnston 	memset(ts, 0, sizeof(ts));
3653a248c84SMark Johnston 	ATF_REQUIRE_MSG(utimensat(pathfd, "", ts, AT_EMPTY_PATH) == 0,
3663a248c84SMark Johnston 	    FMT_ERR("utimensat"));
3673a248c84SMark Johnston 
3683a248c84SMark Johnston 	CHECKED_CLOSE(pathfd);
3693a248c84SMark Johnston }
3703a248c84SMark Johnston 
3713a248c84SMark Johnston /* Verify that various operations on a path fd have access checks. */
3723a248c84SMark Johnston ATF_TC_WITH_CLEANUP(path_empty_not_root);
3733a248c84SMark Johnston ATF_TC_HEAD(path_empty_not_root, tc)
3743a248c84SMark Johnston {
3753a248c84SMark Johnston 	atf_tc_set_md_var(tc, "require.user", "unprivileged");
3763a248c84SMark Johnston }
3773a248c84SMark Johnston ATF_TC_BODY(path_empty_not_root, tc)
3783a248c84SMark Johnston {
3793a248c84SMark Johnston 	int pathfd;
3803a248c84SMark Johnston 
3813a248c84SMark Johnston 	pathfd = open("/dev/null", O_PATH);
3823a248c84SMark Johnston 	ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
3833a248c84SMark Johnston 
3843a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EPERM,
3853a248c84SMark Johnston 	    chflagsat(pathfd, "", UF_NODUMP, AT_EMPTY_PATH) == -1);
3863a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EPERM,
3873a248c84SMark Johnston 	    fchownat(pathfd, "", getuid(), getgid(), AT_EMPTY_PATH) == -1);
3883a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EPERM,
3893a248c84SMark Johnston 	    fchmodat(pathfd, "", 0600, AT_EMPTY_PATH) == -1);
3903a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EPERM,
3913a248c84SMark Johnston 	    linkat(pathfd, "", AT_FDCWD, "test", AT_EMPTY_PATH) == -1);
3923a248c84SMark Johnston 
3933a248c84SMark Johnston 	CHECKED_CLOSE(pathfd);
3943a248c84SMark Johnston }
3953a248c84SMark Johnston ATF_TC_CLEANUP(path_empty_not_root, tc)
3963a248c84SMark Johnston {
3973a248c84SMark Johnston }
3983a248c84SMark Johnston 
3993a248c84SMark Johnston /* Test linkat(2) with AT_EMPTY_PATH, which requires privileges. */
4003a248c84SMark Johnston ATF_TC_WITH_CLEANUP(path_empty_root);
4013a248c84SMark Johnston ATF_TC_HEAD(path_empty_root, tc)
4023a248c84SMark Johnston {
4033a248c84SMark Johnston 	atf_tc_set_md_var(tc, "require.user", "root");
4043a248c84SMark Johnston }
4053a248c84SMark Johnston ATF_TC_BODY(path_empty_root, tc)
4063a248c84SMark Johnston {
4073a248c84SMark Johnston 	char path[PATH_MAX];
4083a248c84SMark Johnston 	struct stat sb, sb2;
4093a248c84SMark Johnston 	int pathfd;
4103a248c84SMark Johnston 
4113a248c84SMark Johnston 	mktfile(path, "path_empty_root.XXXXXX");
4123a248c84SMark Johnston 
4133a248c84SMark Johnston 	pathfd = open(path, O_PATH);
4143a248c84SMark Johnston 	ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
4153a248c84SMark Johnston 	ATF_REQUIRE_MSG(fstatat(pathfd, "", &sb, AT_EMPTY_PATH) == 0,
4163a248c84SMark Johnston 	    FMT_ERR("fstatat"));
4173a248c84SMark Johnston 
4183a248c84SMark Johnston 	ATF_REQUIRE_MSG(linkat(pathfd, "", AT_FDCWD, "test", AT_EMPTY_PATH) ==
4193a248c84SMark Johnston 	    0, FMT_ERR("linkat"));
4203a248c84SMark Johnston 	ATF_REQUIRE_MSG(fstatat(AT_FDCWD, "test", &sb2, 0) == 0,
4213a248c84SMark Johnston 	    FMT_ERR("fstatat"));
4223a248c84SMark Johnston 	ATF_REQUIRE_MSG(sb.st_dev == sb2.st_dev, "st_dev mismatch");
4233a248c84SMark Johnston 	ATF_REQUIRE_MSG(sb.st_ino == sb2.st_ino, "st_ino mismatch");
4243a248c84SMark Johnston 
4253a248c84SMark Johnston 	CHECKED_CLOSE(pathfd);
4263a248c84SMark Johnston 
4273a248c84SMark Johnston }
4283a248c84SMark Johnston ATF_TC_CLEANUP(path_empty_root, tc)
4293a248c84SMark Johnston {
4303a248c84SMark Johnston }
4313a248c84SMark Johnston 
4323a248c84SMark Johnston /* poll(2) never returns an event for path fds, but kevent(2) does. */
4333a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_event);
4343a248c84SMark Johnston ATF_TC_BODY(path_event, tc)
4353a248c84SMark Johnston {
4363a248c84SMark Johnston 	char buf[BUFSIZ], path[PATH_MAX];
4373a248c84SMark Johnston 	struct kevent ev;
4383a248c84SMark Johnston 	struct pollfd pollfd;
4393a248c84SMark Johnston 	int kq, pathfd;
4403a248c84SMark Johnston 
4413a248c84SMark Johnston 	mktfile(path, "path_event.XXXXXX");
4423a248c84SMark Johnston 
4433a248c84SMark Johnston 	pathfd = open(path, O_PATH);
4443a248c84SMark Johnston 	ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
4453a248c84SMark Johnston 
4463a248c84SMark Johnston 	/* poll(2) should return POLLNVAL. */
4473a248c84SMark Johnston 	pollfd.fd = pathfd;
4483a248c84SMark Johnston 	pollfd.events = POLLIN;
4493a248c84SMark Johnston 	pollfd.revents = 0;
4503a248c84SMark Johnston 	ATF_REQUIRE_MSG(poll(&pollfd, 1, 0) == 1, FMT_ERR("poll"));
4513a248c84SMark Johnston 	ATF_REQUIRE_MSG(pollfd.revents == POLLNVAL, "unexpected revents %x",
4523a248c84SMark Johnston 	    pollfd.revents);
4533a248c84SMark Johnston 	pollfd.events = POLLOUT;
4543a248c84SMark Johnston 	pollfd.revents = 0;
4553a248c84SMark Johnston 	ATF_REQUIRE_MSG(poll(&pollfd, 1, 0) == 1, FMT_ERR("poll"));
4563a248c84SMark Johnston 	ATF_REQUIRE_MSG(pollfd.revents == POLLNVAL, "unexpected revents %x",
4573a248c84SMark Johnston 	    pollfd.revents);
4583a248c84SMark Johnston 
4593a248c84SMark Johnston 	/* Try to get a EVFILT_READ event through a path fd. */
4603a248c84SMark Johnston 	kq = kqueue();
4613a248c84SMark Johnston 	ATF_REQUIRE_MSG(kq >= 0, FMT_ERR("kqueue"));
4623a248c84SMark Johnston 	EV_SET(&ev, pathfd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
4633a248c84SMark Johnston 	ATF_REQUIRE_MSG(kevent(kq, &ev, 1, NULL, 0, NULL) == 0,
4643a248c84SMark Johnston 	    FMT_ERR("kevent"));
4653a248c84SMark Johnston 	ATF_REQUIRE_MSG(kevent(kq, NULL, 0, &ev, 1, NULL) == 1,
4663a248c84SMark Johnston 	    FMT_ERR("kevent"));
4673a248c84SMark Johnston 	ATF_REQUIRE_MSG((ev.flags & EV_ERROR) == 0, "EV_ERROR is set");
4683a248c84SMark Johnston 	ATF_REQUIRE_MSG(ev.data == sizeof(buf),
4693a248c84SMark Johnston 	    "data is %jd", (intmax_t)ev.data);
4703a248c84SMark Johnston 	EV_SET(&ev, pathfd, EVFILT_READ, EV_DELETE, 0, 0, 0);
4713a248c84SMark Johnston 	ATF_REQUIRE_MSG(kevent(kq, &ev, 1, NULL, 0, NULL) == 0,
4723a248c84SMark Johnston 	    FMT_ERR("kevent"));
4733a248c84SMark Johnston 
4743a248c84SMark Johnston 	/* Try to get a EVFILT_VNODE/NOTE_LINK event through a path fd. */
4753a248c84SMark Johnston 	EV_SET(&ev, pathfd, EVFILT_VNODE, EV_ADD | EV_ENABLE, NOTE_LINK, 0, 0);
4763a248c84SMark Johnston 	ATF_REQUIRE_MSG(kevent(kq, &ev, 1, NULL, 0, NULL) == 0,
4773a248c84SMark Johnston 	    FMT_ERR("kevent"));
4783a248c84SMark Johnston 	ATF_REQUIRE_MSG(funlinkat(AT_FDCWD, path, pathfd, 0) == 0,
4793a248c84SMark Johnston 	    FMT_ERR("funlinkat"));
4803a248c84SMark Johnston 	ATF_REQUIRE_MSG(kevent(kq, NULL, 0, &ev, 1, NULL) == 1,
4813a248c84SMark Johnston 	    FMT_ERR("kevent"));
4823a248c84SMark Johnston 	EV_SET(&ev, pathfd, EVFILT_VNODE, EV_DELETE, 0, 0, 0);
4833a248c84SMark Johnston 	ATF_REQUIRE_MSG(kevent(kq, &ev, 1, NULL, 0, NULL) == 0,
4843a248c84SMark Johnston 	    FMT_ERR("kevent"));
4853a248c84SMark Johnston 
4863a248c84SMark Johnston 	CHECKED_CLOSE(kq);
4873a248c84SMark Johnston 	CHECKED_CLOSE(pathfd);
4883a248c84SMark Johnston }
4893a248c84SMark Johnston 
4903a248c84SMark Johnston /* Check various fcntl(2) operations on a path desriptor. */
4913a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_fcntl);
4923a248c84SMark Johnston ATF_TC_BODY(path_fcntl, tc)
4933a248c84SMark Johnston {
4943a248c84SMark Johnston 	char path[PATH_MAX];
4953a248c84SMark Johnston 	int flags, pathfd, pathfd2;
4963a248c84SMark Johnston 
4973a248c84SMark Johnston 	mktfile(path, "path_fcntl.XXXXXX");
4983a248c84SMark Johnston 
4993a248c84SMark Johnston 	pathfd = open(path, O_PATH);
5003a248c84SMark Johnston 	ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
5013a248c84SMark Johnston 
5023a248c84SMark Johnston 	/* O_PATH should appear in the fd flags. */
5033a248c84SMark Johnston 	flags = fcntl(pathfd, F_GETFL);
5043a248c84SMark Johnston 	ATF_REQUIRE_MSG(flags != -1, FMT_ERR("fcntl"));
5053a248c84SMark Johnston 	ATF_REQUIRE_MSG((flags & O_PATH) != 0, "O_PATH not set");
5063a248c84SMark Johnston 
5073a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF,
5083a248c84SMark Johnston 	    fcntl(pathfd, F_SETFL, flags & ~O_PATH));
5093a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF,
5103a248c84SMark Johnston 	    fcntl(pathfd, F_SETFL, flags | O_APPEND));
5113a248c84SMark Johnston 
5123a248c84SMark Johnston 	/* A dup'ed O_PATH fd had better have O_PATH set too. */
5133a248c84SMark Johnston 	pathfd2 = fcntl(pathfd, F_DUPFD, 0);
5143a248c84SMark Johnston 	ATF_REQUIRE_MSG(pathfd2 >= 0, FMT_ERR("fcntl"));
5153a248c84SMark Johnston 	flags = fcntl(pathfd2, F_GETFL);
5163a248c84SMark Johnston 	ATF_REQUIRE_MSG(flags != -1, FMT_ERR("fcntl"));
5173a248c84SMark Johnston 	ATF_REQUIRE_MSG((flags & O_PATH) != 0, "O_PATH not set");
5183a248c84SMark Johnston 	CHECKED_CLOSE(pathfd2);
5193a248c84SMark Johnston 
5203a248c84SMark Johnston 	/* Double check with dup(2). */
5213a248c84SMark Johnston 	pathfd2 = dup(pathfd);
5223a248c84SMark Johnston 	ATF_REQUIRE_MSG(pathfd2 >= 0, FMT_ERR("dup"));
5233a248c84SMark Johnston 	flags = fcntl(pathfd2, F_GETFL);
5243a248c84SMark Johnston 	ATF_REQUIRE_MSG(flags != -1, FMT_ERR("fcntl"));
5253a248c84SMark Johnston 	ATF_REQUIRE_MSG((flags & O_PATH) != 0, "O_PATH not set");
5263a248c84SMark Johnston 	CHECKED_CLOSE(pathfd2);
5273a248c84SMark Johnston 
5283a248c84SMark Johnston 	/* It should be possible to set O_CLOEXEC. */
5293a248c84SMark Johnston 	ATF_REQUIRE_MSG(fcntl(pathfd, F_SETFD, FD_CLOEXEC) == 0,
5303a248c84SMark Johnston 	    FMT_ERR("fcntl"));
5313a248c84SMark Johnston 	ATF_REQUIRE_MSG(fcntl(pathfd, F_GETFD) == FD_CLOEXEC,
5323a248c84SMark Johnston 	    FMT_ERR("fcntl"));
5333a248c84SMark Johnston 
5343a248c84SMark Johnston 	CHECKED_CLOSE(pathfd);
5353a248c84SMark Johnston }
5363a248c84SMark Johnston 
5373a248c84SMark Johnston /* Verify that we can execute a file opened with O_PATH. */
5383a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_fexecve);
5393a248c84SMark Johnston ATF_TC_BODY(path_fexecve, tc)
5403a248c84SMark Johnston {
5413a248c84SMark Johnston 	char path[PATH_MAX];
5423a248c84SMark Johnston 	pid_t child;
5433a248c84SMark Johnston 	int fd, pathfd;
5443a248c84SMark Johnston 
5453a248c84SMark Johnston 	child = fork();
5463a248c84SMark Johnston 	ATF_REQUIRE_MSG(child != -1, FMT_ERR("fork"));
5473a248c84SMark Johnston 	if (child == 0) {
5483a248c84SMark Johnston 		pathfd = open("/usr/bin/true", O_PATH | O_EXEC);
5493a248c84SMark Johnston 		if (pathfd < 0)
5503a248c84SMark Johnston 			_exit(1);
5513a248c84SMark Johnston 		fexecve(pathfd,
5523a248c84SMark Johnston 		    (char * const[]){__DECONST(char *, "/usr/bin/true"), NULL},
5533a248c84SMark Johnston 		    NULL);
5543a248c84SMark Johnston 		_exit(2);
5553a248c84SMark Johnston 	}
5563a248c84SMark Johnston 	waitchild(child, 0);
5573a248c84SMark Johnston 
5583a248c84SMark Johnston 	/*
5593a248c84SMark Johnston 	 * Also verify that access permissions are checked when opening with
5603a248c84SMark Johnston 	 * O_PATH.
5613a248c84SMark Johnston 	 */
5623a248c84SMark Johnston 	snprintf(path, sizeof(path), "path_fexecve.XXXXXX");
5633a248c84SMark Johnston 	ATF_REQUIRE_MSG(mktemp(path) == path, FMT_ERR("mktemp"));
5643a248c84SMark Johnston 
5653a248c84SMark Johnston 	fd = open(path, O_CREAT | O_RDONLY, 0600);
5663a248c84SMark Johnston 	ATF_REQUIRE_MSG(fd >= 0, FMT_ERR("open"));
5673a248c84SMark Johnston 
5683a248c84SMark Johnston 	pathfd = open(path, O_PATH | O_EXEC);
5693a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EACCES, pathfd < 0);
5703a248c84SMark Johnston }
5713a248c84SMark Johnston 
572b59851e9SMark Johnston /* Make sure that O_PATH restrictions apply to named pipes as well. */
573b59851e9SMark Johnston ATF_TC_WITHOUT_HEAD(path_fifo);
574b59851e9SMark Johnston ATF_TC_BODY(path_fifo, tc)
575b59851e9SMark Johnston {
576b59851e9SMark Johnston 	char path[PATH_MAX], buf[BUFSIZ];
577b59851e9SMark Johnston 	struct kevent ev;
578b59851e9SMark Johnston 	int kq, pathfd;
579b59851e9SMark Johnston 
580b59851e9SMark Johnston 	snprintf(path, sizeof(path), "path_fifo.XXXXXX");
581b59851e9SMark Johnston 	ATF_REQUIRE_MSG(mktemp(path) == path, FMT_ERR("mktemp"));
582b59851e9SMark Johnston 
583b59851e9SMark Johnston 	ATF_REQUIRE_MSG(mkfifo(path, 0666) == 0, FMT_ERR("mkfifo"));
584b59851e9SMark Johnston 
585b59851e9SMark Johnston 	pathfd = open(path, O_PATH);
586b59851e9SMark Johnston 	ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
587b59851e9SMark Johnston 	memset(buf, 0, sizeof(buf));
588b59851e9SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, write(pathfd, buf, sizeof(buf)));
589b59851e9SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, read(pathfd, buf, sizeof(buf)));
590b59851e9SMark Johnston 
591b59851e9SMark Johnston 	kq = kqueue();
592b59851e9SMark Johnston 	ATF_REQUIRE_MSG(kq >= 0, FMT_ERR("kqueue"));
593b59851e9SMark Johnston 	EV_SET(&ev, pathfd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
594b59851e9SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, kevent(kq, &ev, 1, NULL, 0, NULL) == -1);
595b59851e9SMark Johnston 
596b59851e9SMark Johnston 	CHECKED_CLOSE(pathfd);
597b59851e9SMark Johnston }
598b59851e9SMark Johnston 
5993a248c84SMark Johnston /* Files may be unlinked using a path fd. */
6003a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_funlinkat);
6013a248c84SMark Johnston ATF_TC_BODY(path_funlinkat, tc)
6023a248c84SMark Johnston {
6033a248c84SMark Johnston 	char path[PATH_MAX];
6043a248c84SMark Johnston 	struct stat sb;
6053a248c84SMark Johnston 	int pathfd;
6063a248c84SMark Johnston 
6073a248c84SMark Johnston 	mktfile(path, "path_rights.XXXXXX");
6083a248c84SMark Johnston 
6093a248c84SMark Johnston 	pathfd = open(path, O_PATH);
6103a248c84SMark Johnston 	ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
6113a248c84SMark Johnston 
6123a248c84SMark Johnston 	ATF_REQUIRE_MSG(funlinkat(AT_FDCWD, path, pathfd, 0) == 0,
6133a248c84SMark Johnston 	    FMT_ERR("funlinkat"));
6143a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(ENOENT, stat(path, &sb) == -1);
6153a248c84SMark Johnston 
6163a248c84SMark Johnston 	CHECKED_CLOSE(pathfd);
6173a248c84SMark Johnston }
6183a248c84SMark Johnston 
6193a248c84SMark Johnston /* Verify that various I/O operations fail on an O_PATH descriptor. */
6203a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_io);
6213a248c84SMark Johnston ATF_TC_BODY(path_io, tc)
6223a248c84SMark Johnston {
6233a248c84SMark Johnston 	char path[PATH_MAX], path2[PATH_MAX];
6243a248c84SMark Johnston 	char buf[BUFSIZ];
6253a248c84SMark Johnston 	struct iovec iov;
6263a248c84SMark Johnston 	int error, fd, pathfd, sd[2];
6273a248c84SMark Johnston 
6283a248c84SMark Johnston 	/* It shouldn't be possible to create new files with O_PATH. */
6293a248c84SMark Johnston 	snprintf(path, sizeof(path), "path_io.XXXXXX");
6303a248c84SMark Johnston 	ATF_REQUIRE_MSG(mktemp(path) == path, FMT_ERR("mktemp"));
6313a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(ENOENT, open(path, O_PATH | O_CREAT, 0600) < 0);
6323a248c84SMark Johnston 
6333a248c84SMark Johnston 	/* Create a non-empty file for use in the rest of the tests. */
6343a248c84SMark Johnston 	mktfile(path, "path_io.XXXXXX");
6353a248c84SMark Johnston 
6363a248c84SMark Johnston 	pathfd = open(path, O_PATH);
6373a248c84SMark Johnston 	ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
6383a248c84SMark Johnston 
6393a248c84SMark Johnston 	/* Make sure that basic I/O operations aren't possible. */
6403a248c84SMark Johnston 	iov.iov_base = path;
6413a248c84SMark Johnston 	iov.iov_len = strlen(path);
6423a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF,
6433a248c84SMark Johnston 	    write(pathfd, iov.iov_base, iov.iov_len) == -1);
6443a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF,
6453a248c84SMark Johnston 	    pwrite(pathfd, iov.iov_base, iov.iov_len, 0) == -1);
6463a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF,
6473a248c84SMark Johnston 	    writev(pathfd, &iov, 1) == -1);
6483a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF,
6493a248c84SMark Johnston 	    pwritev(pathfd, &iov, 1, 0) == -1);
6503a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF,
6513a248c84SMark Johnston 	    read(pathfd, path, 1) == -1);
6523a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF,
6533a248c84SMark Johnston 	    pread(pathfd, path, 1, 0) == -1);
6543a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF,
6553a248c84SMark Johnston 	    readv(pathfd, &iov, 1) == -1);
6563a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF,
6573a248c84SMark Johnston 	    preadv(pathfd, &iov, 1, 0) == -1);
6583a248c84SMark Johnston 
6593a248c84SMark Johnston 	/* copy_file_range() should not be permitted. */
6603a248c84SMark Johnston 	mktfile(path2, "path_io.XXXXXX");
6613a248c84SMark Johnston 	fd = open(path2, O_RDWR);
6623a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF,
6633a248c84SMark Johnston 	    copy_file_range(fd, NULL, pathfd, NULL, sizeof(buf), 0) == -1);
6643a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF,
6653a248c84SMark Johnston 	    copy_file_range(pathfd, NULL, fd, NULL, sizeof(buf), 0) == -1);
6663a248c84SMark Johnston 	CHECKED_CLOSE(fd);
6673a248c84SMark Johnston 
6683a248c84SMark Johnston 	/* sendfile() should not be permitted. */
6693a248c84SMark Johnston 	ATF_REQUIRE_MSG(socketpair(PF_LOCAL, SOCK_STREAM, 0, sd) == 0,
6703a248c84SMark Johnston 	    FMT_ERR("socketpair"));
6713a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF,
6723a248c84SMark Johnston 	    sendfile(pathfd, sd[0], 0, 0, NULL, NULL, 0));
6733a248c84SMark Johnston 	CHECKED_CLOSE(sd[0]);
6743a248c84SMark Johnston 	CHECKED_CLOSE(sd[1]);
6753a248c84SMark Johnston 
6763a248c84SMark Johnston 	/* No seeking. */
6773a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(ESPIPE,
6783a248c84SMark Johnston 	    lseek(pathfd, 0, SEEK_SET) == -1);
6793a248c84SMark Johnston 
6803a248c84SMark Johnston 	/* No operations on the file extent. */
6813a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EINVAL,
6823a248c84SMark Johnston 	    ftruncate(pathfd, 0) == -1);
6833a248c84SMark Johnston 	error = posix_fallocate(pathfd, 0, sizeof(buf) * 2);
6843a248c84SMark Johnston 	ATF_REQUIRE_MSG(error == ESPIPE, "posix_fallocate() returned %d", error);
6853a248c84SMark Johnston 	error = posix_fadvise(pathfd, 0, sizeof(buf), POSIX_FADV_NORMAL);
6863a248c84SMark Johnston 	ATF_REQUIRE_MSG(error == ESPIPE, "posix_fadvise() returned %d", error);
6873a248c84SMark Johnston 
6883a248c84SMark Johnston 	/* mmap() is not allowed. */
6893a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(ENODEV,
6903a248c84SMark Johnston 	    mmap(NULL, PAGE_SIZE, PROT_READ, MAP_SHARED, pathfd, 0) ==
6913a248c84SMark Johnston 	    MAP_FAILED);
6923a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(ENODEV,
6933a248c84SMark Johnston 	    mmap(NULL, PAGE_SIZE, PROT_NONE, MAP_SHARED, pathfd, 0) ==
6943a248c84SMark Johnston 	    MAP_FAILED);
6953a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(ENODEV,
6963a248c84SMark Johnston 	    mmap(NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, pathfd, 0) ==
6973a248c84SMark Johnston 	    MAP_FAILED);
6983a248c84SMark Johnston 
6993a248c84SMark Johnston 	/* No fsync() or fdatasync(). */
7003a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, fsync(pathfd) == -1);
7013a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, fdatasync(pathfd) == -1);
7023a248c84SMark Johnston 
7033a248c84SMark Johnston 	CHECKED_CLOSE(pathfd);
7043a248c84SMark Johnston }
7053a248c84SMark Johnston 
7063a248c84SMark Johnston /* ioctl(2) is not permitted on path fds. */
7073a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_ioctl);
7083a248c84SMark Johnston ATF_TC_BODY(path_ioctl, tc)
7093a248c84SMark Johnston {
7103a248c84SMark Johnston 	char path[PATH_MAX];
7113a248c84SMark Johnston 	struct mem_extract me;
7123a248c84SMark Johnston 	int pathfd, val;
7133a248c84SMark Johnston 
7143a248c84SMark Johnston 	mktfile(path, "path_ioctl.XXXXXX");
7153a248c84SMark Johnston 
7163a248c84SMark Johnston 	/* Standard file descriptor ioctls should fail. */
7173a248c84SMark Johnston 	pathfd = open(path, O_PATH);
7183a248c84SMark Johnston 	ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
7193a248c84SMark Johnston 
7203a248c84SMark Johnston 	val = 0;
7213a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, ioctl(pathfd, FIONBIO, &val) == -1);
7223a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, ioctl(pathfd, FIONREAD, &val) == -1);
7233a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, ioctl(pathfd, FIONWRITE, &val) == -1);
7243a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, ioctl(pathfd, FIONSPACE, &val) == -1);
7253a248c84SMark Johnston 
7263a248c84SMark Johnston 	CHECKED_CLOSE(pathfd);
7273a248c84SMark Johnston 
7283a248c84SMark Johnston 	/* Device ioctls should fail. */
7293a248c84SMark Johnston 	pathfd = open("/dev/mem", O_PATH);
7303a248c84SMark Johnston 	ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
7313a248c84SMark Johnston 
7323a248c84SMark Johnston 	me.me_vaddr = (uintptr_t)&me;
7333a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, ioctl(pathfd, MEM_EXTRACT_PADDR, &me) == -1);
7343a248c84SMark Johnston 
7353a248c84SMark Johnston 	CHECKED_CLOSE(pathfd);
7363a248c84SMark Johnston }
7373a248c84SMark Johnston 
7383a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_lock);
7393a248c84SMark Johnston ATF_TC_BODY(path_lock, tc)
7403a248c84SMark Johnston {
7413a248c84SMark Johnston 	char buf[BUFSIZ], path[PATH_MAX];
7423a248c84SMark Johnston 	struct flock flk;
7433a248c84SMark Johnston 	int fd, pathfd;
7443a248c84SMark Johnston 
7453a248c84SMark Johnston 	snprintf(path, sizeof(path), "path_rights.XXXXXX");
7463a248c84SMark Johnston 	fd = mkostemp(path, O_SHLOCK);
7473a248c84SMark Johnston 	ATF_REQUIRE_MSG(fd >= 0, FMT_ERR("mkostemp"));
7483a248c84SMark Johnston 	memset(buf, 0, sizeof(buf));
7493a248c84SMark Johnston 	ATF_REQUIRE_MSG(write(fd, buf, sizeof(buf)) == sizeof(buf),
7503a248c84SMark Johnston 	    FMT_ERR("write()"));
7513a248c84SMark Johnston 
7523a248c84SMark Johnston 	/* Verify that O_EXLOCK is ignored when combined with O_PATH. */
7533a248c84SMark Johnston 	pathfd = open(path, O_PATH | O_EXLOCK);
7543a248c84SMark Johnston 	ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
7553a248c84SMark Johnston 
7563a248c84SMark Johnston 	CHECKED_CLOSE(fd);
7573a248c84SMark Johnston 
7583a248c84SMark Johnston 	/* flock(2) is prohibited. */
7593a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EOPNOTSUPP, flock(pathfd, LOCK_SH) == -1);
7603a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EOPNOTSUPP, flock(pathfd, LOCK_EX) == -1);
7613a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EOPNOTSUPP, flock(pathfd, LOCK_SH | LOCK_NB) == -1);
7623a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EOPNOTSUPP, flock(pathfd, LOCK_EX | LOCK_NB) == -1);
7633a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EOPNOTSUPP, flock(pathfd, LOCK_UN) == -1);
7643a248c84SMark Johnston 
7653a248c84SMark Johnston 	/* fcntl(2) file locks are prohibited. */
7663a248c84SMark Johnston 	memset(&flk, 0, sizeof(flk));
7673a248c84SMark Johnston 	flk.l_whence = SEEK_CUR;
7683a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, fcntl(pathfd, F_GETLK, &flk) == -1);
7693a248c84SMark Johnston 	flk.l_type = F_RDLCK;
7703a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, fcntl(pathfd, F_SETLK, &flk) == -1);
7713a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, fcntl(pathfd, F_SETLKW, &flk) == -1);
7723a248c84SMark Johnston 	flk.l_type = F_WRLCK;
7733a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, fcntl(pathfd, F_SETLK, &flk) == -1);
7743a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF, fcntl(pathfd, F_SETLKW, &flk) == -1);
7753a248c84SMark Johnston 
7763a248c84SMark Johnston 	CHECKED_CLOSE(pathfd);
7773a248c84SMark Johnston }
7783a248c84SMark Johnston 
7793a248c84SMark Johnston /* Verify that we can send an O_PATH descriptor over a unix socket. */
7803a248c84SMark Johnston ATF_TC_WITHOUT_HEAD(path_rights);
7813a248c84SMark Johnston ATF_TC_BODY(path_rights, tc)
7823a248c84SMark Johnston {
7833a248c84SMark Johnston 	char path[PATH_MAX];
7843a248c84SMark Johnston 	struct cmsghdr *cmsg;
7853a248c84SMark Johnston 	struct msghdr msg;
7863a248c84SMark Johnston 	struct iovec iov;
7873a248c84SMark Johnston 	int flags, pathfd, pathfd_copy, sd[2];
7883a248c84SMark Johnston 	char c;
7893a248c84SMark Johnston 
7903a248c84SMark Johnston 	ATF_REQUIRE_MSG(socketpair(PF_LOCAL, SOCK_STREAM, 0, sd) == 0,
7913a248c84SMark Johnston 	    FMT_ERR("socketpair"));
7923a248c84SMark Johnston 
7933a248c84SMark Johnston 	mktfile(path, "path_rights.XXXXXX");
7943a248c84SMark Johnston 
7953a248c84SMark Johnston 	pathfd = open(path, O_PATH);
7963a248c84SMark Johnston 	ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
7973a248c84SMark Johnston 
7983a248c84SMark Johnston 	/* Package up the O_PATH and send it over the socket pair. */
7993a248c84SMark Johnston 	cmsg = malloc(CMSG_SPACE(sizeof(pathfd)));
8003a248c84SMark Johnston 	ATF_REQUIRE_MSG(cmsg != NULL, FMT_ERR("malloc"));
8013a248c84SMark Johnston 
8023a248c84SMark Johnston 	cmsg->cmsg_len = CMSG_LEN(sizeof(pathfd));
8033a248c84SMark Johnston 	cmsg->cmsg_level = SOL_SOCKET;
8043a248c84SMark Johnston 	cmsg->cmsg_type = SCM_RIGHTS;
8053a248c84SMark Johnston 	*(int *)(void *)CMSG_DATA(cmsg) = pathfd;
8063a248c84SMark Johnston 
8073a248c84SMark Johnston 	c = 0;
8083a248c84SMark Johnston 	iov.iov_base = &c;
8093a248c84SMark Johnston 	iov.iov_len = 1;
8103a248c84SMark Johnston 
8113a248c84SMark Johnston 	memset(&msg, 0, sizeof(msg));
8123a248c84SMark Johnston 	msg.msg_iov = &iov;
8133a248c84SMark Johnston 	msg.msg_iovlen = 1;
8143a248c84SMark Johnston 	msg.msg_control = cmsg;
8153a248c84SMark Johnston 	msg.msg_controllen = CMSG_SPACE(sizeof(pathfd));
8163a248c84SMark Johnston 
8173a248c84SMark Johnston 	ATF_REQUIRE_MSG(sendmsg(sd[0], &msg, 0) == sizeof(c),
8183a248c84SMark Johnston 	    FMT_ERR("sendmsg"));
8193a248c84SMark Johnston 
8203a248c84SMark Johnston 	/* Grab the pathfd copy from the other end of the pair. */
8213a248c84SMark Johnston 	memset(&msg, 0, sizeof(msg));
8223a248c84SMark Johnston 	msg.msg_iov = &iov;
8233a248c84SMark Johnston 	msg.msg_iovlen = 1;
8243a248c84SMark Johnston 	msg.msg_control = cmsg;
8253a248c84SMark Johnston 	msg.msg_controllen = CMSG_SPACE(sizeof(pathfd));
8263a248c84SMark Johnston 
8273a248c84SMark Johnston 	ATF_REQUIRE_MSG(recvmsg(sd[1], &msg, 0) == 1,
8283a248c84SMark Johnston 	    FMT_ERR("recvmsg"));
8293a248c84SMark Johnston 	pathfd_copy = *(int *)(void *)CMSG_DATA(cmsg);
8303a248c84SMark Johnston 	ATF_REQUIRE_MSG(pathfd_copy != pathfd,
8313a248c84SMark Johnston 	    "pathfd and pathfd_copy are equal");
8323a248c84SMark Johnston 
8333a248c84SMark Johnston 	/* Verify that the copy has O_PATH properties. */
8343a248c84SMark Johnston 	flags = fcntl(pathfd_copy, F_GETFL);
8353a248c84SMark Johnston 	ATF_REQUIRE_MSG(flags != -1, FMT_ERR("fcntl"));
8363a248c84SMark Johnston 	ATF_REQUIRE_MSG((flags & O_PATH) != 0, "O_PATH is not set");
8373a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF,
8383a248c84SMark Johnston 	    read(pathfd_copy, &c, 1) == -1);
8393a248c84SMark Johnston 	ATF_REQUIRE_ERRNO(EBADF,
8403a248c84SMark Johnston 	    write(pathfd_copy, &c, 1) == -1);
8413a248c84SMark Johnston 
8423a248c84SMark Johnston 	CHECKED_CLOSE(pathfd);
8433a248c84SMark Johnston 	CHECKED_CLOSE(pathfd_copy);
8443a248c84SMark Johnston 	CHECKED_CLOSE(sd[0]);
8453a248c84SMark Johnston 	CHECKED_CLOSE(sd[1]);
8463a248c84SMark Johnston }
8473a248c84SMark Johnston 
848b59851e9SMark Johnston /* Verify that a local socket can't be opened with O_PATH. */
849b59851e9SMark Johnston ATF_TC_WITHOUT_HEAD(path_unix);
850b59851e9SMark Johnston ATF_TC_BODY(path_unix, tc)
851b59851e9SMark Johnston {
852b59851e9SMark Johnston 	char path[PATH_MAX];
853b59851e9SMark Johnston 	struct sockaddr_un sun;
854b59851e9SMark Johnston 	int pathfd, sd;
855b59851e9SMark Johnston 
856b59851e9SMark Johnston 	snprintf(path, sizeof(path), "path_unix.XXXXXX");
857b59851e9SMark Johnston 	ATF_REQUIRE_MSG(mktemp(path) == path, FMT_ERR("mktemp"));
858b59851e9SMark Johnston 
859b59851e9SMark Johnston 	sd = socket(PF_LOCAL, SOCK_STREAM, 0);
860b59851e9SMark Johnston 	ATF_REQUIRE_MSG(sd >= 0, FMT_ERR("socket"));
861b59851e9SMark Johnston 
862b59851e9SMark Johnston 	memset(&sun, 0, sizeof(sun));
863b59851e9SMark Johnston 	sun.sun_family = PF_LOCAL;
864b59851e9SMark Johnston 	(void)strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
865b59851e9SMark Johnston 	ATF_REQUIRE_MSG(bind(sd, (struct sockaddr *)&sun, SUN_LEN(&sun)) == 0,
866b59851e9SMark Johnston 	    FMT_ERR("bind"));
867b59851e9SMark Johnston 
868*8b83b656SMark Johnston 	pathfd = open(path, O_PATH);
869b59851e9SMark Johnston 	ATF_REQUIRE_ERRNO(EOPNOTSUPP, pathfd < 0);
870b59851e9SMark Johnston 
871b59851e9SMark Johnston 	CHECKED_CLOSE(sd);
872b59851e9SMark Johnston }
873b59851e9SMark Johnston 
8743a248c84SMark Johnston ATF_TP_ADD_TCS(tp)
8753a248c84SMark Johnston {
8763a248c84SMark Johnston 	ATF_TP_ADD_TC(tp, path_access);
8773a248c84SMark Johnston 	ATF_TP_ADD_TC(tp, path_aio);
8783a248c84SMark Johnston 	ATF_TP_ADD_TC(tp, path_capsicum);
879b59851e9SMark Johnston 	ATF_TP_ADD_TC(tp, path_coredump);
8803a248c84SMark Johnston 	ATF_TP_ADD_TC(tp, path_directory);
8813a248c84SMark Johnston 	ATF_TP_ADD_TC(tp, path_directory_not_root);
8823a248c84SMark Johnston 	ATF_TP_ADD_TC(tp, path_empty);
8833a248c84SMark Johnston 	ATF_TP_ADD_TC(tp, path_empty_not_root);
8843a248c84SMark Johnston 	ATF_TP_ADD_TC(tp, path_empty_root);
8853a248c84SMark Johnston 	ATF_TP_ADD_TC(tp, path_event);
8863a248c84SMark Johnston 	ATF_TP_ADD_TC(tp, path_fcntl);
8873a248c84SMark Johnston 	ATF_TP_ADD_TC(tp, path_fexecve);
888b59851e9SMark Johnston 	ATF_TP_ADD_TC(tp, path_fifo);
8893a248c84SMark Johnston 	ATF_TP_ADD_TC(tp, path_funlinkat);
8903a248c84SMark Johnston 	ATF_TP_ADD_TC(tp, path_io);
8913a248c84SMark Johnston 	ATF_TP_ADD_TC(tp, path_ioctl);
8923a248c84SMark Johnston 	ATF_TP_ADD_TC(tp, path_lock);
8933a248c84SMark Johnston 	ATF_TP_ADD_TC(tp, path_rights);
894b59851e9SMark Johnston 	ATF_TP_ADD_TC(tp, path_unix);
895b42df9daSKonstantin Belousov 	ATF_TP_ADD_TC(tp, path_pipe_fstatat);
8963a248c84SMark Johnston 
8973a248c84SMark Johnston 	return (atf_no_error());
8983a248c84SMark Johnston }
899