| //@ 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`. |
| } |