diff --git a/src/utils/clone3.rs b/src/utils/clone3.rs index 04826d24..7382271d 100644 --- a/src/utils/clone3.rs +++ b/src/utils/clone3.rs @@ -8,22 +8,6 @@ use { uapi::{Msghdr, MsghdrMut, OwnedFd, c}, }; -#[derive(Default, Copy, Clone)] -#[expect(non_camel_case_types, dead_code)] -struct clone_args { - flags: u64, - pidfd: u64, - child_tid: u64, - parent_tid: u64, - exit_signal: u64, - stack: u64, - stack_size: u64, - tls: u64, - set_tid: u64, - set_tid_size: u64, - cgroup: u64, -} - pub enum Forked { Parent { pid: c::pid_t, pidfd: OwnedFd }, Child { pidfd: Option }, @@ -32,33 +16,25 @@ pub enum Forked { const REAPER_VAR: &str = "JAY_REAPER_PID"; pub fn fork_with_pidfd(pidfd_for_child: bool) -> Result { - let mut pidfd: c::c_int = 0; - let mut args = clone_args { - flags: c::CLONE_PIDFD as u64, - pidfd: (&mut pidfd as *mut c::c_int) as _, - exit_signal: c::SIGCHLD as _, - ..Default::default() - }; let mut child_pidfd = None; if pidfd_for_child { child_pidfd = Some(uapi::pidfd_open(uapi::getpid(), 0).unwrap()); } + let (p, c) = uapi::socketpair(c::AF_UNIX, c::SOCK_DGRAM | c::SOCK_CLOEXEC, 0) + .map_err(|e| ForkerError::Socketpair(e.into()))?; unsafe { - let pid = c::syscall( - c::SYS_clone3, - &mut args as *const _ as usize, - size_of::(), - ); - if let Err(e) = uapi::map_err!(pid) { - return Err(ForkerError::Fork(e.into())); - } + let pid = uapi::fork().map_err(|e| ForkerError::Fork(e.into()))?; let res = if pid == 0 { + drop(p); env::remove_var(REAPER_VAR); + let pidfd = uapi::pidfd_open(uapi::getpid(), 0).unwrap(); + send_pidfd(&c, &pidfd); Forked::Child { pidfd: child_pidfd } } else { + drop(c); Forked::Parent { pid: pid as _, - pidfd: OwnedFd::new(pidfd), + pidfd: recv_pidfd(&p)?, } }; Ok(res) @@ -71,47 +47,17 @@ pub fn double_fork() -> Result, ForkerError> { match fork_with_pidfd(false)? { Forked::Parent { pid, .. } => { drop(c); - let mut buf = [MaybeUninit::::uninit(); 128]; - let iov: &mut [&mut [u8]] = &mut []; - let mut msghdr = MsghdrMut { - iov, - control: Some(&mut buf), - name: uapi::sockaddr_none_mut(), - flags: 0, - }; let _wait = OnDrop(|| { let _ = uapi::waitpid(pid, 0); }); - let (_, _, mut ctrl) = uapi::recvmsg(p.raw(), &mut msghdr, c::MSG_CMSG_CLOEXEC) - .map_err(|e| ForkerError::RecvPidfd(e.into()))?; - let (_, hdr, data) = - uapi::cmsg_read(&mut ctrl).map_err(|e| ForkerError::CmsgRead(e.into()))?; - if hdr.cmsg_level != c::SOL_SOCKET || hdr.cmsg_type != c::SCM_RIGHTS { - return Err(ForkerError::InvalidCmsg); - } - let Ok(fd) = uapi::pod_read(data) else { - return Err(ForkerError::InvalidCmsg); - }; - Ok(Some(OwnedFd::new(fd))) + recv_pidfd(&p).map(Some) } Forked::Child { .. } => { drop(p); if let Ok(f) = fork_with_pidfd(true) { match f { Forked::Parent { pidfd, .. } => { - let pidfd = pidfd.raw(); - let mut buf = [MaybeUninit::uninit(); 128]; - let mut hdr: c::cmsghdr = uapi::pod_zeroed(); - hdr.cmsg_level = c::SOL_SOCKET; - hdr.cmsg_type = c::SCM_RIGHTS; - let _ = uapi::cmsg_write(&mut &mut buf[..], hdr, &pidfd); - let iov: &[&[u8]] = &[]; - let msghdr = Msghdr { - iov, - control: Some(&buf[..uapi::cmsg_space(size_of_val(&pidfd))]), - name: uapi::sockaddr_none_ref(), - }; - let _ = uapi::sendmsg(c.raw(), &msghdr, 0); + send_pidfd(&c, &pidfd); } Forked::Child { pidfd } => { let pidfd = pidfd.unwrap(); @@ -176,3 +122,40 @@ fn set_deathsig() { c::prctl(c::PR_SET_PDEATHSIG, c::SIGKILL as c::c_ulong); } } + +fn send_pidfd(socket: &OwnedFd, pidfd: &OwnedFd) { + let pidfd = pidfd.raw(); + let mut buf = [MaybeUninit::uninit(); 128]; + let mut hdr: c::cmsghdr = uapi::pod_zeroed(); + hdr.cmsg_level = c::SOL_SOCKET; + hdr.cmsg_type = c::SCM_RIGHTS; + let _ = uapi::cmsg_write(&mut &mut buf[..], hdr, &pidfd); + let iov: &[&[u8]] = &[]; + let msghdr = Msghdr { + iov, + control: Some(&buf[..uapi::cmsg_space(size_of_val(&pidfd))]), + name: uapi::sockaddr_none_ref(), + }; + let _ = uapi::sendmsg(socket.raw(), &msghdr, 0); +} + +fn recv_pidfd(socket: &OwnedFd) -> Result { + let mut buf = [MaybeUninit::::uninit(); 128]; + let iov: &mut [&mut [u8]] = &mut []; + let mut msghdr = MsghdrMut { + iov, + control: Some(&mut buf), + name: uapi::sockaddr_none_mut(), + flags: 0, + }; + let (_, _, mut ctrl) = uapi::recvmsg(socket.raw(), &mut msghdr, c::MSG_CMSG_CLOEXEC) + .map_err(|e| ForkerError::RecvPidfd(e.into()))?; + let (_, hdr, data) = uapi::cmsg_read(&mut ctrl).map_err(|e| ForkerError::CmsgRead(e.into()))?; + if hdr.cmsg_level != c::SOL_SOCKET || hdr.cmsg_type != c::SCM_RIGHTS { + return Err(ForkerError::InvalidCmsg); + } + let Ok(fd) = uapi::pod_read(data) else { + return Err(ForkerError::InvalidCmsg); + }; + Ok(OwnedFd::new(fd)) +}