git: b4e2c9abaf14 - main - libcasper: tolerate kernels without PD_NOWAITPID
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 06 Oct 2026 08:11:31 UTC
The branch main has been updated by delphij:
URL: https://cgit.FreeBSD.org/src/commit/?id=b4e2c9abaf14d6eabbce59656d448c064e0dc40b
commit b4e2c9abaf14d6eabbce59656d448c064e0dc40b
Author: Xin LI <delphij@FreeBSD.org>
AuthorDate: 2026-10-06 07:17:21 +0000
Commit: Xin LI <delphij@FreeBSD.org>
CommitDate: 2026-10-06 08:10:57 +0000
libcasper: tolerate kernels without PD_NOWAITPID
Commit 1a296762b3d0 made libcasper pass PD_NOWAITPID to pdfork(2).
Kernels predating the flag (15.1 and earlier, since the flag first
ships in 15.2) reject it with EINVAL, which makes cap_init() and every
service fork fail when a newer world runs on an older kernel, for
example in a poudriere jail.
Add casper_pdfork(), which retries without the flag on EINVAL, and use
it at both pdfork(2) call sites. The retry is safe because the kernel
validates pdfork flags before creating a child. On such kernels the
zombie must still be reaped with waitpid(2), as before the flag was
introduced.
The fallback is compiled out once __FreeBSD_version reaches 1700000,
so it disappears from main when stable/16 branches while remaining in
the stable/15 and stable/16 branches that need it.
Reviewed by: kib
MFC after: 3 days
Differential Revision: https://reviews.freebsd.org/D60401
---
lib/libcasper/libcasper/libcasper.c | 2 +-
lib/libcasper/libcasper/libcasper_impl.h | 23 +++++++++++++++++++++++
lib/libcasper/libcasper/zygote.c | 2 +-
3 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/lib/libcasper/libcasper/libcasper.c b/lib/libcasper/libcasper/libcasper.c
index 8783529d9dc0..9a34b8d7acea 100644
--- a/lib/libcasper/libcasper/libcasper.c
+++ b/lib/libcasper/libcasper/libcasper.c
@@ -95,7 +95,7 @@ cap_init(void)
return (NULL);
}
- pid = pdfork(&pfd, PD_NOWAITPID);
+ pid = casper_pdfork(&pfd);
if (pid == 0) {
/* Child. */
close(sock[0]);
diff --git a/lib/libcasper/libcasper/libcasper_impl.h b/lib/libcasper/libcasper/libcasper_impl.h
index 43d2495cacf6..682a71fff01c 100644
--- a/lib/libcasper/libcasper/libcasper_impl.h
+++ b/lib/libcasper/libcasper/libcasper_impl.h
@@ -33,6 +33,10 @@
#ifndef _LIBCASPER_IMPL_H_
#define _LIBCASPER_IMPL_H_
+#include <sys/param.h>
+#include <sys/procdesc.h>
+
+#include <errno.h>
#include <stdbool.h>
#include "libcasper.h"
@@ -44,6 +48,25 @@ struct service_connection;
bool fd_is_valid(int fd);
void fd_fix_environment(int *fdp);
+static inline pid_t
+casper_pdfork(int *fdp)
+{
+ pid_t pid;
+
+ pid = pdfork(fdp, PD_NOWAITPID);
+#if __FreeBSD_version < 1700000
+ /*
+ * Kernels predating PD_NOWAITPID (introduced in 15.2) reject the
+ * flag with EINVAL before creating a child, so retrying without
+ * it is safe. The child must then be reaped with waitpid(2), as
+ * before. This fallback is compiled out starting with 17.0.
+ */
+ if (pid == -1 && errno == EINVAL)
+ pid = pdfork(fdp, 0);
+#endif
+ return (pid);
+}
+
/* Private service functions. */
struct service *service_alloc(const char *name,
service_limit_func_t *limitfunc,
diff --git a/lib/libcasper/libcasper/zygote.c b/lib/libcasper/libcasper/zygote.c
index 8295312b6593..574d80a16b01 100644
--- a/lib/libcasper/libcasper/zygote.c
+++ b/lib/libcasper/libcasper/zygote.c
@@ -149,7 +149,7 @@ zygote_main(int *sockp)
error = errno;
goto send;
}
- pid = pdfork(&procfd, PD_NOWAITPID);
+ pid = casper_pdfork(&procfd);
switch (pid) {
case -1:
/* Failure. */