libcasper: tolerate kernels without PD_NOWAITPID

Commit 1a296762b3 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
This commit is contained in:
Xin LI committed 2026-10-06 01:10:57 -07:00
1 parent ea23918b02
commit b4e2c9abaf
3 files changed
+25 -2

No files matched your search

+1 -1
View File
@@ -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]);
+23
View File
@@ -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,
+1 -1
View File
@@ -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. */