Skip to content

Expressions in block tail supposed to outlive block variables dropped early. #33490

Closed
@eddyb

Description

@eddyb

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-MIRArea: Mid-level IR (MIR) - https://blog.rust-lang.org/2016/04/19/MIR.html

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions