Current-Users archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[PATCH] Add posix_spawn_file_actions_addclosefrom_np
The posix_spawn(3) API as standardized is inherently racy with respect to ensuring only desired file descriptors are inherited by the spawned process: In an application using many libraries, or an application using multiple threads, it’s not necessarily possible to know at the posix_spawn(3) point of call exactly which descriptors are will need to be explicitly closed. In particular, not all calls to open(2) in libraries can be counted on to be careful about specifying O_CLOEXEC.
Here’s a git-generated patch that adds a FreeBSD/Solaris-style posix_spawn_file_actions_addclosefrom_np(3) function to libc and support for it to the kernel, which can be used to ensure only those descriptors numbered below its argument are kept open in the spawned process. (Of course the patch also adds man page content, a test, bumps the libc minor version, adds a note to doc/CHANGES, and ensures the symbol itself is weak and guarded by _NETBSD_SOURCE in the <spawn.h> header.)
Tested on -current using a live-image on virt68k. I back-ported my changes to my 10.1 system and tested it live there as well; the patch should be easy to back-port to 11.0 or 10.x, if desired.
Finally, I also looked into adding a Darwin-style POSIX_SPAWN_CLOEXEC_DEFAULT posix_spawnattr flag, which would allow finer-grained control; unfortunately, I haven’t been able to get that working yet. Happy to share my patch for that as-is if anyone wants to put extra eyes on it.
-- Chris
PS - For those not used to git patches, they should be easy to apply locally with "patch -p1” if you’re not applying it with “git am”. (The -p1 is needed since, for some reason, “git format-patch” likes to stick “a/“ and “b/“ in front of the diff intro lines.)
From e3ff719b3cd5b5593aa769b7b5cdbb12c071ed03 Mon Sep 17 00:00:00 2001
From: Chris Hanson <cmhanson%eschatologist.net@localhost>
Date: Sat, 18 Apr 2026 16:30:18 -0700
Subject: [PATCH] Add posix_spawn_file_actions_addclosefrom_np
Add support for a closefrom(3) action to posix_spawn_file_actions_t.
This can be used to ensure all but specific low-numbered descriptors are
closed in the child process.
Rationale: In a large or complex application or one using many
libraries, the caller of posix_spawn(3) can't necessarily have complete
knowledge of the state of all descriptors that could potentially be
inherited by the child process.
---
distrib/sets/lists/base/shl.mi | 4 +-
distrib/sets/lists/debug/shl.mi | 4 +-
doc/CHANGES | 2 +
include/spawn.h | 4 ++
lib/libc/gen/posix_spawn.3 | 1 +
.../gen/posix_spawn_file_actions_addopen.3 | 39 +++++++++++++++++--
lib/libc/gen/posix_spawn_file_actions_init.3 | 1 +
lib/libc/gen/posix_spawn_fileactions.c | 26 +++++++++++++
lib/libc/include/namespace.h | 4 ++
lib/libc/shlib_version | 2 +-
sys/kern/kern_exec.c | 10 ++++-
sys/sys/spawn.h | 10 ++++-
.../lib/libc/gen/posix_spawn/h_fileactions.c | 11 ++++++
.../lib/libc/gen/posix_spawn/t_fileactions.c | 25 +++++++++++-
14 files changed, 131 insertions(+), 12 deletions(-)
diff --git distrib/sets/lists/base/shl.mi distrib/sets/lists/base/shl.mi
index dc61b3a5efa9..1655e5c40147 100644
--- distrib/sets/lists/base/shl.mi
+++ distrib/sets/lists/base/shl.mi
@@ -22,7 +22,7 @@
./lib/libblocklist.so.0.1 base-sys-shlib dynamicroot
./lib/libc.so base-sys-shlib dynamicroot
./lib/libc.so.12 base-sys-shlib dynamicroot
-./lib/libc.so.12.224 base-sys-shlib dynamicroot
+./lib/libc.so.12.225 base-sys-shlib dynamicroot
./lib/libcrypt.so base-sys-shlib dynamicroot
./lib/libcrypt.so.1 base-sys-shlib dynamicroot
./lib/libcrypt.so.1.0 base-sys-shlib dynamicroot
@@ -267,7 +267,7 @@
./usr/lib/libc++.so.1.0 base-sys-shlib compatfile,libcxx
./usr/lib/libc.so base-sys-shlib compatfile
./usr/lib/libc.so.12 base-sys-shlib compatfile
-./usr/lib/libc.so.12.224 base-sys-shlib compatfile
+./usr/lib/libc.so.12.225 base-sys-shlib compatfile
./usr/lib/libcbor.so base-sys-shlib compatfile
./usr/lib/libcbor.so.0 base-sys-shlib compatfile
./usr/lib/libcbor.so.0.5 base-sys-shlib compatfile
diff --git distrib/sets/lists/debug/shl.mi distrib/sets/lists/debug/shl.mi
index f29c98ef9319..b27607ae7876 100644
--- distrib/sets/lists/debug/shl.mi
+++ distrib/sets/lists/debug/shl.mi
@@ -3,7 +3,7 @@
./usr/libdata/debug/lib base-sys-usr debug,dynamicroot,compatdir
./usr/libdata/debug/lib/libavl.so.0.0.debug comp-zfs-debug debug,dynamicroot,zfs
./usr/libdata/debug/lib/libblocklist.so.0.1.debug comp-sys-debug debug,dynamicroot
-./usr/libdata/debug/lib/libc.so.12.224.debug comp-sys-debug debug,dynamicroot
+./usr/libdata/debug/lib/libc.so.12.225.debug comp-sys-debug debug,dynamicroot
./usr/libdata/debug/lib/libcrypt.so.1.0.debug comp-sys-debug debug,dynamicroot
./usr/libdata/debug/lib/libcrypto.so.12.0.debug comp-sys-debug debug,dynamicroot,openssl=10
./usr/libdata/debug/lib/libcrypto.so.14.1.debug comp-sys-debug debug,dynamicroot,openssl=11
@@ -89,7 +89,7 @@
./usr/libdata/debug/usr/lib/libbsdmalloc.so.0.1.debug comp-sys-debug debug,compatfile
./usr/libdata/debug/usr/lib/libbz2.so.1.1.debug comp-sys-debug debug,compatfile
./usr/libdata/debug/usr/lib/libc++.so.1.0.debug comp-sys-debug debug,compatfile,libcxx
-./usr/libdata/debug/usr/lib/libc.so.12.224.debug comp-sys-debug debug,compatfile
+./usr/libdata/debug/usr/lib/libc.so.12.225.debug comp-sys-debug debug,compatfile
./usr/libdata/debug/usr/lib/libcbor.so.0.5.debug comp-sys-debug debug,compatfile
./usr/libdata/debug/usr/lib/libcom_err.so.8.0.debug comp-krb5-debug debug,compatfile,kerberos
./usr/libdata/debug/usr/lib/libcrypt.so.1.0.debug comp-sys-debug debug,compatfile
diff --git doc/CHANGES doc/CHANGES
index 5678c01952ad..d93db8886d5d 100644
--- doc/CHANGES
+++ doc/CHANGES
@@ -238,6 +238,8 @@ Changes from NetBSD 11.0 to NetBSD 12.0:
tzdata: Updated to 2026b (using 2026bgtz) [kre 20260429]
pthread(3): add pthread_main_np(3) [wiz 20260501]
zstd(1): import 1.5.7 [christos 20260501]
+ posix_spawn(3): Add posix_spawn_file_actions_addclosefrom_np
+ function from FreeBSD. [cmh 20260418]
libarchive: Import libarchive-3.8.7. [christos 20260503]
acpi(4): Updated ACPICA to 20260408. [christos 20260503]
byacc: Update to 20260126. [christos 20260503]
diff --git include/spawn.h include/spawn.h
index 32cdd85ca27f..d5963cdb1942 100644
--- include/spawn.h
+++ include/spawn.h
@@ -60,6 +60,10 @@ int posix_spawn_file_actions_addchdir(posix_spawn_file_actions_t * __restrict,
const char * __restrict);
int posix_spawn_file_actions_addfchdir(posix_spawn_file_actions_t *, int);
+#if defined(_NETBSD_SOURCE)
+int posix_spawn_file_actions_addclosefrom_np(posix_spawn_file_actions_t *, int);
+#endif
+
/*
* Spawn attributes
*/
diff --git lib/libc/gen/posix_spawn.3 lib/libc/gen/posix_spawn.3
index 6ca4a59a0810..379da1708fd6 100644
--- lib/libc/gen/posix_spawn.3
+++ lib/libc/gen/posix_spawn.3
@@ -444,6 +444,7 @@ is returned.
.Xr vfork 2 ,
.Xr posix_spawn_file_actions_addchdir 3 ,
.Xr posix_spawn_file_actions_addclose 3 ,
+.Xr posix_spawn_file_actions_addclosefrom_np 3 ,
.Xr posix_spawn_file_actions_adddup2 3 ,
.Xr posix_spawn_file_actions_addfchdir 3 ,
.Xr posix_spawn_file_actions_addopen 3 ,
diff --git lib/libc/gen/posix_spawn_file_actions_addopen.3 lib/libc/gen/posix_spawn_file_actions_addopen.3
index 9a23d6f9e899..1f9fc26a8790 100644
--- lib/libc/gen/posix_spawn_file_actions_addopen.3
+++ lib/libc/gen/posix_spawn_file_actions_addopen.3
@@ -36,14 +36,15 @@
.\"
.\" $FreeBSD: src/lib/libc/gen/posix_spawn_file_actions_addopen.3,v 1.2.2.1.4.1 2010/06/14 02:09:06 kensmith Exp $
.\"
-.Dd February 2, 2014
+.Dd April 18, 2026
.Dt POSIX_SPAWN_FILE_ACTIONS_ADDOPEN 3
.Os
.Sh NAME
.Nm posix_spawn_file_actions_addopen ,
.Nm posix_spawn_file_actions_adddup2 ,
-.Nm posix_spawn_file_actions_addclose
-.Nd "add open, dup2 or close action to spawn file actions object"
+.Nm posix_spawn_file_actions_addclose ,
+.Nm posix_spawn_file_actions_addclosefrom_np
+.Nd "add open, dup2, close, or closefrom action to spawn file actions object"
.Sh LIBRARY
.Lb libc
.Sh SYNOPSIS
@@ -54,8 +55,10 @@
.Fn posix_spawn_file_actions_adddup2 "posix_spawn_file_actions_t * file_actions" "int fildes" "int newfildes"
.Ft int
.Fn posix_spawn_file_actions_addclose "posix_spawn_file_actions_t * file_actions" "int fildes"
+.Ft int
+.Fn posix_spawn_file_actions_addclosefrom_np "posix_spawn_file_actions_t * file_actions" "int fildes"
.Sh DESCRIPTION
-These functions add an open, dup2 or close action to a spawn
+These functions add an open, dup2, close, or closefrom action to a spawn
file actions object.
.Pp
A spawn file actions object is of type
@@ -140,6 +143,21 @@ close(fildes)
.Pp
had been called) when a new process is spawned using this file actions
object.
+.Pp
+The
+.Fn posix_spawn_file_actions_addclosefrom_np
+function adds a closefrom action to the object
+referenced by
+.Fa file_actions
+that causes the file descriptor
+.Fa filedes
+and all higher file descriptors to be closed (as if
+.Bd -literal -offset indent
+closefrom(filedes)
+.Ed
+.Pp
+had been called) when a new process is spawned using this file actions
+object.
.Sh RETURN VALUES
Upon successful completion, these functions return zero;
otherwise, an error number is returned to indicate the error.
@@ -164,6 +182,7 @@ Insufficient memory exists to add to the spawn file actions object.
.Xr close 2 ,
.Xr dup2 2 ,
.Xr open 2 ,
+.Xr closefrom 3 ,
.Xr posix_spawn 3 ,
.Xr posix_spawn_file_actions_destroy 3 ,
.Xr posix_spawn_file_actions_init 3 ,
@@ -176,6 +195,10 @@ and
.Fn posix_spawn_file_actions_addclose
functions conform to
.St -p1003.1-2001 .
+.Pp
+The
+.Fn posix_spawn_file_actions_addclosefrom_np
+function is a non-standard extension.
.Sh HISTORY
The
.Fn posix_spawn_file_actions_addopen ,
@@ -186,5 +209,13 @@ functions first appeared in
.Fx 8.0
and imported for
.Nx 6.0 .
+.Pp
+The implementation of
+.Fn posix_spawn_file_actions_addclosefrom_np
+is inspired by FreeBSD's
+.Fn posix_spawn_file_actions_addclosefrom_np
+and first appeared in
+.Nx 12.0 .
.Sh AUTHORS
.An Ed Schouten Aq Mt ed%FreeBSD.org@localhost
+.An Chris Hanson Aq Mt cmhanson%eschatologist.net@localhost
diff --git lib/libc/gen/posix_spawn_file_actions_init.3 lib/libc/gen/posix_spawn_file_actions_init.3
index d33a09c267b3..366a68d45d16 100644
--- lib/libc/gen/posix_spawn_file_actions_init.3
+++ lib/libc/gen/posix_spawn_file_actions_init.3
@@ -89,6 +89,7 @@ Insufficient memory exists to initialize the spawn file actions object.
.Sh SEE ALSO
.Xr posix_spawn 3 ,
.Xr posix_spawn_file_actions_addclose 3 ,
+.Xr posix_spawn_file_actions_addclosefrom_np 3 ,
.Xr posix_spawn_file_actions_adddup2 3 ,
.Xr posix_spawn_file_actions_addopen 3 ,
.Xr posix_spawnp 3
diff --git lib/libc/gen/posix_spawn_fileactions.c lib/libc/gen/posix_spawn_fileactions.c
index 1ff45c97faa5..a9335013d9e8 100644
--- lib/libc/gen/posix_spawn_fileactions.c
+++ lib/libc/gen/posix_spawn_fileactions.c
@@ -40,6 +40,11 @@ __RCSID("$NetBSD: posix_spawn_fileactions.c,v 1.5 2021/11/07 14:34:30 christos E
#define MIN_SIZE 16
+#ifdef __weak_alias
+__weak_alias(posix_spawn_file_actions_addclosefrom_np,
+ _posix_spawn_file_actions_addclosefrom_np)
+#endif
+
/*
* File descriptor actions
*/
@@ -215,3 +220,24 @@ posix_spawn_file_actions_addfchdir(posix_spawn_file_actions_t *fa, int fildes)
return 0;
}
+
+int
+posix_spawn_file_actions_addclosefrom_np(posix_spawn_file_actions_t *fa,
+ int fildes)
+{
+ unsigned int i;
+ int error;
+
+ if (fildes < 0)
+ return EBADF;
+
+ error = posix_spawn_file_actions_getentry(fa, &i);
+ if (error)
+ return error;
+
+ fa->fae[i].fae_action = FAE_CLOSEFROM;
+ fa->fae[i].fae_fildes = fildes;
+ fa->len++;
+
+ return 0;
+}
diff --git lib/libc/include/namespace.h lib/libc/include/namespace.h
index 6ac8288b9123..424a7779ea22 100644
--- lib/libc/include/namespace.h
+++ lib/libc/include/namespace.h
@@ -935,6 +935,10 @@
#define dladdr __dladdr
#define fmtcheck __fmtcheck
+/* posix_spawn */
+#define posix_spawn_file_actions_addclosefrom_np \
+ _posix_spawn_file_actions_addclosefrom_np
+
/* RB trees */
#define rb_tree_init _rb_tree_init
#define rb_tree_find_node _rb_tree_find_node
diff --git lib/libc/shlib_version lib/libc/shlib_version
index 7bcdd8dcbcb5..ad43eb5ef62d 100644
--- lib/libc/shlib_version
+++ lib/libc/shlib_version
@@ -55,4 +55,4 @@
# - remove tzsetwall(3), upstream has removed it
# - move *rand48* to libcompat
major=12
-minor=224
+minor=225
diff --git sys/kern/kern_exec.c sys/kern/kern_exec.c
index 0d1b498ab303..0621f4d66ea8 100644
--- sys/kern/kern_exec.c
+++ sys/kern/kern_exec.c
@@ -2154,7 +2154,7 @@ handle_posix_spawn_file_actions(struct posix_spawn_file_actions *actions)
{
struct lwp *l = curlwp;
register_t retval;
- int error = 0, newfd;
+ int error = 0, newfd, fd;
if (actions == NULL)
return 0;
@@ -2201,6 +2201,14 @@ handle_posix_spawn_file_actions(struct posix_spawn_file_actions *actions)
case FAE_FCHDIR:
error = do_sys_fchdir(l, fae->fae_fildes, &retval);
break;
+ case FAE_CLOSEFROM:
+ /*
+ * as above, ignore failures from close().
+ */
+ for (fd = fae->fae_fildes; fd <= l->l_fd->fd_lastfile; fd++)
+ if (fd_getfile(fd) != NULL)
+ fd_close(fd);
+ break;
}
if (error)
return error;
diff --git sys/sys/spawn.h sys/sys/spawn.h
index b04db01bd03c..ecca5c9ad541 100644
--- sys/sys/spawn.h
+++ sys/sys/spawn.h
@@ -47,7 +47,15 @@ struct posix_spawnattr {
sigset_t sa_sigmask;
};
-enum fae_action { FAE_OPEN, FAE_DUP2, FAE_CLOSE, FAE_CHDIR, FAE_FCHDIR };
+enum fae_action {
+ FAE_OPEN,
+ FAE_DUP2,
+ FAE_CLOSE,
+ FAE_CHDIR,
+ FAE_FCHDIR,
+ FAE_CLOSEFROM,
+};
+
typedef struct posix_spawn_file_actions_entry {
enum fae_action fae_action;
diff --git tests/lib/libc/gen/posix_spawn/h_fileactions.c tests/lib/libc/gen/posix_spawn/h_fileactions.c
index c658600bbf1c..0d9637b7d1ec 100644
--- tests/lib/libc/gen/posix_spawn/h_fileactions.c
+++ tests/lib/libc/gen/posix_spawn/h_fileactions.c
@@ -100,6 +100,17 @@ main(int argc, char **argv)
fprintf(stderr, "%s: stat results differ\n", getprogname());
res = EXIT_FAILURE;
}
+ /* file descs 8 and 9 should be closed (via addclosefrom_np) */
+ if (read(8, buf, BUFSIZE) != -1 || errno != EBADF) {
+ fprintf(stderr, "%s: filedesc 8 is not closed\n",
+ getprogname());
+ res = EXIT_FAILURE;
+ }
+ if (read(9, buf, BUFSIZE) != -1 || errno != EBADF) {
+ fprintf(stderr, "%s: filedesc 9 is not closed\n",
+ getprogname());
+ res = EXIT_FAILURE;
+ }
return res;
}
diff --git tests/lib/libc/gen/posix_spawn/t_fileactions.c tests/lib/libc/gen/posix_spawn/t_fileactions.c
index 57e27a0ada46..c8763ebcc42d 100644
--- tests/lib/libc/gen/posix_spawn/t_fileactions.c
+++ tests/lib/libc/gen/posix_spawn/t_fileactions.c
@@ -274,7 +274,7 @@ ATF_TC_HEAD(t_spawn_fileactions, tc)
}
ATF_TC_BODY(t_spawn_fileactions, tc)
{
- int fd1, fd2, fd3, status;
+ int fd1, fd2, fd3, fd4, fd5, fd6, fd7, status;
pid_t pid;
char * const args[2] = { __UNCONST("h_fileactions"), NULL };
char helper[FILENAME_MAX];
@@ -297,6 +297,29 @@ ATF_TC_BODY(t_spawn_fileactions, tc)
RZ(posix_spawn_file_actions_addopen(&fa, 6, "/dev/null", O_RDWR, 0));
RZ(posix_spawn_file_actions_adddup2(&fa, 1, 7));
+ /*
+ * Open descriptors 6 & 7 so we can get to 8 & 9 for testing
+ * closefrom_np, but close 6 & 7 before the spawn since the
+ * child process will get them via the open and dup2 actions
+ * above.
+ */
+ RL(fd4 = open("/dev/null", O_RDONLY));
+ ATF_REQUIRE(fd4 == 6);
+
+ RL(fd5 = open("/dev/null", O_RDONLY));
+ ATF_REQUIRE(fd5 == 7);
+
+ RL(fd6 = open("/dev/null", O_RDONLY));
+ ATF_REQUIRE(fd6 == 8);
+
+ RL(fd7 = open("/dev/null", O_RDONLY));
+ ATF_REQUIRE(fd7 == 9);
+
+ close(fd4);
+ close(fd5);
+
+ RZ(posix_spawn_file_actions_addclosefrom_np(&fa, 8));
+
snprintf(helper, sizeof helper, "%s/h_fileactions",
atf_tc_get_config_var(tc, "srcdir"));
RZ(posix_spawn(&pid, helper, &fa, NULL, args, NULL));
--
2.54.0
Home |
Main Index |
Thread Index |
Old Index