Skip to content

dropck combined with type erasure allows use after free  #25199

Closed
@rkjnsn

Description

@rkjnsn

While I was trying attempting (and failing) to implement pythonesque's suggestion of using dropck to ensure that cycles were not visible from the destructors of any of the contained objects (thus allowing cycles and a cycle collector without the possibility of deref ever failing), I came across a situation (explicitly including a type-erased box in the structure) that seemed like it really should cause the compiler to enforce what I wanted it to, and that it was being overly permissive.

I have now created a test case that shows that this can, indeed, be used to obtain incorrect behavior. Specifically, this code...

use std::cell::RefCell;

struct VecHolder {
    v: Vec<u32>,
}

impl Drop for VecHolder {
    fn drop(&mut self) {
        println!("Dropping Vec");
    }
}

struct Container<'a> {
    v: VecHolder,
    d: RefCell<Vec<Box<Drop+'a>>>,
}

impl<'a> Container<'a> {
    fn new() -> Container<'a> {
        Container{d: RefCell::new(Vec::new()), v: VecHolder{v: vec![42; 100]}}
    }

    fn store<T: 'a+Drop>(&'a self, val: T) {
        self.d.borrow_mut().push(Box::new(val));
    }
}

struct Test<'a> {
    test: &'a Container<'a>,
}

impl<'a> Drop for Test<'a> {
    fn drop(&mut self) {
        println!("Val from Vec: {}", self.test.v.v[30]);
    }
}

fn main() {
    let container = Container::new();
    let test = Test{test: &container};
    container.store(test);
}

will reliably output the following:

Dropping Vec
Val from Vec: 42

You can see it in action on the playpen.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions