Hello,
Following the same pattern identified in #1237, ProcessInner::death_delivered is a safe function that does not enforce the invariant documented on delivered_links, relying entirely on callers to uphold a precondition that is neither checked nor expressed in the type system. Calling it with a mismatched NodeDeath can cause a null pointer dereference by invoking only safe Rust functions.
Confirmed on: Linux kernel 7.1.0-rc5 mainline (8fde5d1d47f6).
PoC
let name = CStr::from_bytes_with_nul(b"q\0").map_err(|_| EINVAL)?;
let ctx = crate::Context::new(name)?;
let local_file = kernel::fs::LocalFile::fget(3)?;
let cred = local_file.cred();
let proc_a = Process::new(ctx.clone().into(), ARef::from(cred))?;
let proc_b = Process::new(ctx.into(), ARef::from(cred))?;
let thread_a = proc_a.as_arc_borrow().get_current_thread()?;
let node_ref = Process::get_node(
proc_a.as_arc_borrow(), 0x660, 4, 2, false, &thread_a,
)?;
let node = node_ref.node.clone();
let death: DArc<NodeDeath> = {
let uninit = kernel::sync::UniqueArc::new_uninit(GFP_KERNEL)?;
let init = NodeDeath::new(node, proc_a.clone(), 4);
let death = uninit.pin_init_with(init).map_err(|_| ENOMEM)?;
Arc::from(death)
};
{
let mut inner = proc_b.inner.lock();
inner.death_delivered(death.clone());
}
proc_a.remove_from_delivered_deaths(&death);
Process::deferred_release(proc_b);
Hello,
Following the same pattern identified in #1237,
ProcessInner::death_deliveredis a safe function that does not enforce the invariant documented ondelivered_links, relying entirely on callers to uphold a precondition that is neither checked nor expressed in the type system. Calling it with a mismatchedNodeDeathcan cause a null pointer dereference by invoking only safe Rust functions.Confirmed on: Linux kernel 7.1.0-rc5 mainline (
8fde5d1d47f6).PoC