git: 02ffca404e65 - main - kern: Add support for POSIX O_CLOFORK flag

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Sun, 06 Jul 2025 23:18:35 UTC
The branch main has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=02ffca404e65b2058720e1e9d5a5bc8bb2867113

commit 02ffca404e65b2058720e1e9d5a5bc8bb2867113
Author:     Ricardo Branco <rbranco@suse.de>
AuthorDate: 2025-06-20 12:06:48 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2025-07-06 23:08:19 +0000

    kern: Add support for POSIX O_CLOFORK flag
    
    Reviewed by:    kib
    MFC after:      1 month
    Pull Request:   https://github.com/freebsd/freebsd-src/pull/1698
---
 sys/kern/kern_descrip.c | 28 ++++++++++++++++++++++------
 sys/kern/sys_pipe.c     |  2 +-
 sys/sys/fcntl.h         | 13 +++++++++++++
 sys/sys/filedesc.h      |  2 ++
 4 files changed, 38 insertions(+), 7 deletions(-)

diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index ac4b6ac3f457..bd6fa0c14075 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -511,6 +511,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 +533,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 +551,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;
@@ -946,7 +953,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 +978,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 +1056,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
@@ -2172,6 +2180,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 +2441,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 +2739,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/sys/fcntl.h b/sys/sys/fcntl.h
index dd9fccf5cf38..7234c9240c84 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,7 +284,13 @@ 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
 /* Seals (F_ADD_SEALS, F_GET_SEALS). */
 #define	F_SEAL_SEAL	0x0001		/* Prevent adding sealings */
 #define	F_SEAL_SHRINK	0x0002		/* May not shrink */
@@ -292,6 +302,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) \