Skip to content

Commit 1e168f7

Browse files
committed
wip: send child pidfd back to parent for waiting
This should eliminate any race conditions around PIDs.
1 parent 757d1b4 commit 1e168f7

1 file changed

Lines changed: 62 additions & 11 deletions

File tree

test/sys/test_pidfd.rs

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,44 @@ fn test_pidfd_open() {
1111

1212
#[cfg(feature = "signal")]
1313
mod send_signal {
14+
use std::{
15+
io::{IoSlice, IoSliceMut},
16+
os::fd::{AsFd as _, AsRawFd as _, FromRawFd as _, OwnedFd, RawFd},
17+
};
18+
1419
use nix::{
1520
sys::{
1621
pidfd::{
1722
pidfd_open, pidfd_send_signal, PidfdOpenFlags,
1823
PidfdSendSignalFlags,
1924
},
2025
signal::Signal,
21-
wait::{waitpid, WaitStatus},
26+
socket::{
27+
recvmsg, sendmsg, socketpair, AddressFamily, ControlMessage,
28+
ControlMessageOwned, MsgFlags, SockFlag, SockType,
29+
},
30+
wait::{waitid, Id, WaitPidFlag, WaitStatus},
2231
},
2332
unistd::{fork, getpid, ForkResult},
2433
};
2534

2635
#[test]
2736
fn test_pidfd_send_signal() {
2837
// NOTE: This function MUST be async-signal-safe.
29-
fn child_process() -> ! {
38+
fn child_process(socket: OwnedFd) -> ! {
3039
let pidfd = pidfd_open(getpid(), PidfdOpenFlags::empty())
3140
.unwrap_or_else(|_| std::process::exit(1));
3241

42+
// Send PIDFD to the parent.
43+
sendmsg::<()>(
44+
socket.as_raw_fd(),
45+
&[IoSlice::new(&[])],
46+
&[ControlMessage::ScmRights(&[pidfd.as_raw_fd()])],
47+
MsgFlags::empty(),
48+
None,
49+
)
50+
.unwrap_or_else(|_| std::process::exit(1));
51+
3352
// Code beyond this call should be unreachable.
3453
pidfd_send_signal(
3554
&pidfd,
@@ -42,21 +61,53 @@ mod send_signal {
4261
std::process::exit(1)
4362
}
4463

64+
let (socket, child_socket) = socketpair(
65+
AddressFamily::Unix,
66+
SockType::SeqPacket,
67+
None,
68+
SockFlag::empty(),
69+
)
70+
.expect("should be able to create socketpair");
71+
4572
// SAFETY: `child_process` is async-signal-safe.
46-
let child = unsafe {
73+
unsafe {
4774
match fork().expect("should be able to fork") {
48-
ForkResult::Parent { child } => child,
49-
ForkResult::Child => child_process(),
75+
ForkResult::Child => child_process(child_socket),
76+
ForkResult::Parent { .. } => (),
5077
}
5178
};
5279

80+
let mut iov = [IoSliceMut::new(&mut [])];
81+
let mut cmsg = cmsg_space!(RawFd);
82+
83+
let msg = recvmsg::<()>(
84+
socket.as_raw_fd(),
85+
&mut iov,
86+
Some(&mut cmsg),
87+
MsgFlags::empty(),
88+
)
89+
.expect("should be able to call recvmsg");
90+
91+
let cmsg = msg
92+
.cmsgs()
93+
.expect("should be able to receive cmsgs")
94+
.next()
95+
.expect("should receive 1 cmsg");
96+
97+
let child_pidfd = if let ControlMessageOwned::ScmRights(fds) = cmsg {
98+
if let [fd] = &*fds {
99+
// SAFETY: SCM_RIGHTS provides valid file descriptors.
100+
unsafe { OwnedFd::from_raw_fd(RawFd::from(*fd)) }
101+
} else {
102+
panic!("should receive one file descriptor");
103+
}
104+
} else {
105+
panic!("should receive an SCM_RIGHTS cmsg");
106+
};
107+
53108
assert!(matches!(
54-
waitpid(child, None),
55-
Ok(WaitStatus::Signaled(
56-
x,
57-
Signal::SIGKILL,
58-
_
59-
)) if x == child
109+
waitid(Id::PIDFd(child_pidfd.as_fd()), WaitPidFlag::WEXITED),
110+
Ok(WaitStatus::Signaled(_, Signal::SIGKILL, _))
60111
));
61112
}
62113
}

0 commit comments

Comments
 (0)