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 1.67.0. Many All earlier versions reject construct_implementor
, but most just 1.70.0-1.72.0 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
open/unblocked
Milestone
Relationships
Development
No branches or pull requests
Activity
theemathas commentedon May 29, 2025
theemathas commentedon May 29, 2025
theemathas commentedon May 29, 2025
maxdexh commentedon May 29, 2025
lcnr commentedon May 29, 2025
Fun ✨
The type of
|| "whatever"
isClosure<for<'b> fn() -> &'b str>
. Its builtin impl is not well-formed as the impl signature does not constrain'a
.FnOnce
super-trait bounds which would be considered to be unconstraining by themselves14 remaining items