Re: [PATCH] Add posix_spawn_file_actions_addclosefrom_np

Chris Hanson <[email protected]> Sun, 10 May 2026 14:26:47 -0700
Newsgroups gmane.os.netbsd.devel.kernel
Message-ID <[email protected]>
Here’s my final updated patch that also ensures the posix_spawn_file_actions_addclosefrom_np symbol is appropriately weak, guarded by _NETBSD_SOURCE, the libc version is updated, etc.

It should apply cleanly to trunk and (modulo the libc versioning) should also pull back to NetBSD 11 trivially if it’s desired there. (I’d certainly like it to go into 11 if possible, but 11 is producing release candidates, so...)

Any 

  -- Chris
0001-Add-posix_spawn_file_actions_addclosefrom_np.patch (application/octet-stream, 14.2 KB)
From 56f82a540724523352f2f940069e26a514cec125 Mon Sep 17 00:00:00 2001
From: Chris Hanson <[email protected]>
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 [email protected]
+.An Chris Hanson Aq Mt [email protected]
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