Close hostile recovery and concurrency gaps

This commit is contained in:
ms2k 2026-07-18 05:47:15 +09:00
commit 3f9b1b3543

View file

@ -1070,12 +1070,12 @@ where
return Ok(PidfsRecoveryMatch::BootMismatch);
}
let host_pid = identity.host_pid().get();
let pidfd = open(host_pid)?;
let pidfd = normalize_process_absence(open(host_pid))?;
let observed = inspect(pidfd.as_fd(), current_boot)?;
if observed != identity.authority() {
return Err(io::Error::other("pidfs process authority mismatch"));
}
if start(host_pid)? != identity.start_time_ticks().get() {
if normalize_process_absence(start(host_pid))? != identity.start_time_ticks().get() {
return Err(io::Error::other("process start time mismatch"));
}
Ok(PidfsRecoveryMatch::Matched(RetainedProcess {
@ -1084,6 +1084,16 @@ where
}))
}
fn normalize_process_absence<T>(result: io::Result<T>) -> io::Result<T> {
result.map_err(|error| {
if error.raw_os_error() == Some(rustix::io::Errno::NOENT.raw_os_error()) {
io::Error::from_raw_os_error(rustix::io::Errno::SRCH.raw_os_error())
} else {
error
}
})
}
/// # Errors
/// Refuses if arming fails or the exact parent changes during the recheck.
pub fn arm_parent_death_and_recheck(
@ -1745,6 +1755,50 @@ mod tests {
}
}
#[test]
fn recovery_normalizes_vanished_proc_evidence_to_esrch() {
let authority = PidfsProcessAuthorityV1 {
boot_id: BootId::new([1; 16]),
pidfs_st_dev: PidfsDeviceId::new(7),
pidfs_st_ino: PidfsInodeId::new(9),
};
let identity = PidfsProcessIdentityV1::new(
authority,
HostPidLocator::new(41),
ProcessStartTimeTicks::new(43),
);
for missing in ["open", "start"] {
let result = match_recovery_process_with(
identity,
|| Ok(authority.boot_id),
|_| {
if missing == "open" {
Err(io::Error::from_raw_os_error(
rustix::io::Errno::NOENT.raw_os_error(),
))
} else {
Ok(File::open("/dev/null").unwrap().into())
}
},
|_, _| Ok(authority),
|_| {
if missing == "start" {
Err(io::Error::from_raw_os_error(
rustix::io::Errno::NOENT.raw_os_error(),
))
} else {
Ok(43)
}
},
)
.unwrap_err();
assert_eq!(
result.raw_os_error(),
Some(rustix::io::Errno::SRCH.raw_os_error())
);
}
}
#[test]
fn recovery_action_reconciles_boot_without_process_action() {
let mut old_boot = *read_boot_id().unwrap().as_bytes();