more fine-grained feature-detection for pidfd spawning

we now distinguish between pidfd_spawn support, pidfd-via-fork/exec and not-supported
This commit is contained in:
The 8472 2024-06-25 00:14:55 +02:00
parent 0ce361938e
commit 3e4e31b7bf

View File

@ -476,35 +476,47 @@ fn pidfd_spawnp(
weak! { fn pidfd_getpid(libc::c_int) -> libc::c_int } weak! { fn pidfd_getpid(libc::c_int) -> libc::c_int }
static PIDFD_SPAWN_SUPPORTED: AtomicU8 = AtomicU8::new(0); static PIDFD_SUPPORTED: AtomicU8 = AtomicU8::new(0);
const UNKNOWN: u8 = 0; const UNKNOWN: u8 = 0;
const YES: u8 = 1; const SPAWN: u8 = 1;
// NO currently forces a fallback to fork/exec. We could be more nuanced here and keep using spawn // Obtaining a pidfd via the fork+exec path might work
// if we know pidfd's aren't supported at all and the fallback would be futile. const FORK_EXEC: u8 = 2;
const NO: u8 = 2; // Neither pidfd_spawn nor fork/exec will get us a pidfd.
// Instead we'll just posix_spawn if the other preconditions are met.
const NO: u8 = 3;
if self.get_create_pidfd() { if self.get_create_pidfd() {
let flag = PIDFD_SPAWN_SUPPORTED.load(Ordering::Relaxed); let mut support = PIDFD_SUPPORTED.load(Ordering::Relaxed);
if flag == NO || pidfd_spawnp.get().is_none() || pidfd_getpid.get().is_none() { if support == FORK_EXEC {
return Ok(None); return Ok(None);
} }
if flag == UNKNOWN { if support == UNKNOWN {
let mut support = NO; support = NO;
let our_pid = crate::process::id(); let our_pid = crate::process::id();
let pidfd = let pidfd = cvt(unsafe { libc::syscall(libc::SYS_pidfd_open, our_pid, 0) } as c_int);
unsafe { libc::syscall(libc::SYS_pidfd_open, our_pid, 0) } as libc::c_int; match pidfd {
if pidfd >= 0 { Ok(pidfd) => {
let pid = unsafe { pidfd_getpid.get().unwrap()(pidfd) } as u32; support = FORK_EXEC;
unsafe { libc::close(pidfd) }; if let Some(Ok(pid)) = pidfd_getpid.get().map(|f| cvt(unsafe { f(pidfd) } as i32)) {
if pid == our_pid { if pidfd_spawnp.get().is_some() && pid as u32 == our_pid {
support = YES support = SPAWN
}; }
}
unsafe { libc::close(pidfd) };
}
Err(e) if e.raw_os_error() == Some(libc::EMFILE) => {
// We're temporarily(?) out of file descriptors. In this case obtaining a pidfd would also fail
// Don't update the support flag so we can probe again later.
return Err(e)
}
_ => {}
} }
PIDFD_SPAWN_SUPPORTED.store(support, Ordering::Relaxed); PIDFD_SUPPORTED.store(support, Ordering::Relaxed);
if support != YES { if support == FORK_EXEC {
return Ok(None); return Ok(None);
} }
} }
core::assert_matches::debug_assert_matches!(support, SPAWN | NO);
} }
} else { } else {
if self.get_create_pidfd() { if self.get_create_pidfd() {
@ -691,7 +703,7 @@ fn drop(&mut self) {
let spawn_fn = retrying_libc_posix_spawnp; let spawn_fn = retrying_libc_posix_spawnp;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
if self.get_create_pidfd() { if self.get_create_pidfd() && PIDFD_SUPPORTED.load(Ordering::Relaxed) == SPAWN {
let mut pidfd: libc::c_int = -1; let mut pidfd: libc::c_int = -1;
let spawn_res = pidfd_spawnp.get().unwrap()( let spawn_res = pidfd_spawnp.get().unwrap()(
&mut pidfd, &mut pidfd,
@ -706,7 +718,7 @@ fn drop(&mut self) {
if let Err(ref e) = spawn_res if let Err(ref e) = spawn_res
&& e.raw_os_error() == Some(libc::ENOSYS) && e.raw_os_error() == Some(libc::ENOSYS)
{ {
PIDFD_SPAWN_SUPPORTED.store(NO, Ordering::Relaxed); PIDFD_SUPPORTED.store(FORK_EXEC, Ordering::Relaxed);
return Ok(None); return Ok(None);
} }
spawn_res?; spawn_res?;