Closed
Description
Found while investigating the remaining wrong case in #32433. Effectively use-after-free.
Can be used to cause a segmentation fault (check out on playpen):
struct Fine<T: std::fmt::Debug>(T);
impl<T: std::fmt::Debug> Drop for Fine<T> {
fn drop(&mut self) {
print!("Fine({:?}) ", self.0);
}
}
enum Evil<'a, T: 'a+std::fmt::Debug> {
None,
Some(&'a Fine<T>)
}
impl<'a, T: std::fmt::Debug> Drop for Evil<'a, T> {
fn drop(&mut self) {
if let Evil::Some(x) = *self {
print!("Evil({:?}) ", x.0);
}
}
}
// Allocates a new 3-usize Box on drop.
struct Dirty;
impl Drop for Dirty {
fn drop(&mut self) {
let _ = Box::new((1usize, 1usize, 1usize));
}
}
fn assign<'a, T: std::fmt::Debug>(x: &mut Evil<'a, T>, _: &Dirty, y: &'a Fine<T>) {
*x = Evil::Some(y);
}
fn main() {
let mut _x = Evil::None;
assign(&mut _x, &Dirty, &Fine(Box::new(vec![1, 2, 3])))
}
The Fine
pointee drops before the Evil
holding of the pointer, and in between those the destructor of Dirty
overwrites the same heap location of the deallocated Box<Vec<i32>>
with a different 3-usize
Box
, causing a segmentation fault on Debug mode and garbage to be printed on Release mode.
EDIT: Works fine even with old trans if there is any sort of block nesting (i.e. only the outermost block of a function has the broken behavior) - check out on playpen.
cc @rust-lang/compiler @rust-lang/lang