Open
Description
This code results in undefined behavior in safe code.
use std::any::Any;
// `for<'a> Producer<&'a T>` is equivalent to the (non-existent) `for<'a> FnOnce() -> &'a T`
pub trait Producer<T>: FnOnce() -> T {}
impl<T, F: FnOnce() -> T> Producer<T> for F {}
// What does `P::Output` even mean in this context? If this was `FnOnce(&()) -> &T`, using `Output`
// would result in "Cannot use the assiciated type of a trait with uninferred generic parameters",
// even when hiding behind an extra trait
fn write_incoherent_p2<T, P: for<'a> Producer<&'a T>>(
weird: P::Output,
out: &mut &'static dyn Any,
) {
// `T` is not even `'static`, but `P::Output` seems to kind of
// resemble `for<'a> &'a T` (if that even means anything)
*out = weird;
}
fn write_incoherent_p1<T, P: for<'a> Producer<&'a T>>(p: P, out: &mut &'static dyn Any) {
// Producing and writing p() in one function doesn't work. Doing so requires T: 'static.
// Adding T: 'static to all functions also makes this not work.
// This fragility is why every line is its own function.
write_incoherent_p2::<T, P>(p(), out)
}
// Now we can trigger unsoundness by finding something that is `FnOnce() -> &'a T` for any `'a`
// `for<'a> FnOnce() -> &'a T` is basically just the signature of Box::leak
fn construct_implementor<T>(not_static: T, out: &mut &'static dyn Any) {
write_incoherent_p1::<T, _>(|| Box::leak(Box::new(not_static)), out);
}
fn make_static_and_drop<T: 'static>(t: T) -> &'static T {
let mut out: &'static dyn Any = &();
construct_implementor::<&T>(&t, &mut out);
*out.downcast_ref::<&T>().unwrap()
}
fn main() {
println!("{:?}", make_static_and_drop(vec![vec![1]])); // use after free
}
The earliest affected version is 1.72. Many earlier versions reject construct_implementor
, but most just ICE. Current nightly is also affected.
I am not entirely sure what is going on in this code; I was trying to create a function type that returns a reference of any requested lifetime (don't ask why), i.e. for<'a> Fn() -> &'a T
. For some reason it works when hiding behind a blanket implementation, though it is very fragile. Taking Fn::Output
of such a function type yields a type that seems to kind of resemble "for<'a> &'a T
" and allows assignment to &'static dyn Any
, even though T
is not 'static
.
Metadata
Metadata
Assignees
Labels
Area: Associated items (types, constants & functions)Area: Closures (`|…| { … }`)Area: Type systemCategory: This is a bug.Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessHigh priorityRelevant to the types team, which will review and decide on the PR/issue.Performance or correctness regression from one stable version to another.
Type
Projects
Status
unknown