Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions tests/ui/coroutine/static-coroutine-with-nonstatic-yield.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//@ 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`.
}
Loading