diff --git a/tests/ui/implied-bounds/dyn-erasure-no-tait.rs b/tests/ui/implied-bounds/dyn-erasure-no-tait.rs new file mode 100644 index 00000000000..b5a8f421d3d --- /dev/null +++ b/tests/ui/implied-bounds/dyn-erasure-no-tait.rs @@ -0,0 +1,53 @@ +//@ known-bug: #112905 +//@ check-pass + +// Classified as an issue with implied bounds: +// https://github.com/rust-lang/rust/issues/112905#issuecomment-1757847998 + +/// Note: this is sound! It's the "type witness" pattern (here, lt witness). +mod some_lib { + use super::T; + + /// Invariant in `'a` and `'b` for soundness. + pub struct LtEq<'a, 'b>(::std::marker::PhantomData<*mut Self>); + + impl<'a, 'b> LtEq<'a, 'b> { + pub fn new() -> LtEq<'a, 'a> { + LtEq(<_>::default()) + } + + pub fn eq(&self) -> impl 'static + Fn(T<'a>) -> T<'b> { + |a| unsafe { ::std::mem::transmute::, T<'b>>(a) } + } + } +} + +use some_lib::LtEq; +use std::{any::Any, cell::Cell}; + +/// Feel free to choose whatever you want, here. +type T<'lt> = Cell<&'lt str>; + +fn exploit<'a, 'b>(a: T<'a>) -> T<'b> { + let f = LtEq::<'a, 'a>::new().eq(); + let any = Box::new(f) as Box; + + let new_f = None.map(LtEq::<'a, 'b>::eq); + + fn downcast_a_to_type_of_new_f(any: Box, _: Option) -> F { + *any.downcast().unwrap_or_else(|_| unreachable!()) + } + + let f = downcast_a_to_type_of_new_f(any, new_f); + + f(a) +} + +fn main() { + let r: T<'static> = { + let local = String::from("…"); + let a: T<'_> = Cell::new(&local[..]); + exploit(a) + }; + dbg!(r.get()); +} diff --git a/tests/ui/implied-bounds/dyn-erasure-tait.rs b/tests/ui/implied-bounds/dyn-erasure-tait.rs new file mode 100644 index 00000000000..4766d221d67 --- /dev/null +++ b/tests/ui/implied-bounds/dyn-erasure-tait.rs @@ -0,0 +1,39 @@ +//@ known-bug: #112905 +//@ check-pass + +// Classified as an issue with implied bounds: +// https://github.com/rust-lang/rust/issues/112905#issuecomment-1757847998 + +#![forbid(unsafe_code)] // No `unsafe!` +#![feature(type_alias_impl_trait)] + +use std::any::Any; + +/// Anything covariant will do, for this demo. +type T<'lt> = &'lt str; + +type F<'a, 'b> = impl 'static + Fn(T<'a>) -> T<'b>; + +fn helper<'a, 'b>(_: [&'b &'a (); 0]) -> F<'a, 'b> { + |x: T<'a>| -> T<'b> { x } // this should *not* be `: 'static` +} + +fn exploit<'a, 'b>(a: T<'a>) -> T<'b> { + let f: F<'a, 'a> = helper([]); + let any = Box::new(f) as Box; + + let f: F<'a, 'static> = *any.downcast().unwrap_or_else(|_| unreachable!()); + + f(a) +} + +fn main() { + let r: T<'static> = { + let local = String::from("..."); + exploit(&local) + }; + // Since `r` now dangles, we can easily make the use-after-free + // point to newly allocated memory! + let _unrelated = String::from("UAF"); + dbg!(r); // may print `UAF`! Run with `miri` to see the UB. +}