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<'a>, 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<dyn Any>;
+
+    let new_f = None.map(LtEq::<'a, 'b>::eq);
+
+    fn downcast_a_to_type_of_new_f<F: 'static>(any: Box<dyn Any>, _: Option<F>) -> 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<dyn Any>;
+
+    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.
+}