blob: 99b53fae8bce020ebcf8916c97bb26989b0d32a3 [file] [edit]
//@ check-pass
//@ known-bug: #144442
// Same family as #84366 / #112905: a coroutine that yields a non-`'static`
// reference is wrongly `: 'static`, allowing a `Box<dyn Any>` downcast to
// transmute between distinct lifetime substitutions and produce a UAF.
#![forbid(unsafe_code)] // No `unsafe!`
#![feature(coroutines, coroutine_trait, stmt_expr_attributes)]
use std::any::Any;
use std::cell::RefCell;
use std::ops::{Coroutine, CoroutineState};
use std::pin::Pin;
use std::rc::Rc;
type Payload = Box<i32>;
fn make_coro<'a>()
-> impl Coroutine<Yield = Rc<RefCell<Option<&'a Payload>>>, Return = ()> + 'static {
#[coroutine]
|| {
let storage: Rc<RefCell<Option<&'a Payload>>> = Rc::new(RefCell::new(None));
yield storage.clone();
yield storage;
}
}
pub fn expand<'a>(payload: &'a Payload) -> &'static Payload {
let mut coro1 = Box::pin(make_coro::<'a>());
let coro2 = make_coro::<'static>();
let CoroutineState::Yielded(storage) = coro1.as_mut().resume(()) else {
panic!()
};
*storage.borrow_mut() = Some(payload);
extract(coro1, coro2)
}
fn extract<
'a,
F: Coroutine<Yield = Rc<RefCell<Option<&'a Payload>>>, Return = ()> + 'static,
G: Coroutine<Yield = Rc<RefCell<Option<&'static Payload>>>, Return = ()> + 'static,
>(
x: Pin<Box<F>>,
_: G,
) -> &'static Payload {
let mut g: Pin<Box<G>> = *(Box::new(x) as Box<dyn Any>).downcast().unwrap();
let CoroutineState::Yielded(storage) = g.as_mut().resume(()) else {
panic!()
};
let payload = storage.borrow().unwrap();
payload
}
fn main() {
let x = Box::new(Box::new(1i32));
let y = expand(&x);
drop(x);
println!("{y}"); // Segfaults — UAF without `unsafe`.
}