From 5782cb927ede98978f8f76fec2cf0867ff19166b Mon Sep 17 00:00:00 2001 From: Andrei Ivanou Date: Sun, 17 May 2026 11:08:40 +0200 Subject: [PATCH] fix(process): safely exit forked child after spawn Co-Authored-By: Michael Murphy --- src/process.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/process.rs b/src/process.rs index 2b6c4e0..e2cb0f7 100644 --- a/src/process.rs +++ b/src/process.rs @@ -5,7 +5,7 @@ use smol::io::AsyncReadExt; use std::io; use std::os::fd::OwnedFd; -use std::process::{Command, Stdio, exit}; +use std::process::{Command, Stdio}; #[cfg(feature = "tokio")] use tokio::io::AsyncReadExt; @@ -56,24 +56,33 @@ pub async fn spawn(mut command: Command) -> Option { match unsafe { libc::fork() } { // Parent process - 1.. => { + fork_pid @ 1..=i32::MAX => { // Drop copy of write end, then read PID from pipe drop(write); let pid = read_from_pipe(read).await; - // wait to prevent zombie - _ = rustix::process::wait(rustix::process::WaitOptions::empty()); + _ = rustix::process::waitpid( + rustix::process::Pid::from_raw(fork_pid), + rustix::process::WaitOptions::empty(), + ); pid } // Child process 0 => { let _res = rustix::process::setsid(); - if let Ok(child) = command.spawn() { + let exit_status = if let Ok(child) = command.spawn() { // Write PID to pipe let _ = rustix::io::write(write, &child.id().to_be_bytes()); - } + 0 + } else { + 1 + }; - exit(0) + // # Safety + // Required for child fork to exit without affecting the parent. + unsafe { + libc::_exit(exit_status); + } } ..=-1 => {