Closed kyren closed 1 year ago
As extra evidence that this is an actual rustc
bug and not an issue with gc-arena
's abstractions, here's a piece of code using a similar trait setup and Any
to demonstrate UB with no unsafe code (playground link):
use std::any::Any;
use std::sync::OnceLock;
trait Visit {
fn visit(&self);
}
trait Visitable<'a> {
type Visit: Visit + 'a;
}
static HOLDER: OnceLock<&'static String> = OnceLock::new();
impl<T> Visit for &'static T {
fn visit(&self) {
let any = (*self) as &dyn Any;
if let Some(i) = any.downcast_ref::<&'static String>() {
HOLDER.get_or_init(|| i);
}
}
}
fn call_and_visit<V, F>(value: String, f: F)
where
V: for<'a> Visitable<'a> + ?Sized,
F: for<'a> Fn(&'a String) -> <V as Visitable<'a>>::Visit,
{
f(&value).visit();
}
type MyVisitable = dyn for<'a> Visitable<'a, Visit = &'static &'a String>;
fn main() {
call_and_visit::<MyVisitable, _>("hello".into(), |i| {
Box::leak(Box::new(i))
});
// oops, use-after-free
dbg!(HOLDER.get());
}
I believe this is an instance of https://github.com/rust-lang/rust/issues/84533, as it also involves "broken" dyn Trait
types allowing the bypass of well-formed checks.
Credit goes to me for stumbling across it and 100% to @moulins for actually finding the fix.
I do not have a link to an open rust issue for the specific bug, I can try and find a link later if @moulins doesn't already know which one it is.