-
Notifications
You must be signed in to change notification settings - Fork 14k
Description
This unsoundness is an exploitation of the weirdness in #115175.
The below code causes use-after-free in safe code. (Prints garbage data in my testing.)
In the function extend, rust should have prohibited constructing the type Outer::<T, Inner<T>>, since Inner has a T: 'static bound, while T doesn't have such a lifetime bound in extend. As a result, rust runs the destructor of Outer with incorrect lifetimes, which runs the into_dyn function of Inner with incorrect lifetimes, which then allows unsound lifetime extension.
(Trying to do anything with the Outer::<T, Inner<T>> value other than implicitly running its destructor seems to cause a compile error.)
(Edit: Simplified the code to remove one Drop impl.)
use std::cell::OnceCell;
use std::fmt::Display;
use std::marker::PhantomData;
use std::rc::Rc;
type Storage = Rc<OnceCell<Box<dyn Display + 'static>>>;
trait IntoDyn<T> {
fn into_dyn(input: T, output: Storage);
}
struct Inner<T: Display + 'static>(PhantomData<T>);
impl<T: Display> IntoDyn<T> for Inner<T> {
fn into_dyn(input: T, output: Storage) {
output
.set(Box::new(input))
.ok()
.unwrap();
}
}
struct Outer<T, U: IntoDyn<T>> {
input: Option<T>,
output: Storage,
_phantom: PhantomData<U>,
}
impl<T, U: IntoDyn<T>> Drop for Outer<T, U> {
fn drop(&mut self) {
U::into_dyn(self.input.take().unwrap(), self.output.clone());
}
}
fn extend<T: Display>(x: T) -> Box<dyn Display + 'static> {
let storage = Rc::new(OnceCell::new());
{
let _ = Outer::<T, Inner<T>> {
input: Some(x),
output: storage.clone(),
_phantom: PhantomData,
};
}
Rc::into_inner(storage).unwrap().into_inner().unwrap()
}
fn main() {
let wrong = {
let data = String::from("abc");
extend::<&String>(&data)
};
println!("{wrong}");
}Meta
Reproducible on the playground with stable rust 1.91.1, and with nightly rust 1.93.0-nightly (2025-11-10 29a69716f2c0f19b5f91)
Metadata
Metadata
Assignees
Labels
Type
Projects
Status