aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cddl/contrib/opensolaris/tests/os-tests/tests/oclo/oclo.c1341
-rw-r--r--cddl/contrib/opensolaris/tests/os-tests/tests/oclo/oclo_errors.c202
-rw-r--r--cddl/contrib/opensolaris/tests/os-tests/tests/oclo/ocloexec_verify.c154
-rw-r--r--lib/lib80211/regdomain.xml44
-rw-r--r--lib/libc/gen/dup3.315
-rw-r--r--lib/libc/gen/dup3.c9
-rw-r--r--lib/libopenbsd/Makefile3
-rw-r--r--lib/libopenbsd/recallocarray.c82
-rw-r--r--lib/libsys/accept.214
-rw-r--r--lib/libsys/closefrom.29
-rw-r--r--lib/libsys/execve.27
-rw-r--r--lib/libsys/fcntl.243
-rw-r--r--lib/libsys/fork.212
-rw-r--r--lib/libsys/open.229
-rw-r--r--lib/libsys/pipe.29
-rw-r--r--lib/libsys/recv.23
-rw-r--r--lib/libsys/socket.213
-rw-r--r--lib/libsys/socketpair.25
-rw-r--r--lib/libsysdecode/flags.c13
-rw-r--r--lib/libsysdecode/sysdecode_fcntl_arg.33
-rw-r--r--sbin/routed/routed.89
-rw-r--r--sbin/routed/rtquery/rtquery.87
-rw-r--r--sys/amd64/amd64/efirt_machdep.c2
-rw-r--r--sys/amd64/amd64/machdep.c6
-rw-r--r--sys/amd64/amd64/pmap.c12
-rw-r--r--sys/compat/freebsd32/freebsd32_sysent.c2
-rw-r--r--sys/fs/nfs/nfs_commonsubs.c87
-rw-r--r--sys/fs/nfs/nfsproto.h8
-rw-r--r--sys/fs/nfsclient/nfs_clrpcops.c7
-rw-r--r--sys/fs/nfsclient/nfs_clvnops.c24
-rw-r--r--sys/fs/nfsserver/nfs_nfsdport.c20
-rw-r--r--sys/fs/nfsserver/nfs_nfsdserv.c33
-rw-r--r--sys/kern/init_sysent.c2
-rw-r--r--sys/kern/kern_descrip.c57
-rw-r--r--sys/kern/sys_pipe.c2
-rw-r--r--sys/kern/uipc_syscalls.c13
-rw-r--r--sys/kern/uipc_usrreq.c3
-rw-r--r--sys/sys/fcntl.h17
-rw-r--r--sys/sys/filedesc.h2
-rw-r--r--sys/sys/socket.h6
-rw-r--r--sys/sys/unistd.h1
-rw-r--r--tests/Makefile5
-rw-r--r--tests/oclo/Makefile11
-rw-r--r--tests/sys/file/closefrom_test.c35
-rw-r--r--tests/sys/file/dup_test.c98
-rw-r--r--tests/sys/kern/unix_passfd_test.c25
-rw-r--r--usr.sbin/rip6query/rip6query.88
-rw-r--r--usr.sbin/route6d/route6d.87
48 files changed, 2420 insertions, 99 deletions
diff --git a/cddl/contrib/opensolaris/tests/os-tests/tests/oclo/oclo.c b/cddl/contrib/opensolaris/tests/os-tests/tests/oclo/oclo.c
new file mode 100644
index 000000000000..8e6f7c726f24
--- /dev/null
+++ b/cddl/contrib/opensolaris/tests/os-tests/tests/oclo/oclo.c
@@ -0,0 +1,1341 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2025 Oxide Computer Company
+ */
+
+/*
+ * Verify the behavior of the various O_CLOFORK and O_CLOEXEC variants. In
+ * particular getting this via:
+ *
+ * - open(2): O_CLOFORK/O_CLOEXEC
+ * - fcntl(2): F_SETFD FD_CLOFORK/FD_CLOEXEC
+ * - fcntl(2): F_DUPFD_CLOFORK/F_DUPFD_CLOEXEC
+ * - fcntl(2): F_DUP2FD_CLOFORK/F_DUP2FD_CLOEXEC
+ * - dup2(3C)
+ * - dup3(3C): argument translation
+ * - pipe2(2)
+ * - socket(2): SOCK_CLOEXEC/SOCK_CLOFORK
+ * - accept(2): flags on the listen socket aren't inherited on accept
+ * - socketpair(3SOCKET)
+ * - accept4(2): SOCK_CLOEXEC/SOCK_CLOFORK
+ * - recvmsg(2): SCM_RIGHTS MSG_CMSG_CLOFORK/MSG_CMSG_CLOEXEC
+ *
+ * The test is designed such that we have an array of functions that are used to
+ * create file descriptors with different rules. This is found in the
+ * oclo_create array. Each file descriptor that is created is then registered
+ * with information about what is expected about it. A given creation function
+ * can create more than one file descriptor; however, our expectation is that
+ * every file descriptor is accounted for (ignoring stdin, stdout, and stderr).
+ *
+ * We pass a record of each file descriptor that was recorded to a verification
+ * program that will verify everything is correctly honored after an exec. Note
+ * that O_CLOFORK is cleared after exec. The original specification in POSIX has
+ * it being retained; however, this issue was raised after the spec was
+ * published as folks went to implement it and we have ended up following along
+ * with the divergence of other implementations.
+ */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void *recallocarray(void *, size_t, size_t, size_t);
+
+#define strerrorname_np(e) (sys_errlist[e])
+
+/*
+ * Get pathname to avoid reading /proc/curproc/exe
+ *
+ * Taken from procstat_getpathname_sysctl()
+ */
+static int
+getpathname(pid_t pid, char *pathname, size_t maxlen)
+{
+ int error, name[4];
+ size_t len;
+
+ name[0] = CTL_KERN;
+ name[1] = KERN_PROC;
+ name[2] = KERN_PROC_PATHNAME;
+ name[3] = pid;
+ len = maxlen;
+ error = sysctl(name, nitems(name), pathname, &len, NULL, 0);
+ if (error != 0 && errno != ESRCH)
+ warn("sysctl: kern.proc.pathname: %d", pid);
+ if (len == 0)
+ pathname[0] = '\0';
+ return (error);
+}
+
+/*
+ * Verification program name.
+ */
+#define OCLO_VERIFY "ocloexec_verify"
+
+/*
+ * This structure represents a table of ways we expect to create file
+ * descriptors that should have the resulting flags set when done. The table is
+ * ordered and subsequent iterations are allowed to assume that the ones that
+ * have gone ahead of them have run and are therefore allowed to access them.
+ * The create function is expected to return the created fd.
+ */
+typedef struct clo_create clo_create_t;
+struct clo_create {
+ const char *clo_desc;
+ int clo_flags;
+ void (*clo_func)(const clo_create_t *);
+};
+
+/*
+ * This is our run-time data. We expect all file descriptors to be registered by
+ * our calling functions through oclo_record().
+ */
+typedef struct clo_rtdata {
+ const clo_create_t *crt_data;
+ size_t crt_idx;
+ int crt_fd;
+ int crt_flags;
+ const char *crt_desc;
+} clo_rtdata_t;
+
+static clo_rtdata_t *oclo_rtdata;
+static size_t oclo_rtdata_nents = 0;
+static size_t oclo_rtdata_next = 0;
+static int oclo_nextfd = STDERR_FILENO + 1;
+
+static bool
+oclo_flags_match(const clo_rtdata_t *rt, bool child)
+{
+ const char *pass = child ? "post-fork" : "pre-fork";
+ bool fail = child && (rt->crt_flags & FD_CLOFORK) != 0;
+ int flags = fcntl(rt->crt_fd, F_GETFD, NULL);
+
+ if (flags < 0) {
+ int e = errno;
+
+ if (fail) {
+ if (e == EBADF) {
+ (void) printf("TEST PASSED: %s (%s): fd %d: "
+ "correctly closed\n",
+ rt->crt_data->clo_desc, pass, rt->crt_fd);
+ return (true);
+ }
+
+ warn("TEST FAILED: %s (%s): fd %d: expected fcntl to "
+ "fail with EBADF, but found %s",
+ rt->crt_data->clo_desc, pass, rt->crt_fd,
+ strerrorname_np(e));
+ return (false);
+ }
+
+ warnx("TEST FAILED: %s (%s): fd %d: fcntl(F_GETFD) "
+ "unexpectedly failed", rt->crt_data->clo_desc, pass,
+ rt->crt_fd);
+ return (false);
+ }
+
+ if (fail) {
+ warnx("TEST FAILED: %s (%s): fd %d: received flags %d, but "
+ "expected to fail based on flags %d",
+ rt->crt_data->clo_desc, pass, rt->crt_fd, flags,
+ rt->crt_fd);
+ return (false);
+ }
+
+ if (flags != rt->crt_flags) {
+ warnx("TEST FAILED: %s (%s): fd %d: discovered flags 0x%x do "
+ "not match expected flags 0x%x", rt->crt_data->clo_desc,
+ pass, rt->crt_fd, flags, rt->crt_fd);
+ return (false);
+ }
+
+ (void) printf("TEST PASSED: %s (%s): fd %d discovered flags match "
+ "(0x%x)\n", rt->crt_data->clo_desc, pass, rt->crt_fd, flags);
+ return (true);
+}
+
+
+static void
+oclo_record(const clo_create_t *c, int fd, int exp_flags, const char *desc)
+{
+ if (oclo_rtdata_next == oclo_rtdata_nents) {
+ size_t newrt = oclo_rtdata_nents + 8;
+ clo_rtdata_t *rt;
+ rt = recallocarray(oclo_rtdata, oclo_rtdata_nents, newrt,
+ sizeof (clo_rtdata_t));
+ if (rt == NULL) {
+ err(EXIT_FAILURE, "TEST_FAILED: internal error "
+ "expanding fd records to %zu entries", newrt);
+ }
+
+ oclo_rtdata_nents = newrt;
+ oclo_rtdata = rt;
+ }
+
+ if (fd != oclo_nextfd) {
+ errx(EXIT_FAILURE, "TEST FAILED: internal test error: expected "
+ "to record next fd %d, given %d", oclo_nextfd, fd);
+ }
+
+ oclo_rtdata[oclo_rtdata_next].crt_data = c;
+ oclo_rtdata[oclo_rtdata_next].crt_fd = fd;
+ oclo_rtdata[oclo_rtdata_next].crt_flags = exp_flags;
+ oclo_rtdata[oclo_rtdata_next].crt_desc = desc;
+
+ /*
+ * Matching errors at this phase are fatal as it means we screwed up the
+ * program pretty badly.
+ */
+ if (!oclo_flags_match(&oclo_rtdata[oclo_rtdata_next], false)) {
+ exit(EXIT_FAILURE);
+ }
+
+ oclo_rtdata_next++;
+ oclo_nextfd++;
+}
+
+static int
+oclo_file(const clo_create_t *c)
+{
+ int flags = O_RDWR, fd;
+
+ if ((c->clo_flags & FD_CLOEXEC) != 0)
+ flags |= O_CLOEXEC;
+ if ((c->clo_flags & FD_CLOFORK) != 0)
+ flags |= O_CLOFORK;
+ fd = open("/dev/null", flags);
+ if (fd < 0) {
+ err(EXIT_FAILURE, "TEST FAILED: %s: failed to open /dev/null",
+ c->clo_desc);
+ }
+
+ return (fd);
+}
+
+static void
+oclo_open(const clo_create_t *c)
+{
+ oclo_record(c, oclo_file(c), c->clo_flags, NULL);
+}
+
+static void
+oclo_setfd_common(const clo_create_t *c, int targ_flags)
+{
+ int fd = oclo_file(c);
+ if (fcntl(fd, F_SETFD, targ_flags) < 0) {
+ err(EXIT_FAILURE, "TEST FAILED: %s: F_SETFD failed to set "
+ "flags to %d", c->clo_desc, targ_flags);
+ }
+
+ oclo_record(c, fd, targ_flags, NULL);
+}
+
+static void
+oclo_setfd_none(const clo_create_t *c)
+{
+ oclo_setfd_common(c, 0);
+}
+
+static void
+oclo_setfd_exec(const clo_create_t *c)
+{
+ oclo_setfd_common(c, FD_CLOEXEC);
+}
+
+static void
+oclo_setfd_fork(const clo_create_t *c)
+{
+ oclo_setfd_common(c, FD_CLOFORK);
+}
+
+static void
+oclo_setfd_both(const clo_create_t *c)
+{
+ oclo_setfd_common(c, FD_CLOFORK | FD_CLOEXEC);
+}
+
+/*
+ * Open an fd with flags in a certain form and then use one of the F_DUPFD or
+ * F_DUP2FD variants and ensure that flags are properly propagated as expected.
+ */
+static void
+oclo_fdup_common(const clo_create_t *c, int targ_flags, int cmd)
+{
+ int dup, fd;
+
+ fd = oclo_file(c);
+ oclo_record(c, fd, c->clo_flags, "base");
+ switch (cmd) {
+ case F_DUPFD:
+ case F_DUPFD_CLOEXEC:
+ case F_DUPFD_CLOFORK:
+ dup = fcntl(fd, cmd, fd);
+ break;
+ case F_DUP2FD:
+ case F_DUP2FD_CLOEXEC:
+#ifdef F_DUP2FD_CLOFORK
+ case F_DUP2FD_CLOFORK:
+#endif
+ dup = fcntl(fd, cmd, fd + 1);
+ break;
+ case F_DUP3FD:
+ dup = fcntl(fd, cmd | (targ_flags << F_DUP3FD_SHIFT), fd + 1);
+ break;
+ default:
+ errx(EXIT_FAILURE, "TEST FAILURE: %s: internal error: "
+ "unexpected fcntl cmd: 0x%x", c->clo_desc, cmd);
+ }
+
+ if (dup < 0) {
+ err(EXIT_FAILURE, "TEST FAILURE: %s: failed to dup fd with "
+ "fcntl command 0x%x", c->clo_desc, cmd);
+ }
+
+ oclo_record(c, dup, targ_flags, "dup");
+}
+
+static void
+oclo_fdupfd(const clo_create_t *c)
+{
+ oclo_fdup_common(c, 0, F_DUPFD);
+}
+
+static void
+oclo_fdupfd_fork(const clo_create_t *c)
+{
+ oclo_fdup_common(c, FD_CLOFORK, F_DUPFD_CLOFORK);
+}
+
+static void
+oclo_fdupfd_exec(const clo_create_t *c)
+{
+ oclo_fdup_common(c, FD_CLOEXEC, F_DUPFD_CLOEXEC);
+}
+
+static void
+oclo_fdup2fd(const clo_create_t *c)
+{
+ oclo_fdup_common(c, 0, F_DUP2FD);
+}
+
+#ifdef F_DUP2FD_CLOFORK
+static void
+oclo_fdup2fd_fork(const clo_create_t *c)
+{
+ oclo_fdup_common(c, FD_CLOFORK, F_DUP2FD_CLOFORK);
+}
+#endif
+
+static void
+oclo_fdup2fd_exec(const clo_create_t *c)
+{
+ oclo_fdup_common(c, FD_CLOEXEC, F_DUP2FD_CLOEXEC);
+}
+
+static void
+oclo_fdup3fd_none(const clo_create_t *c)
+{
+ oclo_fdup_common(c, 0, F_DUP3FD);
+}
+
+static void
+oclo_fdup3fd_exec(const clo_create_t *c)
+{
+ oclo_fdup_common(c, FD_CLOEXEC, F_DUP3FD);
+}
+
+static void
+oclo_fdup3fd_fork(const clo_create_t *c)
+{
+ oclo_fdup_common(c, FD_CLOFORK, F_DUP3FD);
+}
+
+static void
+oclo_fdup3fd_both(const clo_create_t *c)
+{
+ oclo_fdup_common(c, FD_CLOEXEC | FD_CLOFORK, F_DUP3FD);
+}
+
+static void
+oclo_dup_common(const clo_create_t *c, int targ_flags, bool v3)
+{
+ int dup, fd;
+ fd = oclo_file(c);
+ oclo_record(c, fd, c->clo_flags, "base");
+ if (v3) {
+ int dflags = 0;
+ if ((targ_flags & FD_CLOEXEC) != 0)
+ dflags |= O_CLOEXEC;
+ if ((targ_flags & FD_CLOFORK) != 0)
+ dflags |= O_CLOFORK;
+ dup = dup3(fd, fd + 1, dflags);
+ } else {
+ dup = dup2(fd, fd + 1);
+ }
+
+ oclo_record(c, dup, targ_flags, "dup");
+}
+
+static void
+oclo_dup2(const clo_create_t *c)
+{
+ oclo_dup_common(c, 0, false);
+}
+
+static void
+oclo_dup3_none(const clo_create_t *c)
+{
+ oclo_dup_common(c, 0, true);
+}
+
+static void
+oclo_dup3_exec(const clo_create_t *c)
+{
+ oclo_dup_common(c, FD_CLOEXEC, true);
+}
+
+static void
+oclo_dup3_fork(const clo_create_t *c)
+{
+ oclo_dup_common(c, FD_CLOFORK, true);
+}
+
+static void
+oclo_dup3_both(const clo_create_t *c)
+{
+ oclo_dup_common(c, FD_CLOEXEC | FD_CLOFORK, true);
+}
+
+static void
+oclo_pipe(const clo_create_t *c)
+{
+ int flags = 0, fds[2];
+
+ if ((c->clo_flags & FD_CLOEXEC) != 0)
+ flags |= O_CLOEXEC;
+ if ((c->clo_flags & FD_CLOFORK) != 0)
+ flags |= O_CLOFORK;
+
+ if (pipe2(fds, flags) < 0) {
+ err(EXIT_FAILURE, "TEST FAILED: %s: pipe2() with flags %d "
+ "failed", c->clo_desc, flags);
+ }
+
+ oclo_record(c, fds[0], c->clo_flags, "pipe[0]");
+ oclo_record(c, fds[1], c->clo_flags, "pipe[1]");
+}
+
+static void
+oclo_socket(const clo_create_t *c)
+{
+ int type = SOCK_DGRAM, fd;
+
+ if ((c->clo_flags & FD_CLOEXEC) != 0)
+ type |= SOCK_CLOEXEC;
+ if ((c->clo_flags & FD_CLOFORK) != 0)
+ type |= SOCK_CLOFORK;
+ fd = socket(PF_INET, type, 0);
+ if (fd < 0) {
+ err(EXIT_FAILURE, "TEST FAILED: %s: failed to create socket "
+ "with flags: 0x%x\n", c->clo_desc, c->clo_flags);
+ }
+
+ oclo_record(c, fd, c->clo_flags, NULL);
+}
+
+static void
+oclo_accept_common(const clo_create_t *c, int targ_flags, bool a4)
+{
+ int lsock, csock, asock;
+ int ltype = SOCK_STREAM, atype = 0;
+ struct sockaddr_in in;
+ socklen_t slen;
+
+ if ((c->clo_flags & FD_CLOEXEC) != 0)
+ ltype |= SOCK_CLOEXEC;
+ if ((c->clo_flags & FD_CLOFORK) != 0)
+ ltype |= SOCK_CLOFORK;
+
+ if ((targ_flags & FD_CLOEXEC) != 0)
+ atype |= SOCK_CLOEXEC;
+ if ((targ_flags & FD_CLOFORK) != 0)
+ atype |= SOCK_CLOFORK;
+
+ lsock = socket(PF_INET, ltype, 0);
+ if (lsock < 0) {
+ err(EXIT_FAILURE, "TEST FAILED: %s: failed to create listen "
+ "socket with flags: 0x%x\n", c->clo_desc, c->clo_flags);
+ }
+
+ oclo_record(c, lsock, c->clo_flags, "listen");
+ (void) memset(&in, 0, sizeof (in));
+ in.sin_family = AF_INET;
+ in.sin_port = 0;
+ in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ if (bind(lsock, (struct sockaddr *)&in, sizeof (in)) != 0) {
+ err(EXIT_FAILURE, "TEST FAILED: %s: failed to bind socket",
+ c->clo_desc);
+ }
+
+ slen = sizeof (struct sockaddr_in);
+ if (getsockname(lsock, (struct sockaddr *)&in, &slen) != 0) {
+ err(EXIT_FAILURE, "TEST FAILED: %s: failed to discover bound "
+ "socket address", c->clo_desc);
+ }
+
+ if (listen(lsock, 5) < 0) {
+ err(EXIT_FAILURE, "TEST FAILED: %s: failed to listen on socket",
+ c->clo_desc);
+ }
+
+ csock = socket(PF_INET, SOCK_STREAM, 0);
+ if (csock < 0) {
+ err(EXIT_FAILURE, "TEST FAILED: %s: failed to create client "
+ "socket", c->clo_desc);
+ }
+ oclo_record(c, csock, 0, "connect");
+
+ if (connect(csock, (struct sockaddr *)&in, sizeof (in)) != 0) {
+ err(EXIT_FAILURE, "TEST FAILED: %s: failed to connect to "
+ "server socket", c->clo_desc);
+ }
+
+ if (a4) {
+ asock = accept4(lsock, NULL, NULL, atype);
+ } else {
+ asock = accept(lsock, NULL, NULL);
+ }
+ if (asock < 0) {
+ err(EXIT_FAILURE, "TEST FAILED: %s: failed to accept client "
+ "connection", c->clo_desc);
+ }
+ oclo_record(c, asock, targ_flags, "accept");
+}
+
+static void
+oclo_accept(const clo_create_t *c)
+{
+ oclo_accept_common(c, 0, false);
+}
+
+static void
+oclo_accept4_none(const clo_create_t *c)
+{
+ oclo_accept_common(c, 0, true);
+}
+
+static void
+oclo_accept4_fork(const clo_create_t *c)
+{
+ oclo_accept_common(c, FD_CLOFORK, true);
+}
+
+static void
+oclo_accept4_exec(const clo_create_t *c)
+{
+ oclo_accept_common(c, FD_CLOEXEC, true);
+}
+
+static void
+oclo_accept4_both(const clo_create_t *c)
+{
+ oclo_accept_common(c, FD_CLOEXEC | FD_CLOFORK, true);
+}
+
+/*
+ * Go through the process of sending ourselves a file descriptor.
+ */
+static void
+oclo_rights_common(const clo_create_t *c, int targ_flags)
+{
+ int pair[2], type = SOCK_DGRAM, sflags = 0;
+ int tosend = oclo_file(c), recvfd;
+ uint32_t data = 0x7777;
+ struct iovec iov;
+ struct msghdr msg;
+ struct cmsghdr *cm;
+
+ if ((c->clo_flags & FD_CLOEXEC) != 0)
+ type |= SOCK_CLOEXEC;
+ if ((c->clo_flags & FD_CLOFORK) != 0)
+ type |= SOCK_CLOFORK;
+
+ if (socketpair(PF_UNIX, type, 0, pair) < 0) {
+ err(EXIT_FAILURE, "TEST FAILED: %s: failed to create socket "
+ "pair", c->clo_desc);
+ }
+
+ oclo_record(c, tosend, c->clo_flags, "send fd");
+ oclo_record(c, pair[0], c->clo_flags, "pair[0]");
+ oclo_record(c, pair[1], c->clo_flags, "pair[1]");
+
+ iov.iov_base = (void *)&data;
+ iov.iov_len = sizeof (data);
+
+ (void) memset(&msg, 0, sizeof (msg));
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_controllen = CMSG_SPACE(sizeof (int));
+
+ msg.msg_control = calloc(1, msg.msg_controllen);
+ if (msg.msg_control == NULL) {
+ err(EXIT_FAILURE, "TEST FAILED: %s: failed to allocate %u "
+ "bytes for SCM_RIGHTS control message", c->clo_desc,
+ msg.msg_controllen);
+ }
+
+ cm = CMSG_FIRSTHDR(&msg);
+ cm->cmsg_len = CMSG_LEN(sizeof (int));
+ cm->cmsg_level = SOL_SOCKET;
+ cm->cmsg_type = SCM_RIGHTS;
+ (void) memcpy(CMSG_DATA(cm), &tosend, sizeof (tosend));
+
+ if ((targ_flags & FD_CLOEXEC) != 0)
+ sflags |= MSG_CMSG_CLOEXEC;
+ if ((targ_flags & FD_CLOFORK) != 0)
+ sflags |= MSG_CMSG_CLOFORK;
+
+ if (sendmsg(pair[0], &msg, 0) < 0) {
+ err(EXIT_FAILURE, "TEST FAILED: %s: failed to send fd",
+ c->clo_desc);
+ }
+
+ data = 0;
+ if (recvmsg(pair[1], &msg, sflags) < 0) {
+ err(EXIT_FAILURE, "TEST FAILED: %s: failed to get fd",
+ c->clo_desc);
+ }
+
+ if (data != 0x7777) {
+ errx(EXIT_FAILURE, "TEST FAILED: %s: did not receive correct "
+ "data: expected 0x7777, found 0x%x", c->clo_desc, data);
+ }
+
+ if (msg.msg_controllen < CMSG_SPACE(sizeof (int))) {
+ errx(EXIT_FAILURE, "TEST FAILED: %s: found insufficient "
+ "message control length: expected at least 0x%zx, found "
+ "0x%x", c->clo_desc, CMSG_SPACE(sizeof (int)),
+ msg.msg_controllen);
+ }
+
+ cm = CMSG_FIRSTHDR(&msg);
+ if (cm->cmsg_level != SOL_SOCKET || cm->cmsg_type != SCM_RIGHTS) {
+ errx(EXIT_FAILURE, "TEST FAILED: %s: found surprising cmsg "
+ "0x%x/0x%x, expected 0x%x/0x%x", c->clo_desc,
+ cm->cmsg_level, cm->cmsg_type, SOL_SOCKET, SCM_RIGHTS);
+ }
+
+ if (cm->cmsg_len != CMSG_LEN(sizeof (int))) {
+ errx(EXIT_FAILURE, "TEST FAILED: %s: found unexpected "
+ "SCM_RIGHTS length 0x%x: expected 0x%zx", c->clo_desc,
+ cm->cmsg_len, CMSG_LEN(sizeof (int)));
+ }
+
+ (void) memcpy(&recvfd, CMSG_DATA(cm), sizeof (recvfd));
+ oclo_record(c, recvfd, targ_flags, "SCM_RIGHTS");
+}
+
+static void
+oclo_rights_none(const clo_create_t *c)
+{
+ oclo_rights_common(c, 0);
+}
+
+static void
+oclo_rights_exec(const clo_create_t *c)
+{
+ oclo_rights_common(c, FD_CLOEXEC);
+}
+
+static void
+oclo_rights_fork(const clo_create_t *c)
+{
+ oclo_rights_common(c, FD_CLOFORK);
+}
+
+static void
+oclo_rights_both(const clo_create_t *c)
+{
+ oclo_rights_common(c, FD_CLOEXEC | FD_CLOFORK);
+}
+
+static const clo_create_t oclo_create[] = { {
+ .clo_desc = "open(2), no flags",
+ .clo_flags = 0,
+ .clo_func = oclo_open
+}, {
+ .clo_desc = "open(2), O_CLOEXEC",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_open
+}, {
+ .clo_desc = "open(2), O_CLOFORK",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_open
+}, {
+ .clo_desc = "open(2), O_CLOEXEC|O_CLOFORK",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_open
+}, {
+ .clo_desc = "fcntl(F_SETFD) no flags->no flags",
+ .clo_flags = 0,
+ .clo_func = oclo_setfd_none
+}, {
+ .clo_desc = "fcntl(F_SETFD) O_CLOFORK|O_CLOEXEC->no flags",
+ .clo_flags = O_CLOFORK | O_CLOEXEC,
+ .clo_func = oclo_setfd_none
+}, {
+ .clo_desc = "fcntl(F_SETFD) O_CLOEXEC->no flags",
+ .clo_flags = O_CLOEXEC,
+ .clo_func = oclo_setfd_none
+}, {
+ .clo_desc = "fcntl(F_SETFD) O_CLOFORK->no flags",
+ .clo_flags = O_CLOFORK,
+ .clo_func = oclo_setfd_none
+}, {
+ .clo_desc = "fcntl(F_SETFD) no flags->O_CLOEXEC",
+ .clo_flags = 0,
+ .clo_func = oclo_setfd_exec
+}, {
+ .clo_desc = "fcntl(F_SETFD) O_CLOFORK|O_CLOEXEC->O_CLOEXEC",
+ .clo_flags = O_CLOFORK | O_CLOEXEC,
+ .clo_func = oclo_setfd_exec
+}, {
+ .clo_desc = "fcntl(F_SETFD) O_CLOEXEC->O_CLOEXEC",
+ .clo_flags = O_CLOEXEC,
+ .clo_func = oclo_setfd_exec
+}, {
+ .clo_desc = "fcntl(F_SETFD) O_CLOFORK->O_CLOEXEC",
+ .clo_flags = O_CLOFORK,
+ .clo_func = oclo_setfd_exec
+}, {
+ .clo_desc = "fcntl(F_SETFD) no flags->O_CLOFORK",
+ .clo_flags = 0,
+ .clo_func = oclo_setfd_fork
+}, {
+ .clo_desc = "fcntl(F_SETFD) O_CLOFORK|O_CLOEXEC->O_CLOFORK",
+ .clo_flags = O_CLOFORK | O_CLOEXEC,
+ .clo_func = oclo_setfd_fork
+}, {
+ .clo_desc = "fcntl(F_SETFD) O_CLOEXEC->O_CLOFORK",
+ .clo_flags = O_CLOEXEC,
+ .clo_func = oclo_setfd_fork
+}, {
+ .clo_desc = "fcntl(F_SETFD) O_CLOFORK->O_CLOFORK",
+ .clo_flags = O_CLOFORK,
+ .clo_func = oclo_setfd_fork
+}, {
+ .clo_desc = "fcntl(F_SETFD) no flags->O_CLOFORK|O_CLOEXEC",
+ .clo_flags = 0,
+ .clo_func = oclo_setfd_both
+}, {
+ .clo_desc = "fcntl(F_SETFD) O_CLOFORK|O_CLOEXEC->O_CLOFORK|O_CLOEXEC",
+ .clo_flags = O_CLOFORK | O_CLOEXEC,
+ .clo_func = oclo_setfd_both
+}, {
+ .clo_desc = "fcntl(F_SETFD) O_CLOEXEC->O_CLOFORK|O_CLOEXEC",
+ .clo_flags = O_CLOEXEC,
+ .clo_func = oclo_setfd_both
+}, {
+ .clo_desc = "fcntl(F_SETFD) O_CLOFORK->O_CLOFORK|O_CLOEXEC",
+ .clo_flags = O_CLOFORK,
+ .clo_func = oclo_setfd_both
+}, {
+ .clo_desc = "fcntl(F_DUPFD) none->none",
+ .clo_flags = 0,
+ .clo_func = oclo_fdupfd
+}, {
+ .clo_desc = "fcntl(F_DUPFD) FD_CLOEXEC->none",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_fdupfd
+}, {
+ .clo_desc = "fcntl(F_DUPFD) FD_CLOFORK->none",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_fdupfd
+}, {
+ .clo_desc = "fcntl(F_DUPFD) FD_CLOEXEC|FD_CLOFORK->none",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_fdupfd
+}, {
+ .clo_desc = "fcntl(F_DUPFD_CLOFORK) none",
+ .clo_flags = 0,
+ .clo_func = oclo_fdupfd_fork
+}, {
+ .clo_desc = "fcntl(F_DUPFD_CLOFORK) FD_CLOEXEC",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_fdupfd_fork
+}, {
+ .clo_desc = "fcntl(F_DUPFD_CLOFORK) FD_CLOFORK",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_fdupfd_fork
+}, {
+ .clo_desc = "fcntl(F_DUPFD_CLOFORK) FD_CLOEXEC|FD_CLOFORK",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_fdupfd_fork
+}, {
+ .clo_desc = "fcntl(F_DUPFD_CLOEXEC) none",
+ .clo_flags = 0,
+ .clo_func = oclo_fdupfd_exec
+}, {
+ .clo_desc = "fcntl(F_DUPFD_CLOEXEC) FD_CLOEXEC",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_fdupfd_exec
+}, {
+ .clo_desc = "fcntl(F_DUPFD_CLOEXEC) FD_CLOFORK",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_fdupfd_exec
+}, {
+ .clo_desc = "fcntl(F_DUPFD_CLOEXEC) FD_CLOEXEC|FD_CLOFORK",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_fdupfd_exec
+}, {
+ .clo_desc = "fcntl(F_DUP2FD) none->none",
+ .clo_flags = 0,
+ .clo_func = oclo_fdup2fd
+}, {
+ .clo_desc = "fcntl(F_DUP2FD) FD_CLOEXEC->none",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_fdup2fd
+}, {
+ .clo_desc = "fcntl(F_DUP2FD) FD_CLOFORK->none",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_fdup2fd
+}, {
+ .clo_desc = "fcntl(F_DUP2FD) FD_CLOEXEC|FD_CLOFORK->none",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_fdup2fd
+}, {
+#ifdef F_DUP2FD_CLOFORK
+ .clo_desc = "fcntl(F_DUP2FD_CLOFORK) none",
+ .clo_flags = 0,
+ .clo_func = oclo_fdup2fd_fork
+}, {
+ .clo_desc = "fcntl(F_DUP2FD_CLOFORK) FD_CLOEXEC",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_fdup2fd_fork
+}, {
+ .clo_desc = "fcntl(F_DUP2FD_CLOFORK) FD_CLOFORK",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_fdup2fd_fork
+}, {
+ .clo_desc = "fcntl(F_DUP2FD_CLOFORK) FD_CLOEXEC|FD_CLOFORK",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_fdup2fd_fork
+}, {
+#endif
+ .clo_desc = "fcntl(F_DUP2FD_CLOEXEC) none",
+ .clo_flags = 0,
+ .clo_func = oclo_fdup2fd_exec
+}, {
+ .clo_desc = "fcntl(F_DUP2FD_CLOEXEC) FD_CLOEXEC",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_fdup2fd_exec
+}, {
+ .clo_desc = "fcntl(F_DUP2FD_CLOEXEC) FD_CLOFORK",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_fdup2fd_exec
+}, {
+ .clo_desc = "fcntl(F_DUP2FD_CLOEXEC) FD_CLOEXEC|FD_CLOFORK",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_fdup2fd_exec
+}, {
+ .clo_desc = "fcntl(F_DUP3FD) none->none",
+ .clo_flags = 0,
+ .clo_func = oclo_fdup3fd_none
+}, {
+ .clo_desc = "fcntl(F_DUP3FD) FD_CLOEXEC->none",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_fdup3fd_none
+}, {
+ .clo_desc = "fcntl(F_DUP3FD) FD_CLOFORK->none",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_fdup3fd_none
+}, {
+ .clo_desc = "fcntl(F_DUP3FD) FD_CLOEXEC|FD_CLOFORK->none",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_fdup3fd_none
+}, {
+ .clo_desc = "fcntl(F_DUP3FD) none->FD_CLOEXEC",
+ .clo_flags = 0,
+ .clo_func = oclo_fdup3fd_exec
+}, {
+ .clo_desc = "fcntl(F_DUP3FD) FD_CLOEXEC->FD_CLOEXEC",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_fdup3fd_exec
+}, {
+ .clo_desc = "fcntl(F_DUP3FD) FD_CLOFORK->FD_CLOEXEC",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_fdup3fd_exec
+}, {
+ .clo_desc = "fcntl(F_DUP3FD) FD_CLOEXEC|FD_CLOFORK->FD_CLOEXEC",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_fdup3fd_exec
+}, {
+ .clo_desc = "fcntl(F_DUP3FD) none->FD_CLOFORK|FD_CLOEXEC",
+ .clo_flags = 0,
+ .clo_func = oclo_fdup3fd_both
+}, {
+ .clo_desc = "fcntl(F_DUP3FD) FD_CLOEXEC->FD_CLOFORK|FD_CLOEXEC",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_fdup3fd_both
+}, {
+ .clo_desc = "fcntl(F_DUP3FD) FD_CLOFORK->FD_CLOFORK|FD_CLOEXEC",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_fdup3fd_both
+}, {
+ .clo_desc = "fcntl(F_DUP3FD) FD_CLOEXEC|FD_CLOFORK->"
+ "FD_CLOFORK|FD_CLOEXEC",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_fdup3fd_both
+}, {
+ .clo_desc = "fcntl(F_DUP3FD) none->FD_CLOFORK",
+ .clo_flags = 0,
+ .clo_func = oclo_fdup3fd_fork
+}, {
+ .clo_desc = "fcntl(F_DUP3FD) FD_CLOEXEC->FD_CLOFORK",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_fdup3fd_fork
+}, {
+ .clo_desc = "fcntl(F_DUP3FD) FD_CLOFORK->FD_CLOFORK",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_fdup3fd_fork
+}, {
+ .clo_desc = "fcntl(F_DUP3FD) FD_CLOEXEC|FD_CLOFORK->FD_CLOFORK",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_fdup3fd_fork
+}, {
+ .clo_desc = "dup2() none->none",
+ .clo_flags = 0,
+ .clo_func = oclo_dup2
+}, {
+ .clo_desc = "dup2() FD_CLOEXEC->none",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_dup2
+}, {
+ .clo_desc = "dup2() FD_CLOFORK->none",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_dup2
+}, {
+ .clo_desc = "dup2() FD_CLOEXEC|FD_CLOFORK->none",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_dup2
+}, {
+ .clo_desc = "dup3() none->none",
+ .clo_flags = 0,
+ .clo_func = oclo_dup3_none
+}, {
+ .clo_desc = "dup3() FD_CLOEXEC->none",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_dup3_none
+}, {
+ .clo_desc = "dup3() FD_CLOFORK->none",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_dup3_none
+}, {
+ .clo_desc = "dup3() FD_CLOEXEC|FD_CLOFORK->none",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_dup3_none
+}, {
+ .clo_desc = "dup3() none->FD_CLOEXEC",
+ .clo_flags = 0,
+ .clo_func = oclo_dup3_exec
+}, {
+ .clo_desc = "dup3() FD_CLOEXEC->FD_CLOEXEC",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_dup3_exec
+}, {
+ .clo_desc = "dup3() FD_CLOFORK->FD_CLOEXEC",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_dup3_exec
+}, {
+ .clo_desc = "dup3() FD_CLOEXEC|FD_CLOFORK->FD_CLOEXEC",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_dup3_exec
+}, {
+ .clo_desc = "dup3() none->FD_CLOFORK|FD_CLOEXEC",
+ .clo_flags = 0,
+ .clo_func = oclo_dup3_both
+}, {
+ .clo_desc = "dup3() FD_CLOEXEC->FD_CLOFORK|FD_CLOEXEC",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_dup3_both
+}, {
+ .clo_desc = "dup3() FD_CLOFORK->FD_CLOFORK|FD_CLOEXEC",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_dup3_both
+}, {
+ .clo_desc = "dup3() FD_CLOEXEC|FD_CLOFORK->FD_CLOFORK|FD_CLOEXEC",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_dup3_both
+}, {
+ .clo_desc = "dup3() none->FD_CLOFORK",
+ .clo_flags = 0,
+ .clo_func = oclo_dup3_fork
+}, {
+ .clo_desc = "dup3() FD_CLOEXEC->FD_CLOFORK",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_dup3_fork
+}, {
+ .clo_desc = "dup3() FD_CLOFORK->FD_CLOFORK",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_dup3_fork
+}, {
+ .clo_desc = "dup3() FD_CLOEXEC|FD_CLOFORK->FD_CLOFORK",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_dup3_fork
+}, {
+ .clo_desc = "pipe(2), no flags",
+ .clo_flags = 0,
+ .clo_func = oclo_pipe
+}, {
+ .clo_desc = "pipe(2), O_CLOEXEC",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_pipe
+}, {
+ .clo_desc = "pipe(2), O_CLOFORK",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_pipe
+}, {
+ .clo_desc = "pipe(2), O_CLOEXEC|O_CLOFORK",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_pipe
+}, {
+ .clo_desc = "socket(2), no flags",
+ .clo_flags = 0,
+ .clo_func = oclo_socket
+}, {
+ .clo_desc = "socket(2), O_CLOEXEC",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_socket
+}, {
+ .clo_desc = "socket(2), O_CLOFORK",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_socket
+}, {
+ .clo_desc = "socket(2), O_CLOEXEC|O_CLOFORK",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_socket
+}, {
+ .clo_desc = "socket(2), no flags->accept() none",
+ .clo_flags = 0,
+ .clo_func = oclo_accept
+}, {
+ .clo_desc = "socket(2), O_CLOEXEC->accept() none",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_accept
+}, {
+ .clo_desc = "socket(2), O_CLOFORK->accept() none",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_accept
+}, {
+ .clo_desc = "socket(2), O_CLOEXEC|O_CLOFORK->accept() none",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_accept
+}, {
+ .clo_desc = "socket(2), no flags->accept4() none",
+ .clo_flags = 0,
+ .clo_func = oclo_accept4_none
+}, {
+ .clo_desc = "socket(2), O_CLOEXEC->accept4() none",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_accept4_none
+}, {
+ .clo_desc = "socket(2), O_CLOFORK->accept4() none",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_accept4_none
+}, {
+ .clo_desc = "socket(2), O_CLOEXEC|O_CLOFORK->accept4() none",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_accept4_none
+}, {
+ .clo_desc = "socket(2), no flags->accept4() SOCK_CLOFORK|SOCK_CLOEXEC",
+ .clo_flags = 0,
+ .clo_func = oclo_accept4_both
+}, {
+ .clo_desc = "socket(2), O_CLOEXEC->accept4() SOCK_CLOFORK|SOCK_CLOEXEC",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_accept4_both
+}, {
+ .clo_desc = "socket(2), O_CLOFORK->accept4() SOCK_CLOFORK|SOCK_CLOEXEC",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_accept4_both
+}, {
+ .clo_desc = "socket(2), O_CLOEXEC|O_CLOFORK->accept4() "
+ "SOCK_CLOFORK|SOCK_CLOEXEC",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_accept4_both
+}, {
+ .clo_desc = "socket(2), no flags->accept4() SOCK_CLOFORK",
+ .clo_flags = 0,
+ .clo_func = oclo_accept4_fork
+}, {
+ .clo_desc = "socket(2), O_CLOEXEC->accept4() SOCK_CLOFORK",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_accept4_fork
+}, {
+ .clo_desc = "socket(2), O_CLOFORK->accept4() SOCK_CLOFORK",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_accept4_fork
+}, {
+ .clo_desc = "socket(2), O_CLOEXEC|O_CLOFORK->accept4() SOCK_CLOFORK",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_accept4_fork
+}, {
+ .clo_desc = "socket(2), no flags->accept4() SOCK_CLOEXEC",
+ .clo_flags = 0,
+ .clo_func = oclo_accept4_exec
+}, {
+ .clo_desc = "socket(2), O_CLOEXEC->accept4() SOCK_CLOEXEC",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_accept4_exec
+}, {
+ .clo_desc = "socket(2), O_CLOFORK->accept4() SOCK_CLOEXEC",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_accept4_exec
+}, {
+ .clo_desc = "socket(2), O_CLOEXEC|O_CLOFORK->accept4() SOCK_CLOEXEC",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_accept4_exec
+}, {
+ .clo_desc = "SCM_RIGHTS none->none",
+ .clo_flags = 0,
+ .clo_func = oclo_rights_none
+}, {
+ .clo_desc = "SCM_RIGHTS FD_CLOFORK->none",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_rights_none
+}, {
+ .clo_desc = "SCM_RIGHTS FD_CLOEXEC->none",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_rights_none
+}, {
+ .clo_desc = "SCM_RIGHTS FD_CLOEXEC|FD_CLOFORK->none",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_rights_none
+}, {
+ .clo_desc = "SCM_RIGHTS none->MSG_CMSG_CLOEXEC",
+ .clo_flags = 0,
+ .clo_func = oclo_rights_exec
+}, {
+ .clo_desc = "SCM_RIGHTS FD_CLOFORK->MSG_CMSG_CLOEXEC",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_rights_exec
+}, {
+ .clo_desc = "SCM_RIGHTS FD_CLOEXEC->MSG_CMSG_CLOEXEC",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_rights_exec
+}, {
+ .clo_desc = "SCM_RIGHTS FD_CLOEXEC|FD_CLOFORK->MSG_CMSG_CLOEXEC",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_rights_exec
+}, {
+ .clo_desc = "SCM_RIGHTS MSG_CMSG_CLOFORK->nMSG_CMSG_CLOFORK",
+ .clo_flags = 0,
+ .clo_func = oclo_rights_fork
+}, {
+ .clo_desc = "SCM_RIGHTS FD_CLOFORK->MSG_CMSG_CLOFORK",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_rights_fork
+}, {
+ .clo_desc = "SCM_RIGHTS FD_CLOEXEC->MSG_CMSG_CLOFORK",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_rights_fork
+}, {
+ .clo_desc = "SCM_RIGHTS FD_CLOEXEC|FD_CLOFORK->MSG_CMSG_CLOFORK",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_rights_fork
+}, {
+ .clo_desc = "SCM_RIGHTS none->MSG_CMSG_CLOEXEC|MSG_CMSG_CLOFORK",
+ .clo_flags = 0,
+ .clo_func = oclo_rights_both
+}, {
+ .clo_desc = "SCM_RIGHTS FD_CLOFORK->MSG_CMSG_CLOEXEC|MSG_CMSG_CLOFORK",
+ .clo_flags = FD_CLOFORK,
+ .clo_func = oclo_rights_both
+}, {
+ .clo_desc = "SCM_RIGHTS FD_CLOEXEC->MSG_CMSG_CLOEXEC|MSG_CMSG_CLOFORK",
+ .clo_flags = FD_CLOEXEC,
+ .clo_func = oclo_rights_both
+}, {
+ .clo_desc = "SCM_RIGHTS FD_CLOEXEC|FD_CLOFORK->"
+ "MSG_CMSG_CLOEXEC|MSG_CMSG_CLOFORK",
+ .clo_flags = FD_CLOEXEC | FD_CLOFORK,
+ .clo_func = oclo_rights_both
+} };
+
+static bool
+oclo_verify_fork(void)
+{
+ bool ret = true;
+
+ for (size_t i = 0; i < oclo_rtdata_next; i++) {
+ if (!oclo_flags_match(&oclo_rtdata[i], true)) {
+ ret = false;
+ }
+ }
+
+ return (ret);
+}
+
+/*
+ * Here we proceed to re-open any fd that was closed due to O_CLOFORK again to
+ * make sure that the file descriptor makes it to our child verifier.
+ * Importantly, with the changes that cause O_CLOFORK to be cleared on exec, we
+ * should not see any file descriptors with the flag in the child. This allows
+ * us to confirm that this is what we expect.
+ *
+ * In addition, this serves as a test to make sure that our opening of the
+ * lowest fd is correct. While this doesn't actually use the same method as was
+ * done previously, this should get us most of the way there.
+ */
+static void
+oclo_child_reopen(void)
+{
+ for (size_t i = 0; i < oclo_rtdata_next; i++) {
+ int fd;
+ int flags = O_RDWR | O_CLOFORK;
+
+ if ((oclo_rtdata[i].crt_flags & FD_CLOFORK) == 0)
+ continue;
+
+ if ((oclo_rtdata[i].crt_flags & FD_CLOEXEC) != 0)
+ flags |= O_CLOEXEC;
+
+ fd = open("/dev/zero", flags);
+ if (fd < 0) {
+ err(EXIT_FAILURE, "TEST FAILED: failed to re-open fd "
+ "%d with flags %d", oclo_rtdata[i].crt_fd, flags);
+ }
+
+ if (fd != oclo_rtdata[i].crt_fd) {
+ errx(EXIT_FAILURE, "TEST FAILED: re-opening fd %d "
+ "returned fd %d: test design issue or lowest fd "
+ "algorithm is broken", oclo_rtdata[i].crt_fd, fd);
+ }
+ }
+
+ (void) printf("TEST PASSED: successfully reopened fds post-fork");
+}
+
+/*
+ * Look for the verification program in the same directory that this program is
+ * found in. Note, that isn't the same thing as the current working directory.
+ */
+static void
+oclo_exec(void)
+{
+ ssize_t ret;
+ char dir[PATH_MAX], file[PATH_MAX];
+ char **argv;
+
+ ret = getpathname(getpid(), dir, sizeof(dir));
+ if (ret < 0)
+ err(EXIT_FAILURE, "TEST FAILED: failed to read executable path");
+
+ if (snprintf(file, sizeof (file), "%s/%s", dirname(dir), OCLO_VERIFY) >=
+ (int)sizeof (file)) {
+ errx(EXIT_FAILURE, "TEST FAILED: cannot assemble exec path "
+ "name: internal buffer overflow");
+ }
+
+ /* We need an extra for both the NULL terminator and the program name */
+ argv = calloc(oclo_rtdata_next + 2, sizeof (char *));
+ if (argv == NULL) {
+ err(EXIT_FAILURE, "TEST FAILED: failed to allocate exec "
+ "argument array");
+ }
+
+ argv[0] = file;
+ for (size_t i = 0; i < oclo_rtdata_next; i++) {
+ if (asprintf(&argv[i + 1], "0x%x", oclo_rtdata[i].crt_flags) ==
+ -1) {
+ err(EXIT_FAILURE, "TEST FAILED: failed to assemble "
+ "exec argument %zu", i + 1);
+ }
+ }
+
+ (void) execv(file, argv);
+ warn("TEST FAILED: failed to exec verifier %s", file);
+}
+
+int
+main(void)
+{
+ int ret = EXIT_SUCCESS;
+ siginfo_t cret;
+
+ /*
+ * Before we do anything else close all FDs that aren't standard. We
+ * don't want anything the test suite environment may have left behind.
+ */
+ (void) closefrom(STDERR_FILENO + 1);
+
+ /*
+ * Treat failure during this set up phase as a hard failure. There's no
+ * reason to continue if we can't successfully create the FDs we expect.
+ */
+ for (size_t i = 0; i < nitems(oclo_create); i++) {
+ oclo_create[i].clo_func(&oclo_create[i]);
+ }
+
+ pid_t child = fork();
+ if (child == 0) {
+ if (!oclo_verify_fork()) {
+ ret = EXIT_FAILURE;
+ }
+
+ oclo_child_reopen();
+
+ oclo_exec();
+ ret = EXIT_FAILURE;
+ _exit(ret);
+ }
+
+ if (waitid(P_PID, child, &cret, WEXITED) < 0) {
+ err(EXIT_FAILURE, "TEST FAILED: internal test failure waiting "
+ "for forked child to report");
+ }
+
+ if (cret.si_code != CLD_EXITED) {
+ warnx("TEST FAILED: child process did not successfully exit: "
+ "found si_code: %d", cret.si_code);
+ ret = EXIT_FAILURE;
+ } else if (cret.si_status != 0) {
+ warnx("TEST FAILED: child process did not exit with code 0: "
+ "found %d", cret.si_status);
+ ret = EXIT_FAILURE;
+ }
+
+ if (ret == EXIT_SUCCESS) {
+ (void) printf("All tests passed successfully\n");
+ }
+
+ return (ret);
+}
diff --git a/cddl/contrib/opensolaris/tests/os-tests/tests/oclo/oclo_errors.c b/cddl/contrib/opensolaris/tests/os-tests/tests/oclo/oclo_errors.c
new file mode 100644
index 000000000000..05b0c1a0839b
--- /dev/null
+++ b/cddl/contrib/opensolaris/tests/os-tests/tests/oclo/oclo_errors.c
@@ -0,0 +1,202 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2024 Oxide Computer Company
+ */
+
+/*
+ * Verify that unsupported flags will properly generate errors across the
+ * functions that we know perform strict error checking. This includes:
+ *
+ * o fcntl(..., F_DUP3FD, ...)
+ * o dup3()
+ * o pipe2()
+ * o socket()
+ * o accept4()
+ */
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define strerrorname_np(e) (sys_errlist[e])
+
+static bool
+oclo_check(const char *desc, const char *act, int ret, int e)
+{
+ if (ret >= 0) {
+ warnx("TEST FAILED: %s: fd was %s!", desc, act);
+ return (false);
+ } else if (errno != EINVAL) {
+ e = errno;
+ warnx("TEST FAILED: %s: failed with %s, expected "
+ "EINVAL", desc, strerrorname_np(e));
+ return (false);
+ }
+
+ (void) printf("TEST PASSED: %s: correctly failed with EINVAL\n",
+ desc);
+ return (true);
+}
+
+static bool
+oclo_dup3(const char *desc, int flags)
+{
+ int fd = dup3(STDERR_FILENO, 23, flags);
+ return (oclo_check(desc, "duplicated", fd, errno));
+}
+
+static bool
+oclo_dup3fd(const char *desc, int flags)
+{
+ int fd = fcntl(STDERR_FILENO, F_DUP3FD | (flags << F_DUP3FD_SHIFT), 23);
+ return (oclo_check(desc, "duplicated", fd, errno));
+}
+
+
+static bool
+oclo_pipe2(const char *desc, int flags)
+{
+ int fds[2], ret;
+
+ ret = pipe2(fds, flags);
+ return (oclo_check(desc, "piped", ret, errno));
+}
+
+#if 0
+static bool
+oclo_socket(const char *desc, int type)
+{
+ int fd = socket(PF_UNIX, SOCK_STREAM | type, 0);
+ return (oclo_check(desc, "created", fd, errno));
+}
+#endif
+
+static bool
+oclo_accept(const char *desc, int flags)
+{
+ int sock, fd, e;
+ struct sockaddr_in in;
+
+ sock = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
+ if (sock < 0) {
+ warn("TEST FAILED: %s: failed to create listen socket", desc);
+ return (false);
+ }
+
+ (void) memset(&in, 0, sizeof (in));
+ in.sin_family = AF_INET;
+ in.sin_port = 0;
+ in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ if (bind(sock, (struct sockaddr *)&in, sizeof (in)) != 0) {
+ warn("TEST FAILED: %s: failed to bind socket", desc);
+ (void) close(sock);
+ return (false);
+ }
+
+ if (listen(sock, 5) < 0) {
+ warn("TEST FAILED: %s: failed to listen on socket", desc);
+ (void) close(sock);
+ return (false);
+ }
+
+
+ fd = accept4(sock, NULL, NULL, flags);
+ e = errno;
+ (void) close(sock);
+ return (oclo_check(desc, "accepted", fd, e));
+}
+
+int
+main(void)
+{
+ int ret = EXIT_SUCCESS;
+
+ closefrom(STDERR_FILENO + 1);
+
+ if (!oclo_dup3("dup3(): O_RDWR", O_RDWR)) {
+ ret = EXIT_FAILURE;
+ }
+
+ if (!oclo_dup3("dup3(): O_NONBLOCK|O_CLOXEC", O_NONBLOCK | O_CLOEXEC)) {
+ ret = EXIT_FAILURE;
+ }
+
+ if (!oclo_dup3("dup3(): O_CLOFORK|O_WRONLY", O_CLOFORK | O_WRONLY)) {
+ ret = EXIT_FAILURE;
+ }
+
+ if (!oclo_dup3fd("fcntl(FDUP3FD): 0x7777", 0x7777)) {
+ ret = EXIT_FAILURE;
+ }
+
+ if (!oclo_dup3fd("fcntl(FDUP3FD): FD_CLOEXEC|FD_CLOFORK + 1",
+ (FD_CLOEXEC | FD_CLOFORK) + 1)) {
+ ret = EXIT_FAILURE;
+ }
+
+ if (!oclo_dup3fd("fcntl(FDUP3FD): INT_MAX", INT_MAX)) {
+ ret = EXIT_FAILURE;
+ }
+
+
+ if (!oclo_pipe2("pipe2(): O_RDWR", O_RDWR)) {
+ ret = EXIT_FAILURE;
+ }
+
+ if (!oclo_pipe2("pipe2(): O_SYNC|O_CLOXEC", O_SYNC | O_CLOEXEC)) {
+ ret = EXIT_FAILURE;
+ }
+
+ if (!oclo_pipe2("pipe2(): O_CLOFORK|O_WRONLY", O_CLOFORK | O_WRONLY)) {
+ ret = EXIT_FAILURE;
+ }
+
+ if (!oclo_pipe2("pipe2(): INT32_MAX", INT32_MAX)) {
+ ret = EXIT_FAILURE;
+ }
+
+#if 0 /* These tests are known to fail on FreeBSD */
+ if (!oclo_socket("socket(): INT32_MAX", INT32_MAX)) {
+ ret = EXIT_FAILURE;
+ }
+
+ if (!oclo_socket("socket(): 3 << 25", 3 << 25)) {
+ ret = EXIT_FAILURE;
+ }
+#endif
+
+ if (!oclo_accept("accept4(): INT32_MAX", INT32_MAX)) {
+ ret = EXIT_FAILURE;
+ }
+
+ if (!oclo_accept("accept4(): 3 << 25", 3 << 25)) {
+ ret = EXIT_FAILURE;
+ }
+
+ if (ret == EXIT_SUCCESS) {
+ (void) printf("All tests completed successfully\n");
+ }
+
+ return (ret);
+}
diff --git a/cddl/contrib/opensolaris/tests/os-tests/tests/oclo/ocloexec_verify.c b/cddl/contrib/opensolaris/tests/os-tests/tests/oclo/ocloexec_verify.c
new file mode 100644
index 000000000000..e33c61f03d54
--- /dev/null
+++ b/cddl/contrib/opensolaris/tests/os-tests/tests/oclo/ocloexec_verify.c
@@ -0,0 +1,154 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2025 Oxide Computer Company
+ */
+
+/*
+ * Verify that our file descriptors starting after stderr are correct based upon
+ * the series of passed in arguments from the 'oclo' program. Arguments are
+ * passed as a string that represents the flags that were originally verified
+ * pre-fork/exec via fcntl(F_GETFD). In addition, anything that was originally
+ * closed because it had FD_CLOFORK set was reopened with the same flags. This
+ * allows us to verify that the combinations worked and that FD_CLOFORK was
+ * properly cleared.
+ */
+
+#include <sys/types.h>
+#include <sys/user.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define strerrorname_np(e) (sys_errlist[e])
+
+static int
+getmaxfd(void)
+{
+ struct kinfo_file *files;
+ int i, cnt, max;
+
+ if ((files = kinfo_getfile(getpid(), &cnt)) == NULL)
+ err(1, "kinfo_getfile");
+
+ max = -1;
+ for (i = 0; i < cnt; i++)
+ if (files[i].kf_fd > max)
+ max = files[i].kf_fd;
+
+ free(files);
+ return (max);
+}
+
+/*
+ * Our flags may have FD_CLOFORK set in them (anything with FD_CLOEXEC Should
+ * not exist by definition). FD_CLOFORK is supposed to be cleared on exec. We
+ * still indicate which file descriptors FD_CLOFORK so we can check where it
+ * wasn't cleared.
+ */
+static bool
+verify_flags(int fd, int exp_flags)
+{
+ bool fail = (exp_flags & FD_CLOEXEC) != 0;
+ int flags = fcntl(fd, F_GETFD, NULL);
+ bool clofork = (exp_flags & FD_CLOFORK) != 0;
+ exp_flags &= ~FD_CLOFORK;
+
+ if (flags < 0) {
+ int e = errno;
+
+ if (fail) {
+ if (e == EBADF) {
+ (void) printf("TEST PASSED: post-exec fd %d: "
+ "flags 0x%x: correctly closed\n", fd,
+ exp_flags);
+ return (true);
+ }
+
+
+ warn("TEST FAILED: post-fork fd %d: expected fcntl to "
+ "fail with EBADF, but found %s", fd,
+ strerrorname_np(e));
+ return (false);
+ }
+
+ warnx("TEST FAILED: post-fork fd %d: fcntl(F_GETFD) "
+ "unexpectedly failed with %s, expected flags %d", fd,
+ strerrorname_np(e), exp_flags);
+ return (false);
+ }
+
+ if (fail) {
+ warnx("TEST FAILED: post-fork fd %d: received flags %d, but "
+ "expected to fail based on flags %d", fd, flags, exp_flags);
+ return (false);
+ }
+
+ if (clofork && (flags & FD_CLOFORK) != 0) {
+ warnx("TEST FAILED: post-fork fd %d (flags %d) retained "
+ "FD_CLOFORK, but it should have been cleared", fd, flags);
+ return (false);
+ }
+
+ if (flags != exp_flags) {
+ warnx("TEST FAILED: post-exec fd %d: discovered flags 0x%x do "
+ "not match expected flags 0x%x", fd, flags, exp_flags);
+ return (false);
+ }
+
+ (void) printf("TEST PASSED: post-exec fd %d: flags 0x%x: successfully "
+ "matched\n", fd, exp_flags);
+ return (true);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int maxfd;
+ int ret = EXIT_SUCCESS;
+
+ /*
+ * We should have one argument for each fd we found, ignoring stdin,
+ * stdout, and stderr. argc will also have an additional entry for our
+ * program name, which we want to skip. Note, the last fd may not exist
+ * because it was marked for close, hence the use of '>' below.
+ */
+ maxfd = getmaxfd();
+ if (maxfd - 3 > argc - 1) {
+ errx(EXIT_FAILURE, "TEST FAILED: found more fds %d than "
+ "arguments %d", maxfd - 3, argc - 1);
+ }
+
+ for (int i = 1; i < argc; i++) {
+ char *endptr;
+ int targ_fd = i + STDERR_FILENO;
+ errno = 0;
+ long long val = strtoll(argv[i], &endptr, 0);
+
+ if (errno != 0 || *endptr != '\0' ||
+ (val < 0 || val > (FD_CLOEXEC | FD_CLOFORK))) {
+ errx(EXIT_FAILURE, "TEST FAILED: failed to parse "
+ "argument %d: %s", i, argv[i]);
+ }
+
+ if (!verify_flags(targ_fd, (int)val))
+ ret = EXIT_FAILURE;
+ }
+
+ return (ret);
+}
diff --git a/lib/lib80211/regdomain.xml b/lib/lib80211/regdomain.xml
index 9116e54c31cf..16b74445f429 100644
--- a/lib/lib80211/regdomain.xml
+++ b/lib/lib80211/regdomain.xml
@@ -494,6 +494,10 @@
<flags>IEEE80211_CHAN_PASSIVE</flags>
<flags>IEEE80211_CHAN_DFS</flags>
</band>
+ <band>
+ <freqband ref="A20_5745_5865"/>
+ <maxpower>13</maxpower>
+ </band>
</netband>
<netband mode="11ng">
<band>
@@ -548,6 +552,14 @@
<flags>IEEE80211_CHAN_PASSIVE</flags>
<flags>IEEE80211_CHAN_DFS</flags>
</band>
+ <band>
+ <freqband ref="NA20_5745_5865"/>
+ <maxpower>13</maxpower>
+ </band>
+ <band>
+ <freqband ref="NA40_5745_5845"/>
+ <maxpower>13</maxpower>
+ </band>
</netband>
<netband mode="11ac">
<!-- 5150-5250/80, 200 mW, indoor -->
@@ -645,7 +657,7 @@
<flags>IEEE80211_CHAN_DFS</flags>
</band>
<band>
- <freqband ref="AC2_5745_5805_40"/>
+ <freqband ref="AC2_5745_5845_40"/>
<maxpower>13</maxpower>
<flags>IEEE80211_CHAN_HT40</flags>
<flags>IEEE80211_CHAN_VHT40</flags>
@@ -658,13 +670,6 @@
<flags>IEEE80211_CHAN_VHT80</flags>
<flags>IEEE80211_CHAN_DFS</flags>
</band>
- <band>
- <freqband ref="AC2_5745_5885_160"/>
- <maxpower>13</maxpower>
- <flags>IEEE80211_CHAN_HT40</flags>
- <flags>IEEE80211_CHAN_VHT160</flags>
- <flags>IEEE80211_CHAN_DFS</flags>
- </band>
</netband>
</rd>
@@ -2304,6 +2309,29 @@
<chanwidth>20</chanwidth> <chansep>20</chansep>
<flags>IEEE80211_CHAN_A</flags>
</freqband>
+<freqband id="A20_5745_5865">
+ <freqstart>5745</freqstart>
+ <freqend>5865</freqend>
+ <chanwidth>20</chanwidth>
+ <chansep>20</chansep>
+ <flags>IEEE80211_CHAN_A</flags>
+</freqband>
+<freqband id="NA20_5745_5865">
+ <freqstart>5745</freqstart>
+ <freqend>5865</freqend>
+ <chanwidth>20</chanwidth>
+ <chansep>20</chansep>
+ <flags>IEEE80211_CHAN_A</flags>
+ <flags>IEEE80211_CHAN_HT20</flags>
+</freqband>
+<freqband id="NA40_5745_5845">
+ <freqstart>5745</freqstart>
+ <freqend>5845</freqend>
+ <chanwidth>40</chanwidth>
+ <chansep>20</chansep>
+ <flags>IEEE80211_CHAN_A</flags>
+ <flags>IEEE80211_CHAN_HT40</flags>
+</freqband>
<freqband id="F1_5660_5700">
<freqstart>5660</freqstart> <freqend>5700</freqend>
<chanwidth>20</chanwidth> <chansep>20</chansep>
diff --git a/lib/libc/gen/dup3.3 b/lib/libc/gen/dup3.3
index f2798930797b..338a9ae74c64 100644
--- a/lib/libc/gen/dup3.3
+++ b/lib/libc/gen/dup3.3
@@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd August 16, 2013
+.Dd May 17, 2025
.Dt DUP3 3
.Os
.Sh NAME
@@ -47,6 +47,11 @@ The close-on-exec flag on the new file descriptor is determined by the
bit in
.Fa flags .
.Pp
+The close-on-fork flag on the new file descriptor is determined by the
+.Dv O_CLOFORK
+bit in
+.Fa flags .
+.Pp
If
.Fa oldd
\*(Ne
@@ -91,7 +96,9 @@ argument.
The
.Fa flags
argument has bits set other than
-.Dv O_CLOEXEC .
+.Dv O_CLOEXEC
+or
+.Dv O_CLOFORK .
.El
.Sh SEE ALSO
.Xr accept 2 ,
@@ -112,3 +119,7 @@ The
.Fn dup3
function appeared in
.Fx 10.0 .
+The
+.Dv O_CLOFORK
+flag appeared in
+.Fx 15.0 .
diff --git a/lib/libc/gen/dup3.c b/lib/libc/gen/dup3.c
index fca1e99fb47b..1401c1f5b607 100644
--- a/lib/libc/gen/dup3.c
+++ b/lib/libc/gen/dup3.c
@@ -39,21 +39,22 @@ int __dup3(int, int, int);
int
__dup3(int oldfd, int newfd, int flags)
{
- int how;
+ int fdflags;
if (oldfd == newfd) {
errno = EINVAL;
return (-1);
}
- if (flags & ~O_CLOEXEC) {
+ if ((flags & ~(O_CLOEXEC | O_CLOFORK)) != 0) {
errno = EINVAL;
return (-1);
}
- how = (flags & O_CLOEXEC) ? F_DUP2FD_CLOEXEC : F_DUP2FD;
+ fdflags = ((flags & O_CLOEXEC) != 0 ? FD_CLOEXEC : 0) |
+ ((flags & O_CLOFORK) != 0 ? FD_CLOFORK : 0);
- return (_fcntl(oldfd, how, newfd));
+ return (_fcntl(oldfd, F_DUP3FD | (fdflags << F_DUP3FD_SHIFT), newfd));
}
__weak_reference(__dup3, dup3);
diff --git a/lib/libopenbsd/Makefile b/lib/libopenbsd/Makefile
index 675ed476c51d..dca1c08b0aed 100644
--- a/lib/libopenbsd/Makefile
+++ b/lib/libopenbsd/Makefile
@@ -2,7 +2,8 @@ PACKAGE=lib${LIB}
LIB= openbsd
SRCS= imsg-buffer.c \
imsg.c \
- ohash.c
+ ohash.c \
+ recallocarray.c
.if !defined(BOOTSTRAPPING)
# Skip getdtablecount.c when bootstrapping since it doesn't compile for Linux
# and is not used by any of the bootstrap tools
diff --git a/lib/libopenbsd/recallocarray.c b/lib/libopenbsd/recallocarray.c
new file mode 100644
index 000000000000..11e1fda744c7
--- /dev/null
+++ b/lib/libopenbsd/recallocarray.c
@@ -0,0 +1,82 @@
+/* $OpenBSD: recallocarray.c,v 1.1 2017/03/06 18:44:21 otto Exp $ */
+/*
+ * Copyright (c) 2008, 2017 Otto Moerbeek <otto@drijf.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
+ * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
+ */
+#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
+
+void *recallocarray(void *, size_t, size_t, size_t);
+
+void *
+recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size)
+{
+ size_t oldsize, newsize;
+ void *newptr;
+
+ if (ptr == NULL)
+ return calloc(newnmemb, size);
+
+ if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
+ newnmemb > 0 && SIZE_MAX / newnmemb < size) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ newsize = newnmemb * size;
+
+ if ((oldnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
+ oldnmemb > 0 && SIZE_MAX / oldnmemb < size) {
+ errno = EINVAL;
+ return NULL;
+ }
+ oldsize = oldnmemb * size;
+
+ /*
+ * Don't bother too much if we're shrinking just a bit,
+ * we do not shrink for series of small steps, oh well.
+ */
+ if (newsize <= oldsize) {
+ size_t d = oldsize - newsize;
+
+ if (d < oldsize / 2 && d < (size_t)getpagesize()) {
+ memset((char *)ptr + newsize, 0, d);
+ return ptr;
+ }
+ }
+
+ newptr = malloc(newsize);
+ if (newptr == NULL)
+ return NULL;
+
+ if (newsize > oldsize) {
+ memcpy(newptr, ptr, oldsize);
+ memset((char *)newptr + oldsize, 0, newsize - oldsize);
+ } else
+ memcpy(newptr, ptr, newsize);
+
+ explicit_bzero(ptr, oldsize);
+ free(ptr);
+
+ return newptr;
+}
diff --git a/lib/libsys/accept.2 b/lib/libsys/accept.2
index 53926b3153d2..2da2af066a5b 100644
--- a/lib/libsys/accept.2
+++ b/lib/libsys/accept.2
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd October 9, 2014
+.Dd May 17, 2025
.Dt ACCEPT 2
.Os
.Sh NAME
@@ -85,6 +85,13 @@ and the close-on-exec flag on the new file descriptor can be set via the
flag in the
.Fa flags
argument.
+Similarly, the
+.Dv O_CLOFORK
+property can be set via the
+.Dv SOCK_CLOFORK
+flag in the
+.Fa flags
+argument.
.Pp
If no pending connections are
present on the queue, and the original socket
@@ -234,3 +241,8 @@ The
.Fn accept4
system call appeared in
.Fx 10.0 .
+.Pp
+The
+.Dv SOCK_CLOFORK
+flag appeared in
+.Fx 15.0 .
diff --git a/lib/libsys/closefrom.2 b/lib/libsys/closefrom.2
index aaa4c55607ac..1885a6fdeaa8 100644
--- a/lib/libsys/closefrom.2
+++ b/lib/libsys/closefrom.2
@@ -23,7 +23,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd March 3, 2022
+.Dd May 17, 2025
.Dt CLOSEFROM 2
.Os
.Sh NAME
@@ -59,6 +59,8 @@ Supported
.Bl -tag -width ".Dv CLOSE_RANGE_CLOEXEC"
.It Dv CLOSE_RANGE_CLOEXEC
Set the close-on-exec flag on descriptors in the range instead of closing them.
+.It Dv CLOSE_RANGE_CLOFORK
+Set the close-on-fork flag on descriptors in the range instead of closing them.
.El
.Sh RETURN VALUES
Upon successful completion,
@@ -90,3 +92,8 @@ The
.Fn closefrom
function first appeared in
.Fx 8.0 .
+.Pp
+The
+.Dv CLOSE_RANGE_CLOFORK
+flag appeared in
+.Fx 15.0 .
diff --git a/lib/libsys/execve.2 b/lib/libsys/execve.2
index 5a35980e9555..dc85b9321e48 100644
--- a/lib/libsys/execve.2
+++ b/lib/libsys/execve.2
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd January 26, 2022
+.Dd July 02, 2025
.Dt EXECVE 2
.Os
.Sh NAME
@@ -127,7 +127,10 @@ flag is set (see
and
.Xr fcntl 2 ) .
Descriptors that remain open are unaffected by
-.Fn execve .
+.Fn execve ,
+except those with the close-on-fork flag
+.Dv FD_CLOFORK
+which is cleared from all file descriptors.
If any of the standard descriptors (0, 1, and/or 2) are closed at the
time
.Fn execve
diff --git a/lib/libsys/fcntl.2 b/lib/libsys/fcntl.2
index 604de43e5e8c..3cf8adc29f88 100644
--- a/lib/libsys/fcntl.2
+++ b/lib/libsys/fcntl.2
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd June 5, 2025
+.Dd June 24, 2025
.Dt FCNTL 2
.Os
.Sh NAME
@@ -81,6 +81,13 @@ to remain open across
.Xr execve 2
system calls.
.It
+The fork-on-exec flag
+.Dv FD_CLOFORK
+associated with the new file descriptor is cleared, so the file descriptor is
+to remain open across
+.Xr fork 2
+system calls.
+.It
The
.Dv FD_RESOLVE_BENEATH
flag, described below, will be set if it was set on the original
@@ -95,6 +102,15 @@ flag associated with the new file descriptor is set, so the file descriptor
is closed when
.Xr execve 2
system call executes.
+.It Dv F_DUPFD_CLOFORK
+Like
+.Dv F_DUPFD ,
+but the
+.Dv FD_CLOFORK
+flag associated with the new file descriptor is set, so the file descriptor
+is closed when
+.Xr fork 2
+system call executes.
.It Dv F_DUP2FD
It is functionally equivalent to
.Bd -literal -offset indent
@@ -117,6 +133,11 @@ Use
.Fn dup2
instead of
.Dv F_DUP2FD .
+.It Dv F_DUP3FD
+Used to implement the
+.Fn dup3
+call.
+Do not use it.
.It Dv F_GETFD
Get the flags associated with the file descriptor
.Fa fd .
@@ -128,6 +149,10 @@ The file will be closed upon execution of
.Fa ( arg
is ignored).
Otherwise, the file descriptor will remain open.
+.It Dv FD_CLOFORK
+The file will be closed upon execution of the
+.Fn fork
+family of system calls.
.It Dv FD_RESOLVE_BENEATH
All path name lookups relative to that file descriptor
will behave as if the lookup had
@@ -153,7 +178,8 @@ descriptor to also have the flag set.
Set flags associated with
.Fa fd .
The available flags are
-.Dv FD_CLOEXEC
+.Dv FD_CLOEXEC ,
+.Dv FD_CLOFORK
and
.Dv FD_RESOLVE_BENEATH .
The
@@ -785,8 +811,10 @@ for the reasons as stated in
.Sh STANDARDS
The
.Dv F_DUP2FD
-constant is non portable.
-It is provided for compatibility with AIX and Solaris.
+and
+.Dv F_DUP3FD
+constants are not portable.
+They are provided for compatibility with AIX and Solaris.
.Pp
Per
.St -susv4 ,
@@ -811,3 +839,10 @@ The
.Dv F_DUP2FD
constant first appeared in
.Fx 7.1 .
+.Pp
+The
+.Dv F_DUPFD_CLOFORK
+and
+.Dv F_DUP3FD
+flags appeared in
+.Fx 15.0 .
diff --git a/lib/libsys/fork.2 b/lib/libsys/fork.2
index 7d548a42890d..e59b208a9ff5 100644
--- a/lib/libsys/fork.2
+++ b/lib/libsys/fork.2
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd August 5, 2021
+.Dd May 17, 2024
.Dt FORK 2
.Os
.Sh NAME
@@ -68,6 +68,16 @@ by the parent.
This descriptor copying is also used by the shell to
establish standard input and output for newly created processes
as well as to set up pipes.
+Any file descriptors that were marked with the close-on-fork flag,
+.Dv FD_CLOFORK
+.Po see
+.Fn fcntl 2
+and
+.Dv O_CLOFORK
+in
+.Fn open 2
+.Pc ,
+will not be present in the child process, but remain open in the parent.
.It
The child process' resource utilizations
are set to 0; see
diff --git a/lib/libsys/open.2 b/lib/libsys/open.2
index 84c4f02fce8a..a0e905a8f375 100644
--- a/lib/libsys/open.2
+++ b/lib/libsys/open.2
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd April 3, 2025
+.Dd May 17, 2025
.Dt OPEN 2
.Os
.Sh NAME
@@ -195,6 +195,9 @@ error if file is not a directory
.It Dv O_CLOEXEC
automatically close file on
.Xr execve 2
+.It Dv O_CLOFORK
+automatically close file on any child process created with
+.Fn fork 2
.It Dv O_VERIFY
verify the contents of the file with
.Xr mac_veriexec 4
@@ -360,6 +363,27 @@ may be used to set
.Dv FD_CLOEXEC
flag for the newly returned file descriptor.
.Pp
+.Dv O_CLOFORK
+may be used to set
+.Dv FD_CLOFORK
+flag for the newly returned file descriptor.
+The file will be closed on any child process created with
+.Fn fork 2 ,
+.Fn vfork 2
+or
+.Fn rfork 2
+with the
+.Dv RFFDG
+flag, remaining open in the parent.
+Both the
+.Dv O_CLOEXEC
+and
+.Dv O_CLOFORK
+flags can be modified with the
+.Dv F_SETFD
+.Fn fcntl 2
+command.
+.Pp
.Dv O_VERIFY
may be used to indicate to the kernel that the contents of the file should
be verified before allowing the open to proceed.
@@ -846,6 +870,9 @@ function was introduced in
appeared in 13.0.
.Dv O_NAMEDATTR
appeared in 15.0.
+.Dv O_CLOFORK
+appeared in
+.Fx 15.0 .
.Sh BUGS
The
.Fa mode
diff --git a/lib/libsys/pipe.2 b/lib/libsys/pipe.2
index 9531c9717395..37d6eba420de 100644
--- a/lib/libsys/pipe.2
+++ b/lib/libsys/pipe.2
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd December 1, 2017
+.Dd May 17, 2025
.Dt PIPE 2
.Os
.Sh NAME
@@ -64,6 +64,8 @@ list, defined in
.Bl -tag -width ".Dv O_NONBLOCK"
.It Dv O_CLOEXEC
Set the close-on-exec flag for the new file descriptors.
+.It Dv O_CLOFORK
+Set the close-on-fork flag for the new file descriptors.
.It Dv O_NONBLOCK
Set the non-blocking flag for the ends of the pipe.
.El
@@ -173,3 +175,8 @@ function became a wrapper around
.Fn pipe2
in
.Fx 11.0 .
+.Pp
+The
+.Dv O_CLOFORK
+flag appeared in
+.Fx 15.0 .
diff --git a/lib/libsys/recv.2 b/lib/libsys/recv.2
index f3ee60b75663..b78cd70b8a1d 100644
--- a/lib/libsys/recv.2
+++ b/lib/libsys/recv.2
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd July 30, 2022
+.Dd May 17, 2025
.Dt RECV 2
.Os
.Sh NAME
@@ -164,6 +164,7 @@ one or more of the values:
.It Dv MSG_WAITALL Ta wait for full request or error
.It Dv MSG_DONTWAIT Ta do not block
.It Dv MSG_CMSG_CLOEXEC Ta set received fds close-on-exec
+.It Dv MSG_CMSG_CLOFORK Ta set received fds close-on-fork
.It Dv MSG_WAITFORONE Ta do not block after receiving the first message
(only for
.Fn recvmmsg
diff --git a/lib/libsys/socket.2 b/lib/libsys/socket.2
index a383cbcc4d80..b211611c6354 100644
--- a/lib/libsys/socket.2
+++ b/lib/libsys/socket.2
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd January 15, 2023
+.Dd May 17, 2025
.Dt SOCKET 2
.Os
.Sh NAME
@@ -121,6 +121,7 @@ argument:
.Pp
.Bd -literal -offset indent -compact
SOCK_CLOEXEC Set close-on-exec on the new descriptor,
+SOCK_CLOFORK Set close-on-fork on the new descriptor,
SOCK_NONBLOCK Set non-blocking mode on the new socket
.Ed
.Pp
@@ -331,7 +332,10 @@ argument of
.Fn socket .
The
.Dv SOCK_CLOEXEC
-flag is expected to conform to the next revision of the
+and
+.Dv SOCK_CLOFORK
+flags are expected to conform to
+.St -p1003.1-2024 .
.Tn POSIX
standard.
The
@@ -347,3 +351,8 @@ The
.Fn socket
system call appeared in
.Bx 4.2 .
+.Pp
+The
+.Dv SOCK_CLOFORK
+flag appeared in
+.Fx 15.0 .
diff --git a/lib/libsys/socketpair.2 b/lib/libsys/socketpair.2
index 5874a0791f4d..60dec74f9cc2 100644
--- a/lib/libsys/socketpair.2
+++ b/lib/libsys/socketpair.2
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd February 10, 2018
+.Dd May 17, 2025
.Dt SOCKETPAIR 2
.Os
.Sh NAME
@@ -56,7 +56,8 @@ and
The two sockets are indistinguishable.
.Pp
The
-.Dv SOCK_CLOEXEC
+.Dv SOCK_CLOEXEC ,
+.Dv SOCK_CLOFORK
and
.Dv SOCK_NONBLOCK
flags in the
diff --git a/lib/libsysdecode/flags.c b/lib/libsysdecode/flags.c
index dc09c5747968..f8e26e6a9dae 100644
--- a/lib/libsysdecode/flags.c
+++ b/lib/libsysdecode/flags.c
@@ -196,7 +196,7 @@ sysdecode_vmprot(FILE *fp, int type, int *rem)
}
static struct name_table sockflags[] = {
- X(SOCK_CLOEXEC) X(SOCK_NONBLOCK) XEND
+ X(SOCK_CLOEXEC) X(SOCK_CLOFORK) X(SOCK_NONBLOCK) XEND
};
bool
@@ -206,16 +206,17 @@ sysdecode_socket_type(FILE *fp, int type, int *rem)
uintmax_t val;
bool printed;
- str = lookup_value(socktype, type & ~(SOCK_CLOEXEC | SOCK_NONBLOCK));
+ str = lookup_value(socktype,
+ type & ~(SOCK_CLOEXEC | SOCK_CLOFORK | SOCK_NONBLOCK));
if (str != NULL) {
fputs(str, fp);
*rem = 0;
printed = true;
} else {
- *rem = type & ~(SOCK_CLOEXEC | SOCK_NONBLOCK);
+ *rem = type & ~(SOCK_CLOEXEC | SOCK_CLOFORK | SOCK_NONBLOCK);
printed = false;
}
- val = type & (SOCK_CLOEXEC | SOCK_NONBLOCK);
+ val = type & (SOCK_CLOEXEC | SOCK_CLOFORK | SOCK_NONBLOCK);
print_mask_part(fp, sockflags, &val, &printed);
return (printed);
}
@@ -563,7 +564,7 @@ sysdecode_nfssvc_flags(int flags)
}
static struct name_table pipe2flags[] = {
- X(O_CLOEXEC) X(O_NONBLOCK) XEND
+ X(O_CLOEXEC) X(O_CLOFORK) X(O_NONBLOCK) XEND
};
bool
@@ -873,7 +874,7 @@ sysdecode_fcntl_cmd(int cmd)
}
static struct name_table fcntl_fd_arg[] = {
- X(FD_CLOEXEC) X(0) XEND
+ X(FD_CLOEXEC) X(FD_CLOFORK) X(0) XEND
};
bool
diff --git a/lib/libsysdecode/sysdecode_fcntl_arg.3 b/lib/libsysdecode/sysdecode_fcntl_arg.3
index ee3a030a79e4..d5648ce0adc3 100644
--- a/lib/libsysdecode/sysdecode_fcntl_arg.3
+++ b/lib/libsysdecode/sysdecode_fcntl_arg.3
@@ -54,7 +54,8 @@ are determined by
.It Sy Command Ta Fa arg Sy Type Ta Sy Output Format
.It
.It Dv F_SETFD Ta Vt int Ta
-.Dq FD_CLOEXEC
+.Dq FD_CLOEXEC ,
+.Dq FD_CLOFORK
or the value of
.Fa arg
in the indicated
diff --git a/sbin/routed/routed.8 b/sbin/routed/routed.8
index 8cf12d7b60e1..334c828b943e 100644
--- a/sbin/routed/routed.8
+++ b/sbin/routed/routed.8
@@ -27,13 +27,20 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd June 27, 2022
+.Dd May 20, 2025
.Dt ROUTED 8
.Os
.Sh NAME
.Nm routed ,
.Nm rdisc
.Nd network RIP and router discovery routing daemon
+.Sh DEPRECATION NOTICE
+The
+.Nm routed
+and
+.Nm rdisc
+utilities are deprecated and will be removed in
+.Fx 16.0 .
.Sh SYNOPSIS
.Nm
.Op Fl isqdghmpAtv
diff --git a/sbin/routed/rtquery/rtquery.8 b/sbin/routed/rtquery/rtquery.8
index de5e1fc7cf96..ff46a3414dcf 100644
--- a/sbin/routed/rtquery/rtquery.8
+++ b/sbin/routed/rtquery/rtquery.8
@@ -1,11 +1,16 @@
.\" $Revision: 2.27 $
.\"
-.Dd June 1, 1996
+.Dd May 20, 2025
.Dt RTQUERY 8
.Os
.Sh NAME
.Nm rtquery
.Nd query routing daemons for their routing tables
+.Sh DEPRECATION NOTICE
+The
+.Nm
+utility is deprecated and will be removed in
+.Fx 16.0 .
.Sh SYNOPSIS
.Nm
.Op Fl np1
diff --git a/sys/amd64/amd64/efirt_machdep.c b/sys/amd64/amd64/efirt_machdep.c
index f70e235a0150..fe5d60c978dd 100644
--- a/sys/amd64/amd64/efirt_machdep.c
+++ b/sys/amd64/amd64/efirt_machdep.c
@@ -63,8 +63,6 @@
1u << EFI_MD_TYPE_FIRMWARE \
)
-uint32_t efi_map_regs;
-
static pml5_entry_t *efi_pml5;
static pml4_entry_t *efi_pml4;
static vm_object_t obj_1t1_pt;
diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c
index 1e8f9b22bd19..f46462b39fa3 100644
--- a/sys/amd64/amd64/machdep.c
+++ b/sys/amd64/amd64/machdep.c
@@ -188,6 +188,12 @@ struct init_ops init_ops = {
*/
vm_paddr_t efi_systbl_phys;
+/*
+ * Bitmap of extra EFI memory region types that should be preserved and mapped
+ * during runtime services calls.
+ */
+uint32_t efi_map_regs;
+
/* Intel ICH registers */
#define ICH_PMBASE 0x400
#define ICH_SMI_EN ICH_PMBASE + 0x30
diff --git a/sys/amd64/amd64/pmap.c b/sys/amd64/amd64/pmap.c
index cae5436a1ff2..9c985df13ddf 100644
--- a/sys/amd64/amd64/pmap.c
+++ b/sys/amd64/amd64/pmap.c
@@ -9991,7 +9991,7 @@ pmap_demote_DMAP(vm_paddr_t base, vm_size_t len, bool invalidate)
pd_entry_t *pde;
vm_offset_t va;
vm_page_t m, mpte;
- bool changed;
+ bool changed, rv __diagused;
if (len == 0)
return;
@@ -10021,8 +10021,8 @@ pmap_demote_DMAP(vm_paddr_t base, vm_size_t len, bool invalidate)
if ((*pdpe & X86_PG_V) == 0)
panic("pmap_demote_DMAP: invalid PDPE");
if ((*pdpe & PG_PS) != 0) {
- if (!pmap_demote_pdpe(kernel_pmap, pdpe, va, m))
- panic("pmap_demote_DMAP: PDPE failed");
+ rv = pmap_demote_pdpe(kernel_pmap, pdpe, va, m);
+ KASSERT(rv, ("pmap_demote_DMAP: PDPE failed"));
changed = true;
m = NULL;
}
@@ -10033,9 +10033,9 @@ pmap_demote_DMAP(vm_paddr_t base, vm_size_t len, bool invalidate)
if ((*pde & PG_PS) != 0) {
mpte->pindex = pmap_pde_pindex(va);
pmap_pt_page_count_adj(kernel_pmap, 1);
- if (!pmap_demote_pde_mpte(kernel_pmap, pde, va,
- NULL, mpte))
- panic("pmap_demote_DMAP: PDE failed");
+ rv = pmap_demote_pde_mpte(kernel_pmap, pde, va,
+ NULL, mpte);
+ KASSERT(rv, ("pmap_demote_DMAP: PDE failed"));
changed = true;
mpte = NULL;
}
diff --git a/sys/compat/freebsd32/freebsd32_sysent.c b/sys/compat/freebsd32/freebsd32_sysent.c
index 3718a1b0c8ee..ef0aff8bf852 100644
--- a/sys/compat/freebsd32/freebsd32_sysent.c
+++ b/sys/compat/freebsd32/freebsd32_sysent.c
@@ -659,7 +659,7 @@ struct sysent freebsd32_sysent[] = {
{ .sy_narg = AS(getrlimitusage_args), .sy_call = (sy_call_t *)sys_getrlimitusage, .sy_auevent = AUE_NULL, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 589 = getrlimitusage */
{ .sy_narg = AS(fchroot_args), .sy_call = (sy_call_t *)sys_fchroot, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 590 = fchroot */
{ .sy_narg = AS(freebsd32_setcred_args), .sy_call = (sy_call_t *)freebsd32_setcred, .sy_auevent = AUE_SETCRED, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 591 = freebsd32_setcred */
- { .sy_narg = AS(exterrctl_args), .sy_call = (sy_call_t *)sys_exterrctl, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 592 = exterrctl */
+ { .sy_narg = AS(exterrctl_args), .sy_call = (sy_call_t *)sys_exterrctl, .sy_auevent = AUE_NULL, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 592 = exterrctl */
{ .sy_narg = AS(inotify_add_watch_at_args), .sy_call = (sy_call_t *)sys_inotify_add_watch_at, .sy_auevent = AUE_INOTIFY, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 593 = inotify_add_watch_at */
{ .sy_narg = AS(inotify_rm_watch_args), .sy_call = (sy_call_t *)sys_inotify_rm_watch, .sy_auevent = AUE_INOTIFY, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 594 = inotify_rm_watch */
};
diff --git a/sys/fs/nfs/nfs_commonsubs.c b/sys/fs/nfs/nfs_commonsubs.c
index f46b0d282861..4c498e96a3c0 100644
--- a/sys/fs/nfs/nfs_commonsubs.c
+++ b/sys/fs/nfs/nfs_commonsubs.c
@@ -630,6 +630,10 @@ nfscl_fillsattr(struct nfsrv_descript *nd, struct vattr *vap,
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP);
if ((flags & NFSSATTR_FULL) && vap->va_size != VNOVAL)
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_SIZE);
+ if ((flags & NFSSATTR_FULL) && vap->va_flags != VNOVAL) {
+ NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN);
+ NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_SYSTEM);
+ }
if (vap->va_atime.tv_sec != VNOVAL)
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET);
if (vap->va_mtime.tv_sec != VNOVAL)
@@ -1314,6 +1318,7 @@ nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
u_int32_t freenum = 0, tuint;
u_int64_t uquad = 0, thyp, thyp2;
uint16_t tui16;
+ long has_pathconf;
#ifdef QUOTA
struct dqblk dqb;
uid_t savuid;
@@ -1421,6 +1426,16 @@ nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
NFSCLRBIT_ATTRBIT(&checkattrbits, NFSATTRBIT_ACL);
NFSCLRBIT_ATTRBIT(&checkattrbits, NFSATTRBIT_ACLSUPPORT);
}
+ /* Some filesystems do not support uf_hidden */
+ if (vp == NULL || VOP_PATHCONF(vp,
+ _PC_HAS_HIDDENSYSTEM, &has_pathconf) != 0)
+ has_pathconf = 0;
+ if (has_pathconf == 0) {
+ NFSCLRBIT_ATTRBIT(&checkattrbits,
+ NFSATTRBIT_HIDDEN);
+ NFSCLRBIT_ATTRBIT(&checkattrbits,
+ NFSATTRBIT_SYSTEM);
+ }
if (!NFSEQUAL_ATTRBIT(&retattrbits, &checkattrbits)
|| retnotsup)
*retcmpp = NFSERR_NOTSAME;
@@ -1521,15 +1536,13 @@ nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare) {
if (!(*retcmpp)) {
- long has_named_attr;
-
if (vp == NULL || VOP_PATHCONF(vp,
- _PC_HAS_NAMEDATTR, &has_named_attr)
+ _PC_HAS_NAMEDATTR, &has_pathconf)
!= 0)
- has_named_attr = 0;
- if ((has_named_attr != 0 &&
+ has_pathconf = 0;
+ if ((has_pathconf != 0 &&
*tl != newnfs_true) ||
- (has_named_attr == 0 &&
+ (has_pathconf == 0 &&
*tl != newnfs_false))
*retcmpp = NFSERR_NOTSAME;
}
@@ -1792,9 +1805,17 @@ nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
free(cp2, M_NFSSTRING);
break;
case NFSATTRBIT_HIDDEN:
- NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
- if (compare && !(*retcmpp))
- *retcmpp = NFSERR_ATTRNOTSUPP;
+ NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
+ if (compare) {
+ if (!(*retcmpp) && ((*tl == newnfs_true &&
+ (nap->na_flags & UF_HIDDEN) == 0) ||
+ (*tl == newnfs_false &&
+ (nap->na_flags & UF_HIDDEN) != 0)))
+ *retcmpp = NFSERR_NOTSAME;
+ } else if (nap != NULL) {
+ if (*tl == newnfs_true)
+ nap->na_flags |= UF_HIDDEN;
+ }
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_HOMOGENEOUS:
@@ -2166,9 +2187,17 @@ nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
attrsum += NFSX_HYPER;
break;
case NFSATTRBIT_SYSTEM:
- NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
- if (compare && !(*retcmpp))
- *retcmpp = NFSERR_ATTRNOTSUPP;
+ NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
+ if (compare) {
+ if (!(*retcmpp) && ((*tl == newnfs_true &&
+ (nap->na_flags & UF_SYSTEM) == 0) ||
+ (*tl == newnfs_false &&
+ (nap->na_flags & UF_SYSTEM) != 0)))
+ *retcmpp = NFSERR_NOTSAME;
+ } else if (nap != NULL) {
+ if (*tl == newnfs_true)
+ nap->na_flags |= UF_SYSTEM;
+ }
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_TIMEACCESS:
@@ -2634,7 +2663,7 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
size_t atsiz;
bool xattrsupp;
short irflag;
- long has_named_attr;
+ long has_pathconf;
#ifdef QUOTA
struct dqblk dqb;
uid_t savuid;
@@ -2751,6 +2780,14 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
NFSCLRBIT_ATTRBIT(&attrbits,NFSATTRBIT_ACLSUPPORT);
NFSCLRBIT_ATTRBIT(&attrbits,NFSATTRBIT_ACL);
}
+ if (cred == NULL || p == NULL || vp == NULL ||
+ VOP_PATHCONF(vp, _PC_HAS_HIDDENSYSTEM,
+ &has_pathconf) != 0)
+ has_pathconf = 0;
+ if (has_pathconf == 0) {
+ NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN);
+ NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_SYSTEM);
+ }
retnum += nfsrv_putattrbit(nd, &attrbits);
break;
case NFSATTRBIT_TYPE:
@@ -2791,10 +2828,10 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
break;
case NFSATTRBIT_NAMEDATTR:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
- if (VOP_PATHCONF(vp, _PC_HAS_NAMEDATTR, &has_named_attr)
- != 0)
- has_named_attr = 0;
- if (has_named_attr != 0)
+ if (VOP_PATHCONF(vp, _PC_HAS_NAMEDATTR,
+ &has_pathconf) != 0)
+ has_pathconf = 0;
+ if (has_pathconf != 0)
*tl = newnfs_true;
else
*tl = newnfs_false;
@@ -2899,6 +2936,14 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
*tl = 0;
retnum += 2 * NFSX_UNSIGNED;
break;
+ case NFSATTRBIT_HIDDEN:
+ NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+ if ((vap->va_flags & UF_HIDDEN) != 0)
+ *tl = newnfs_true;
+ else
+ *tl = newnfs_false;
+ retnum += NFSX_UNSIGNED;
+ break;
case NFSATTRBIT_HOMOGENEOUS:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
if (fsinf.fs_properties & NFSV3FSINFO_HOMOGENEOUS)
@@ -3088,6 +3133,14 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
txdr_hyper(vap->va_bytes, tl);
retnum += NFSX_HYPER;
break;
+ case NFSATTRBIT_SYSTEM:
+ NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+ if ((vap->va_flags & UF_SYSTEM) != 0)
+ *tl = newnfs_true;
+ else
+ *tl = newnfs_false;
+ retnum += NFSX_UNSIGNED;
+ break;
case NFSATTRBIT_TIMEACCESS:
NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
txdr_nfsv4time(&vap->va_atime, tl);
diff --git a/sys/fs/nfs/nfsproto.h b/sys/fs/nfs/nfsproto.h
index eff53e1a384e..cb5a80e8df73 100644
--- a/sys/fs/nfs/nfsproto.h
+++ b/sys/fs/nfs/nfsproto.h
@@ -1142,6 +1142,7 @@ struct nfsv3_sattr {
NFSATTRBM_FILESFREE | \
NFSATTRBM_FILESTOTAL | \
NFSATTRBM_FSLOCATIONS | \
+ NFSATTRBM_HIDDEN | \
NFSATTRBM_HOMOGENEOUS | \
NFSATTRBM_MAXFILESIZE | \
NFSATTRBM_MAXLINK | \
@@ -1163,6 +1164,7 @@ struct nfsv3_sattr {
NFSATTRBM_SPACEFREE | \
NFSATTRBM_SPACETOTAL | \
NFSATTRBM_SPACEUSED | \
+ NFSATTRBM_SYSTEM | \
NFSATTRBM_TIMEACCESS | \
NFSATTRBM_TIMECREATE | \
NFSATTRBM_TIMEDELTA | \
@@ -1210,11 +1212,13 @@ struct nfsv3_sattr {
*/
#define NFSATTRBIT_SETABLE0 \
(NFSATTRBM_SIZE | \
+ NFSATTRBM_HIDDEN | \
NFSATTRBM_ACL)
#define NFSATTRBIT_SETABLE1 \
(NFSATTRBM_MODE | \
NFSATTRBM_OWNER | \
NFSATTRBM_OWNERGROUP | \
+ NFSATTRBM_SYSTEM | \
NFSATTRBM_TIMECREATE | \
NFSATTRBM_TIMEACCESSSET | \
NFSATTRBM_TIMEMODIFYSET)
@@ -1254,6 +1258,7 @@ struct nfsv3_sattr {
NFSATTRBM_SIZE | \
NFSATTRBM_FSID | \
NFSATTRBM_FILEID | \
+ NFSATTRBM_HIDDEN | \
NFSATTRBM_MAXREAD)
/*
@@ -1266,6 +1271,7 @@ struct nfsv3_sattr {
NFSATTRBM_OWNERGROUP | \
NFSATTRBM_RAWDEV | \
NFSATTRBM_SPACEUSED | \
+ NFSATTRBM_SYSTEM | \
NFSATTRBM_TIMEACCESS | \
NFSATTRBM_TIMECREATE | \
NFSATTRBM_TIMEMETADATA | \
@@ -1288,6 +1294,7 @@ struct nfsv3_sattr {
NFSATTRBM_SIZE | \
NFSATTRBM_FSID | \
NFSATTRBM_FILEID | \
+ NFSATTRBM_HIDDEN | \
NFSATTRBM_MAXREAD)
/*
@@ -1298,6 +1305,7 @@ struct nfsv3_sattr {
NFSATTRBM_NUMLINKS | \
NFSATTRBM_RAWDEV | \
NFSATTRBM_SPACEUSED | \
+ NFSATTRBM_SYSTEM | \
NFSATTRBM_TIMEACCESS | \
NFSATTRBM_TIMECREATE | \
NFSATTRBM_TIMEMETADATA | \
diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c
index c07da6f9275f..e0e66baca44d 100644
--- a/sys/fs/nfsclient/nfs_clrpcops.c
+++ b/sys/fs/nfsclient/nfs_clrpcops.c
@@ -4158,6 +4158,13 @@ nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
if (!NFSISSET_ATTRBIT(&dnp->n_vattr.na_suppattr,
NFSATTRBIT_TIMECREATE))
NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMECREATE);
+ if (!NFSISSET_ATTRBIT(&dnp->n_vattr.na_suppattr,
+ NFSATTRBIT_HIDDEN) ||
+ !NFSISSET_ATTRBIT(&dnp->n_vattr.na_suppattr,
+ NFSATTRBIT_SYSTEM)) {
+ NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN);
+ NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_SYSTEM);
+ }
}
/*
diff --git a/sys/fs/nfsclient/nfs_clvnops.c b/sys/fs/nfsclient/nfs_clvnops.c
index 0049d7edca33..fbfcdafaa06b 100644
--- a/sys/fs/nfsclient/nfs_clvnops.c
+++ b/sys/fs/nfsclient/nfs_clvnops.c
@@ -1074,15 +1074,23 @@ nfs_setattr(struct vop_setattr_args *ap)
int error = 0;
u_quad_t tsize;
struct timespec ts;
+ struct nfsmount *nmp;
#ifndef nolint
tsize = (u_quad_t)0;
#endif
/*
- * Setting of flags and marking of atimes are not supported.
+ * Only setting of UF_HIDDEN and UF_SYSTEM are supported and
+ * only for NFSv4 servers that support them.
*/
- if (vap->va_flags != VNOVAL)
+ nmp = VFSTONFS(vp->v_mount);
+ if (vap->va_flags != VNOVAL && (!NFSHASNFSV4(nmp) ||
+ (vap->va_flags & ~(UF_HIDDEN | UF_SYSTEM)) != 0 ||
+ ((vap->va_flags & UF_HIDDEN) != 0 &&
+ !NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr, NFSATTRBIT_HIDDEN)) ||
+ ((vap->va_flags & UF_SYSTEM) != 0 &&
+ !NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr, NFSATTRBIT_SYSTEM))))
return (EOPNOTSUPP);
/*
@@ -1092,7 +1100,8 @@ nfs_setattr(struct vop_setattr_args *ap)
vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL ||
vap->va_mtime.tv_sec != VNOVAL ||
vap->va_birthtime.tv_sec != VNOVAL ||
- vap->va_mode != (mode_t)VNOVAL) &&
+ vap->va_mode != (mode_t)VNOVAL ||
+ vap->va_flags != (u_long)VNOVAL) &&
(vp->v_mount->mnt_flag & MNT_RDONLY))
return (EROFS);
if (vap->va_size != VNOVAL) {
@@ -4754,6 +4763,15 @@ nfs_pathconf(struct vop_pathconf_args *ap)
else
*ap->a_retval = 0;
break;
+ case _PC_HAS_HIDDENSYSTEM:
+ if (NFS_ISV4(vp) && NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr,
+ NFSATTRBIT_HIDDEN) &&
+ NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr,
+ NFSATTRBIT_SYSTEM))
+ *ap->a_retval = 1;
+ else
+ *ap->a_retval = 0;
+ break;
default:
error = vop_stdpathconf(ap);
diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c
index 3bf54d82b959..a81f1492ef95 100644
--- a/sys/fs/nfsserver/nfs_nfsdport.c
+++ b/sys/fs/nfsserver/nfs_nfsdport.c
@@ -449,6 +449,7 @@ nfsvno_getattr(struct vnode *vp, struct nfsvattr *nvap,
}
nvap->na_bsdflags = 0;
+ nvap->na_flags = 0;
error = VOP_GETATTR(vp, &nvap->na_vattr, nd->nd_cred);
if (lockedit != 0)
NFSVOPUNLOCK(vp);
@@ -3127,6 +3128,9 @@ nfsv4_sattr(struct nfsrv_descript *nd, vnode_t vp, struct nfsvattr *nvap,
bitpos = NFSATTRBIT_MAX;
} else {
bitpos = 0;
+ if (NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_HIDDEN) ||
+ NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_SYSTEM))
+ nvap->na_flags = 0;
}
moderet = 0;
for (; bitpos < NFSATTRBIT_MAX; bitpos++) {
@@ -3163,9 +3167,11 @@ nfsv4_sattr(struct nfsrv_descript *nd, vnode_t vp, struct nfsvattr *nvap,
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_HIDDEN:
- NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
- if (!nd->nd_repstat)
- nd->nd_repstat = NFSERR_ATTRNOTSUPP;
+ NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
+ if (nd->nd_repstat == 0) {
+ if (*tl == newnfs_true)
+ nvap->na_flags |= UF_HIDDEN;
+ }
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_MIMETYPE:
@@ -3240,9 +3246,11 @@ nfsv4_sattr(struct nfsrv_descript *nd, vnode_t vp, struct nfsvattr *nvap,
attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(j));
break;
case NFSATTRBIT_SYSTEM:
- NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
- if (!nd->nd_repstat)
- nd->nd_repstat = NFSERR_ATTRNOTSUPP;
+ NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
+ if (nd->nd_repstat == 0) {
+ if (*tl == newnfs_true)
+ nvap->na_flags |= UF_SYSTEM;
+ }
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_TIMEACCESSSET:
diff --git a/sys/fs/nfsserver/nfs_nfsdserv.c b/sys/fs/nfsserver/nfs_nfsdserv.c
index 4e15d55eb312..f7564ade401b 100644
--- a/sys/fs/nfsserver/nfs_nfsdserv.c
+++ b/sys/fs/nfsserver/nfs_nfsdserv.c
@@ -403,8 +403,10 @@ nfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram,
if (error)
goto nfsmout;
- /* For NFSv4, only va_uid is used from nva2. */
+ /* For NFSv4, only va_uid and va_flags is used from nva2. */
NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNER);
+ NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_HIDDEN);
+ NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_SYSTEM);
preat_ret = nfsvno_getattr(vp, &nva2, nd, p, 1, &retbits);
if (!nd->nd_repstat)
nd->nd_repstat = preat_ret;
@@ -463,6 +465,9 @@ nfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram,
&nva, &attrbits, exp, p);
if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) {
+ u_long oldflags;
+
+ oldflags = nva2.na_flags;
/*
* For V4, try setting the attributes in sets, so that the
* reply bitmap will be correct for an error case.
@@ -532,6 +537,32 @@ nfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram,
NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_MODESETMASKED);
}
}
+ if (!nd->nd_repstat &&
+ (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN) ||
+ NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_SYSTEM))) {
+ if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN)) {
+ if ((nva.na_flags & UF_HIDDEN) != 0)
+ oldflags |= UF_HIDDEN;
+ else
+ oldflags &= ~UF_HIDDEN;
+ }
+ if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_SYSTEM)) {
+ if ((nva.na_flags & UF_SYSTEM) != 0)
+ oldflags |= UF_SYSTEM;
+ else
+ oldflags &= ~UF_SYSTEM;
+ }
+ NFSVNO_ATTRINIT(&nva2);
+ NFSVNO_SETATTRVAL(&nva2, flags, oldflags);
+ nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
+ exp);
+ if (!nd->nd_repstat) {
+ if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN))
+ NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_HIDDEN);
+ if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_SYSTEM))
+ NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_SYSTEM);
+ }
+ }
#ifdef NFS4_ACL_EXTATTR_NAME
if (!nd->nd_repstat && aclp->acl_cnt > 0 &&
diff --git a/sys/kern/init_sysent.c b/sys/kern/init_sysent.c
index 34e71a0665ed..91792430d24c 100644
--- a/sys/kern/init_sysent.c
+++ b/sys/kern/init_sysent.c
@@ -658,7 +658,7 @@ struct sysent sysent[] = {
{ .sy_narg = AS(getrlimitusage_args), .sy_call = (sy_call_t *)sys_getrlimitusage, .sy_auevent = AUE_NULL, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 589 = getrlimitusage */
{ .sy_narg = AS(fchroot_args), .sy_call = (sy_call_t *)sys_fchroot, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 590 = fchroot */
{ .sy_narg = AS(setcred_args), .sy_call = (sy_call_t *)sys_setcred, .sy_auevent = AUE_SETCRED, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 591 = setcred */
- { .sy_narg = AS(exterrctl_args), .sy_call = (sy_call_t *)sys_exterrctl, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 592 = exterrctl */
+ { .sy_narg = AS(exterrctl_args), .sy_call = (sy_call_t *)sys_exterrctl, .sy_auevent = AUE_NULL, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 592 = exterrctl */
{ .sy_narg = AS(inotify_add_watch_at_args), .sy_call = (sy_call_t *)sys_inotify_add_watch_at, .sy_auevent = AUE_INOTIFY, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 593 = inotify_add_watch_at */
{ .sy_narg = AS(inotify_rm_watch_args), .sy_call = (sy_call_t *)sys_inotify_rm_watch, .sy_auevent = AUE_INOTIFY, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 594 = inotify_rm_watch */
};
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index ac4b6ac3f457..406236fc2723 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -38,9 +38,11 @@
#include "opt_ddb.h"
#include "opt_ktrace.h"
+#define EXTERR_CATEGORY EXTERR_CAT_FILEDESC
#include <sys/systm.h>
#include <sys/capsicum.h>
#include <sys/conf.h>
+#include <sys/exterrvar.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/filedesc.h>
@@ -492,6 +494,7 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg)
int error, flg, kif_sz, seals, tmp, got_set, got_cleared;
uint64_t bsize;
off_t foffset;
+ int flags;
error = 0;
flg = F_POSIX;
@@ -511,6 +514,11 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg)
error = kern_dup(td, FDDUP_FCNTL, FDDUP_FLAG_CLOEXEC, fd, tmp);
break;
+ case F_DUPFD_CLOFORK:
+ tmp = arg;
+ error = kern_dup(td, FDDUP_FCNTL, FDDUP_FLAG_CLOFORK, fd, tmp);
+ break;
+
case F_DUP2FD:
tmp = arg;
error = kern_dup(td, FDDUP_FIXED, 0, fd, tmp);
@@ -528,6 +536,7 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg)
if (fde != NULL) {
td->td_retval[0] =
((fde->fde_flags & UF_EXCLOSE) ? FD_CLOEXEC : 0) |
+ ((fde->fde_flags & UF_FOCLOSE) ? FD_CLOFORK : 0) |
((fde->fde_flags & UF_RESOLVE_BENEATH) ?
FD_RESOLVE_BENEATH : 0);
error = 0;
@@ -545,6 +554,7 @@ kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg)
*/
fde->fde_flags = (fde->fde_flags & ~UF_EXCLOSE) |
((arg & FD_CLOEXEC) != 0 ? UF_EXCLOSE : 0) |
+ ((arg & FD_CLOFORK) != 0 ? UF_FOCLOSE : 0) |
((arg & FD_RESOLVE_BENEATH) != 0 ?
UF_RESOLVE_BENEATH : 0);
error = 0;
@@ -916,7 +926,17 @@ revert_f_setfl:
break;
default:
- error = EINVAL;
+ if ((cmd & ((1u << F_DUP3FD_SHIFT) - 1)) != F_DUP3FD)
+ return (EXTERROR(EINVAL, "invalid fcntl cmd"));
+ /* Handle F_DUP3FD */
+ flags = (cmd >> F_DUP3FD_SHIFT);
+ if ((flags & ~(FD_CLOEXEC | FD_CLOFORK)) != 0)
+ return (EXTERROR(EINVAL, "invalid flags for F_DUP3FD"));
+ tmp = arg;
+ error = kern_dup(td, FDDUP_FIXED,
+ ((flags & FD_CLOEXEC) != 0 ? FDDUP_FLAG_CLOEXEC : 0) |
+ ((flags & FD_CLOFORK) != 0 ? FDDUP_FLAG_CLOFORK : 0),
+ fd, tmp);
break;
}
return (error);
@@ -946,7 +966,7 @@ kern_dup(struct thread *td, u_int mode, int flags, int old, int new)
fdp = p->p_fd;
oioctls = NULL;
- MPASS((flags & ~(FDDUP_FLAG_CLOEXEC)) == 0);
+ MPASS((flags & ~(FDDUP_FLAG_CLOEXEC | FDDUP_FLAG_CLOFORK)) == 0);
MPASS(mode < FDDUP_LASTMODE);
AUDIT_ARG_FD(old);
@@ -971,8 +991,10 @@ kern_dup(struct thread *td, u_int mode, int flags, int old, int new)
goto unlock;
if (mode == FDDUP_FIXED && old == new) {
td->td_retval[0] = new;
- if (flags & FDDUP_FLAG_CLOEXEC)
+ if ((flags & FDDUP_FLAG_CLOEXEC) != 0)
fdp->fd_ofiles[new].fde_flags |= UF_EXCLOSE;
+ if ((flags & FDDUP_FLAG_CLOFORK) != 0)
+ fdp->fd_ofiles[new].fde_flags |= UF_FOCLOSE;
error = 0;
goto unlock;
}
@@ -1047,10 +1069,9 @@ kern_dup(struct thread *td, u_int mode, int flags, int old, int new)
fde_copy(oldfde, newfde);
filecaps_copy_finish(&oldfde->fde_caps, &newfde->fde_caps,
nioctls);
- if ((flags & FDDUP_FLAG_CLOEXEC) != 0)
- newfde->fde_flags = oldfde->fde_flags | UF_EXCLOSE;
- else
- newfde->fde_flags = oldfde->fde_flags & ~UF_EXCLOSE;
+ newfde->fde_flags = (oldfde->fde_flags & ~(UF_EXCLOSE | UF_FOCLOSE)) |
+ ((flags & FDDUP_FLAG_CLOEXEC) != 0 ? UF_EXCLOSE : 0) |
+ ((flags & FDDUP_FLAG_CLOFORK) != 0 ? UF_FOCLOSE : 0);
#ifdef CAPABILITIES
seqc_write_end(&newfde->fde_seqc);
#endif
@@ -1416,13 +1437,15 @@ kern_close(struct thread *td, int fd)
}
static int
-close_range_cloexec(struct thread *td, u_int lowfd, u_int highfd)
+close_range_flags(struct thread *td, u_int lowfd, u_int highfd, int flags)
{
struct filedesc *fdp;
struct fdescenttbl *fdt;
struct filedescent *fde;
- int fd;
+ int fd, fde_flags;
+ fde_flags = ((flags & CLOSE_RANGE_CLOEXEC) != 0 ? UF_EXCLOSE : 0) |
+ ((flags & CLOSE_RANGE_CLOFORK) != 0 ? UF_FOCLOSE : 0);
fdp = td->td_proc->p_fd;
FILEDESC_XLOCK(fdp);
fdt = atomic_load_ptr(&fdp->fd_files);
@@ -1434,7 +1457,7 @@ close_range_cloexec(struct thread *td, u_int lowfd, u_int highfd)
for (; fd <= highfd; fd++) {
fde = &fdt->fdt_ofiles[fd];
if (fde->fde_file != NULL)
- fde->fde_flags |= UF_EXCLOSE;
+ fde->fde_flags |= fde_flags;
}
out_locked:
FILEDESC_XUNLOCK(fdp);
@@ -1492,8 +1515,8 @@ kern_close_range(struct thread *td, int flags, u_int lowfd, u_int highfd)
return (EINVAL);
}
- if ((flags & CLOSE_RANGE_CLOEXEC) != 0)
- return (close_range_cloexec(td, lowfd, highfd));
+ if ((flags & (CLOSE_RANGE_CLOEXEC | CLOSE_RANGE_CLOFORK)) != 0)
+ return (close_range_flags(td, lowfd, highfd, flags));
return (close_range_impl(td, lowfd, highfd));
}
@@ -1513,7 +1536,7 @@ sys_close_range(struct thread *td, struct close_range_args *uap)
AUDIT_ARG_CMD(uap->highfd);
AUDIT_ARG_FFLAGS(uap->flags);
- if ((uap->flags & ~(CLOSE_RANGE_CLOEXEC)) != 0)
+ if ((uap->flags & ~(CLOSE_RANGE_CLOEXEC | CLOSE_RANGE_CLOFORK)) != 0)
return (EINVAL);
return (kern_close_range(td, uap->flags, uap->lowfd, uap->highfd));
}
@@ -2172,6 +2195,7 @@ _finstall(struct filedesc *fdp, struct file *fp, int fd, int flags,
#endif
fde->fde_file = fp;
fde->fde_flags = ((flags & O_CLOEXEC) != 0 ? UF_EXCLOSE : 0) |
+ ((flags & O_CLOFORK) != 0 ? UF_FOCLOSE : 0) |
((flags & O_RESOLVE_BENEATH) != 0 ? UF_RESOLVE_BENEATH : 0);
if (fcaps != NULL)
filecaps_move(fcaps, &fde->fde_caps);
@@ -2432,6 +2456,7 @@ fdcopy(struct filedesc *fdp)
newfdp->fd_freefile = fdp->fd_freefile;
FILEDESC_FOREACH_FDE(fdp, i, ofde) {
if ((ofde->fde_file->f_ops->fo_flags & DFLAG_PASSABLE) == 0 ||
+ (ofde->fde_flags & UF_FOCLOSE) != 0 ||
!fhold(ofde->fde_file)) {
if (newfdp->fd_freefile == fdp->fd_freefile)
newfdp->fd_freefile = i;
@@ -2729,6 +2754,12 @@ fdcloseexec(struct thread *td)
fdfree(fdp, i);
(void) closefp(fdp, i, fp, td, false, false);
FILEDESC_UNLOCK_ASSERT(fdp);
+ } else if (fde->fde_flags & UF_FOCLOSE) {
+ /*
+ * https://austingroupbugs.net/view.php?id=1851
+ * FD_CLOFORK should not be preserved across exec
+ */
+ fde->fde_flags &= ~UF_FOCLOSE;
}
}
}
diff --git a/sys/kern/sys_pipe.c b/sys/kern/sys_pipe.c
index 9340779918a2..ed651da96b14 100644
--- a/sys/kern/sys_pipe.c
+++ b/sys/kern/sys_pipe.c
@@ -548,7 +548,7 @@ sys_pipe2(struct thread *td, struct pipe2_args *uap)
{
int error, fildes[2];
- if (uap->flags & ~(O_CLOEXEC | O_NONBLOCK))
+ if ((uap->flags & ~(O_CLOEXEC | O_CLOFORK | O_NONBLOCK)) != 0)
return (EINVAL);
error = kern_pipe(td, fildes, uap->flags, NULL, NULL);
if (error)
diff --git a/sys/kern/uipc_syscalls.c b/sys/kern/uipc_syscalls.c
index ad8485028987..133724ac76c5 100644
--- a/sys/kern/uipc_syscalls.c
+++ b/sys/kern/uipc_syscalls.c
@@ -151,6 +151,10 @@ kern_socket(struct thread *td, int domain, int type, int protocol)
type &= ~SOCK_CLOEXEC;
oflag |= O_CLOEXEC;
}
+ if ((type & SOCK_CLOFORK) != 0) {
+ type &= ~SOCK_CLOFORK;
+ oflag |= O_CLOFORK;
+ }
if ((type & SOCK_NONBLOCK) != 0) {
type &= ~SOCK_NONBLOCK;
fflag |= FNONBLOCK;
@@ -352,7 +356,8 @@ kern_accept4(struct thread *td, int s, struct sockaddr *sa, int flags,
goto done;
#endif
error = falloc_caps(td, &nfp, &fd,
- (flags & SOCK_CLOEXEC) ? O_CLOEXEC : 0, &fcaps);
+ ((flags & SOCK_CLOEXEC) != 0 ? O_CLOEXEC : 0) |
+ ((flags & SOCK_CLOFORK) != 0 ? O_CLOFORK : 0), &fcaps);
if (error != 0)
goto done;
SOCK_LOCK(head);
@@ -435,7 +440,7 @@ int
sys_accept4(struct thread *td, struct accept4_args *uap)
{
- if (uap->flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
+ if ((uap->flags & ~(SOCK_CLOEXEC | SOCK_CLOFORK | SOCK_NONBLOCK)) != 0)
return (EINVAL);
return (accept1(td, uap->s, uap->name, uap->anamelen, uap->flags));
@@ -557,6 +562,10 @@ kern_socketpair(struct thread *td, int domain, int type, int protocol,
type &= ~SOCK_CLOEXEC;
oflag |= O_CLOEXEC;
}
+ if ((type & SOCK_CLOFORK) != 0) {
+ type &= ~SOCK_CLOFORK;
+ oflag |= O_CLOFORK;
+ }
if ((type & SOCK_NONBLOCK) != 0) {
type &= ~SOCK_NONBLOCK;
fflag |= FNONBLOCK;
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
index 72bd0246db11..0056dac65c7d 100644
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -3463,7 +3463,8 @@ unp_externalize(struct mbuf *control, struct mbuf **controlp, int flags)
UNP_LINK_UNLOCK_ASSERT();
- fdflags = (flags & MSG_CMSG_CLOEXEC) ? O_CLOEXEC : 0;
+ fdflags = ((flags & MSG_CMSG_CLOEXEC) ? O_CLOEXEC : 0) |
+ ((flags & MSG_CMSG_CLOFORK) ? O_CLOFORK : 0);
error = 0;
if (controlp != NULL) /* controlp == NULL => free control messages */
diff --git a/sys/sys/fcntl.h b/sys/sys/fcntl.h
index dd9fccf5cf38..18d3928e91c7 100644
--- a/sys/sys/fcntl.h
+++ b/sys/sys/fcntl.h
@@ -144,6 +144,10 @@ typedef __pid_t pid_t;
#define O_XATTR O_NAMEDATTR /* Solaris compatibility */
#endif
+#if __POSIX_VISIBLE >= 202405
+#define O_CLOFORK 0x08000000
+#endif
+
/*
* !!! DANGER !!!
*
@@ -280,6 +284,16 @@ typedef __pid_t pid_t;
#define F_GET_SEALS 20
#define F_ISUNIONSTACK 21 /* Kludge for libc, don't use it. */
#define F_KINFO 22 /* Return kinfo_file for this fd */
+#endif /* __BSD_VISIBLE */
+
+#if __POSIX_VISIBLE >= 202405
+#define F_DUPFD_CLOFORK 23 /* Like F_DUPFD, but FD_CLOFORK is set */
+#endif
+
+#if __BSD_VISIBLE
+#define F_DUP3FD 24 /* Used with dup3() */
+
+#define F_DUP3FD_SHIFT 16 /* Shift used for F_DUP3FD */
/* Seals (F_ADD_SEALS, F_GET_SEALS). */
#define F_SEAL_SEAL 0x0001 /* Prevent adding sealings */
@@ -292,6 +306,9 @@ typedef __pid_t pid_t;
#define FD_CLOEXEC 1 /* close-on-exec flag */
#define FD_RESOLVE_BENEATH 2 /* all lookups relative to fd have
O_RESOLVE_BENEATH semantics */
+#if __POSIX_VISIBLE >= 202405
+#define FD_CLOFORK 4 /* close-on-fork flag */
+#endif
/* record locking flags (F_GETLK, F_SETLK, F_SETLKW) */
#define F_RDLCK 1 /* shared or read lock */
diff --git a/sys/sys/filedesc.h b/sys/sys/filedesc.h
index 55969b2ff4b3..0a388c90de26 100644
--- a/sys/sys/filedesc.h
+++ b/sys/sys/filedesc.h
@@ -149,6 +149,7 @@ struct filedesc_to_leader {
*/
#define UF_EXCLOSE 0x01 /* auto-close on exec */
#define UF_RESOLVE_BENEATH 0x02 /* lookups must be beneath this dir */
+#define UF_FOCLOSE 0x04 /* auto-close on fork */
#ifdef _KERNEL
@@ -221,6 +222,7 @@ enum {
/* Flags for kern_dup(). */
#define FDDUP_FLAG_CLOEXEC 0x1 /* Atomically set UF_EXCLOSE. */
+#define FDDUP_FLAG_CLOFORK 0x2 /* Atomically set UF_FOCLOSE. */
/* For backward compatibility. */
#define falloc(td, resultfp, resultfd, flags) \
diff --git a/sys/sys/socket.h b/sys/sys/socket.h
index 5e7c554c34cf..cdd4fa3b4b89 100644
--- a/sys/sys/socket.h
+++ b/sys/sys/socket.h
@@ -111,10 +111,11 @@ typedef __uintptr_t uintptr_t;
*/
#define SOCK_CLOEXEC 0x10000000
#define SOCK_NONBLOCK 0x20000000
+#define SOCK_CLOFORK 0x40000000
#ifdef _KERNEL
/*
* Flags for accept1(), kern_accept4() and solisten_dequeue, in addition
- * to SOCK_CLOEXEC and SOCK_NONBLOCK.
+ * to SOCK_CLOEXEC, SOCK_CLOFORK and SOCK_NONBLOCK.
*/
#define ACCEPT4_INHERIT 0x1
#define ACCEPT4_COMPAT 0x2
@@ -478,6 +479,9 @@ struct msghdr {
#define MSG_MORETOCOME 0x00100000 /* additional data pending */
#define MSG_TLSAPPDATA 0x00200000 /* do not soreceive() alert rec. (TLS) */
#endif
+#if __BSD_VISIBLE
+#define MSG_CMSG_CLOFORK 0x00400000 /* make received fds close-on-fork */
+#endif
/*
* Header for ancillary data objects in msg_control buffer.
diff --git a/sys/sys/unistd.h b/sys/sys/unistd.h
index c12343e5d0fd..c291c1dc2b95 100644
--- a/sys/sys/unistd.h
+++ b/sys/sys/unistd.h
@@ -211,6 +211,7 @@
* close_range() options.
*/
#define CLOSE_RANGE_CLOEXEC (1<<2)
+#define CLOSE_RANGE_CLOFORK (1<<3)
#endif /* __BSD_VISIBLE */
diff --git a/tests/Makefile b/tests/Makefile
index e8dd7793f169..451d55498a26 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -1,3 +1,5 @@
+.include <src.opts.mk>
+
PACKAGE= tests
TESTSDIR= ${TESTSBASE}
@@ -11,6 +13,9 @@ SUBDIR+= examples
SUBDIR+= include
SUBDIR+= sys
SUBDIR+= atf_python
+.if ${MK_CDDL} != "no"
+SUBDIR+= oclo
+.endif
SUBDIR_PARALLEL=
diff --git a/tests/oclo/Makefile b/tests/oclo/Makefile
new file mode 100644
index 000000000000..350c9f857c85
--- /dev/null
+++ b/tests/oclo/Makefile
@@ -0,0 +1,11 @@
+.PATH: ${SRCTOP}/cddl/contrib/opensolaris/tests/os-tests/tests/oclo
+
+TESTSDIR= ${TESTSBASE}/cddl/oclo
+
+PLAIN_TESTS_C= oclo oclo_errors ocloexec_verify
+
+SRCS.oclo= oclo.c
+LIBADD.oclo+= openbsd
+LIBADD.ocloexec_verify+= util
+
+.include <bsd.test.mk>
diff --git a/tests/sys/file/closefrom_test.c b/tests/sys/file/closefrom_test.c
index e30c5eb3d591..7dccf858c772 100644
--- a/tests/sys/file/closefrom_test.c
+++ b/tests/sys/file/closefrom_test.c
@@ -144,7 +144,7 @@ main(void)
pid_t pid;
int fd, flags, i, start;
- printf("1..21\n");
+ printf("1..22\n");
/* We'd better start up with fd's 0, 1, and 2 open. */
start = devnull();
@@ -356,5 +356,38 @@ main(void)
fail_err("close_range");
ok("close_range(..., CLOSE_RANGE_CLOEXEC)");
+ /* test CLOSE_RANGE_CLOFORK */
+ for (i = 0; i < 8; i++)
+ (void)devnull();
+ fd = highest_fd();
+ start = fd - 8;
+ if (close_range(start + 1, start + 4, CLOSE_RANGE_CLOFORK) < 0)
+ fail_err("close_range(..., CLOSE_RANGE_CLOFORK)");
+ flags = fcntl(start, F_GETFD);
+ if (flags < 0)
+ fail_err("fcntl(.., F_GETFD)");
+ if ((flags & FD_CLOFORK) != 0)
+ fail("close_range", "CLOSE_RANGE_CLOFORK set close-on-exec "
+ "when it should not have on fd %d", start);
+ for (i = start + 1; i <= start + 4; i++) {
+ flags = fcntl(i, F_GETFD);
+ if (flags < 0)
+ fail_err("fcntl(.., F_GETFD)");
+ if ((flags & FD_CLOFORK) == 0)
+ fail("close_range", "CLOSE_RANGE_CLOFORK did not set "
+ "close-on-exec on fd %d", i);
+ }
+ for (; i < start + 8; i++) {
+ flags = fcntl(i, F_GETFD);
+ if (flags < 0)
+ fail_err("fcntl(.., F_GETFD)");
+ if ((flags & FD_CLOFORK) != 0)
+ fail("close_range", "CLOSE_RANGE_CLOFORK set close-on-exec "
+ "when it should not have on fd %d", i);
+ }
+ if (close_range(start, start + 8, 0) < 0)
+ fail_err("close_range");
+ ok("close_range(..., CLOSE_RANGE_CLOFORK)");
+
return (0);
}
diff --git a/tests/sys/file/dup_test.c b/tests/sys/file/dup_test.c
index b024e72d0d1a..455115eda8c8 100644
--- a/tests/sys/file/dup_test.c
+++ b/tests/sys/file/dup_test.c
@@ -46,6 +46,8 @@
* Test #31: check if dup3(0) fails if oldfd == newfd.
* Test #32: check if dup3(O_CLOEXEC) to a fd > current maximum number of
* open files limit work.
+ * Tests #33-43 : Same as #18-26, 30 & 32 with O_CLOFORK instead of O_CLOEXEC,
+ * except F_DUP2FD_CLOEXEC.
*/
#include <sys/types.h>
@@ -82,7 +84,7 @@ main(int __unused argc, char __unused *argv[])
orgfd = getafile();
- printf("1..32\n");
+ printf("1..43\n");
/* If dup(2) ever work? */
if ((fd1 = dup(orgfd)) < 0)
@@ -380,5 +382,99 @@ main(int __unused argc, char __unused *argv[])
printf("ok %d - dup3(O_CLOEXEC) didn't bypass NOFILE limit\n",
test);
+ /* Does fcntl(F_DUPFD_CLOFORK) work? */
+ if ((fd2 = fcntl(fd1, F_DUPFD_CLOFORK, 10)) < 0)
+ err(1, "fcntl(F_DUPFD_CLOFORK)");
+ if (fd2 < 10)
+ printf("not ok %d - fcntl(F_DUPFD_CLOFORK) returned wrong fd %d\n",
+ ++test, fd2);
+ else
+ printf("ok %d - fcntl(F_DUPFD_CLOFORK) works\n", ++test);
+
+ /* Was close-on-fork cleared? */
+ ++test;
+ if (fcntl(fd2, F_GETFD) != FD_CLOFORK)
+ printf(
+ "not ok %d - fcntl(F_DUPFD_CLOFORK) didn't set close-on-fork\n",
+ test);
+ else
+ printf("ok %d - fcntl(F_DUPFD_CLOFORK) set close-on-fork\n",
+ test);
+
+ /* Does dup3(O_CLOFORK) ever work? */
+ if ((fd2 = dup3(fd1, fd1 + 1, O_CLOFORK)) < 0)
+ err(1, "dup3(O_CLOFORK)");
+ printf("ok %d - dup3(O_CLOFORK) works\n", ++test);
+
+ /* Do we get the right fd? */
+ ++test;
+ if (fd2 != fd1 + 1)
+ printf(
+ "no ok %d - dup3(O_CLOFORK) didn't give us the right fd\n",
+ test);
+ else
+ printf("ok %d - dup3(O_CLOFORK) returned a correct fd\n",
+ test);
+
+ /* Was close-on-fork set? */
+ ++test;
+ if (fcntl(fd2, F_GETFD) != FD_CLOFORK)
+ printf(
+ "not ok %d - dup3(O_CLOFORK) didn't set close-on-fork\n",
+ test);
+ else
+ printf("ok %d - dup3(O_CLOFORK) set close-on-fork\n",
+ test);
+
+ /* Does dup3(0) ever work? */
+ if ((fd2 = dup3(fd1, fd1 + 1, 0)) < 0)
+ err(1, "dup3(0)");
+ printf("ok %d - dup3(0) works\n", ++test);
+
+ /* Do we get the right fd? */
+ ++test;
+ if (fd2 != fd1 + 1)
+ printf(
+ "no ok %d - dup3(0) didn't give us the right fd\n",
+ test);
+ else
+ printf("ok %d - dup3(0) returned a correct fd\n",
+ test);
+
+ /* Was close-on-fork cleared? */
+ ++test;
+ if (fcntl(fd2, F_GETFD) != 0)
+ printf(
+ "not ok %d - dup3(0) didn't clear close-on-fork\n",
+ test);
+ else
+ printf("ok %d - dup3(0) cleared close-on-fork\n",
+ test);
+
+ /* dup3() does not allow duplicating to the same fd */
+ ++test;
+ if (dup3(fd1, fd1, O_CLOFORK) != -1)
+ printf(
+ "not ok %d - dup3(fd1, fd1, O_CLOFORK) succeeded\n", test);
+ else
+ printf("ok %d - dup3(fd1, fd1, O_CLOFORK) failed\n", test);
+
+ ++test;
+ if (dup3(fd1, fd1, 0) != -1)
+ printf(
+ "not ok %d - dup3(fd1, fd1, 0) succeeded\n", test);
+ else
+ printf("ok %d - dup3(fd1, fd1, 0) failed\n", test);
+
+ ++test;
+ if (getrlimit(RLIMIT_NOFILE, &rlp) < 0)
+ err(1, "getrlimit");
+ if ((fd2 = dup3(fd1, rlp.rlim_cur + 1, O_CLOFORK)) >= 0)
+ printf("not ok %d - dup3(O_CLOFORK) bypassed NOFILE limit\n",
+ test);
+ else
+ printf("ok %d - dup3(O_CLOFORK) didn't bypass NOFILE limit\n",
+ test);
+
return (0);
}
diff --git a/tests/sys/kern/unix_passfd_test.c b/tests/sys/kern/unix_passfd_test.c
index 95271c04a16b..7dc4541ad402 100644
--- a/tests/sys/kern/unix_passfd_test.c
+++ b/tests/sys/kern/unix_passfd_test.c
@@ -380,6 +380,30 @@ ATF_TC_BODY(simple_send_fd_msg_cmsg_cloexec, tc)
}
/*
+ * Like simple_send_fd but also sets MSG_CMSG_CLOFORK and checks that the
+ * received file descriptor has the FD_CLOFORK flag set.
+ */
+ATF_TC_WITHOUT_HEAD(simple_send_fd_msg_cmsg_clofork);
+ATF_TC_BODY(simple_send_fd_msg_cmsg_clofork, tc)
+{
+ struct stat getfd_stat, putfd_stat;
+ int fd[2], getfd, putfd;
+
+ domainsocketpair(fd);
+ tempfile(&putfd);
+ dofstat(putfd, &putfd_stat);
+ sendfd(fd[0], putfd);
+ recvfd(fd[1], &getfd, MSG_CMSG_CLOFORK);
+ dofstat(getfd, &getfd_stat);
+ samefile(&putfd_stat, &getfd_stat);
+ ATF_REQUIRE_EQ_MSG(fcntl(getfd, F_GETFD) & FD_CLOFORK, FD_CLOFORK,
+ "FD_CLOFORK not set on the received file descriptor");
+ close(putfd);
+ close(getfd);
+ closesocketpair(fd);
+}
+
+/*
* Same as simple_send_fd, only close the file reference after sending, so that
* the only reference is the descriptor in the UNIX domain socket buffer.
*/
@@ -1170,6 +1194,7 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, simple_send_fd);
ATF_TP_ADD_TC(tp, simple_send_fd_msg_cmsg_cloexec);
+ ATF_TP_ADD_TC(tp, simple_send_fd_msg_cmsg_clofork);
ATF_TP_ADD_TC(tp, send_and_close);
ATF_TP_ADD_TC(tp, send_and_cancel);
ATF_TP_ADD_TC(tp, send_and_shutdown);
diff --git a/usr.sbin/rip6query/rip6query.8 b/usr.sbin/rip6query/rip6query.8
index 856a59138bc1..92e49f5ade58 100644
--- a/usr.sbin/rip6query/rip6query.8
+++ b/usr.sbin/rip6query/rip6query.8
@@ -29,13 +29,19 @@
.\"
.\" $Id: rip6query.8,v 1.2 2000/01/19 06:24:55 itojun Exp $
.\"
-.Dd October 7, 1999
+.Dd May 20, 2025
.Dt RIP6QUERY 8
.Os
.Sh NAME
.Nm rip6query
.Nd RIPng debugging tool
.\"
+.Sh DEPRECATION NOTICE
+The
+.Nm
+utility is deprecated and will be removed in
+.Fx 16.0 .
+.\"
.Sh SYNOPSIS
.Nm
.Op Fl I Ar interface
diff --git a/usr.sbin/route6d/route6d.8 b/usr.sbin/route6d/route6d.8
index 3a7bc8721923..e9ad3266ba26 100644
--- a/usr.sbin/route6d/route6d.8
+++ b/usr.sbin/route6d/route6d.8
@@ -14,12 +14,17 @@
.\" LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
.\" A PARTICULAR PURPOSE.
.\"
-.Dd November 18, 2012
+.Dd May 20, 2025
.Dt ROUTE6D 8
.Os
.Sh NAME
.Nm route6d
.Nd RIP6 Routing Daemon
+.Sh DEPRECATION NOTICE
+The
+.Nm
+utility is deprecated and will be removed in
+.Fx 16.0 .
.Sh SYNOPSIS
.Nm
.Op Fl adDhlnqsS