From 227a289461ba629abd29ea1f8a6939cddf2312de Mon Sep 17 00:00:00 2001 From: inq Date: Tue, 5 May 2026 09:57:57 +0700 Subject: [PATCH] add known-bug test for coroutine 'static-yields-non-'static unsoundness Coroutine variant of the closure / impl Fn 'static unsoundness family. References in PR description. --- .../static-coroutine-with-nonstatic-yield.rs | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 tests/ui/coroutine/static-coroutine-with-nonstatic-yield.rs diff --git a/tests/ui/coroutine/static-coroutine-with-nonstatic-yield.rs b/tests/ui/coroutine/static-coroutine-with-nonstatic-yield.rs new file mode 100644 index 0000000000000..99b53fae8bce0 --- /dev/null +++ b/tests/ui/coroutine/static-coroutine-with-nonstatic-yield.rs @@ -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` 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; + +fn make_coro<'a>() +-> impl Coroutine>>, Return = ()> + 'static { + #[coroutine] + || { + let storage: Rc>> = 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>>, Return = ()> + 'static, + G: Coroutine>>, Return = ()> + 'static, +>( + x: Pin>, + _: G, +) -> &'static Payload { + let mut g: Pin> = *(Box::new(x) as Box).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`. +}