-
Notifications
You must be signed in to change notification settings - Fork 13.3k
dropck combined with type erasure allows use after free #25199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
cc @pnkfelix, perhaps related to dropck? |
You can get this behavior with traits other than |
@pythonesque http://is.gd/18YV2M struct Bar { _x: [u8] }
impl Drop for Bar {
fn drop(&mut self) {
println!("dropping")
}
}
fn main() {
let _x: Box<Bar> = unsafe { std::mem::transmute(Box::new([]) as Box<[u8]>) };
}
(Clickable: #16758) |
Actually... now that I think about it, it may be a different problem: it seems like use std::cell::RefCell;
struct VecHolder {
v: Vec<u32>,
}
impl Drop for VecHolder {
fn drop(&mut self) {
println!("Dropping Vec");
}
}
trait Foo {}
impl<'a> Foo for Test<'a> {}
struct Container<'a> {
v: VecHolder,
d: RefCell<Vec<Box<Foo + 'a>>>,
}
impl<'a> Container<'a> {
fn new() -> Container<'a> {
Container{d: RefCell::new(Vec::new()), v: VecHolder{v: vec![42; 100]}}
}
fn store(&'a self, val: Box<Foo + 'a>) {
self.d.borrow_mut().push(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, test);
container = Container::new();
test = Test{test: &container};
container.store(Box::new(test) as Box<Foo>);
} The problem appears to be occuring because we don't actually have to write it like this (rooting things that reference each other in the same let binding) for |
Yeah the implementation obviously ignored this note from the RFC:
Will fix ASAP |
Implements this (previously overlooked) note from [RFC 769]: > (Note: When encountering a D of the form `Box<Trait+'b>`, we > conservatively assume that such a type has a Drop implementation > parametric in 'b.) Fix rust-lang#25199. [breaking-change] The breakage here falls into both obvious and non-obvious cases. The obvious case: if you were relying on the unsoundness this exposes (namely being able to reference dead storage from a destructor, by doing it via a boxed trait object bounded by the lifetime of the dead storage), then this change disallows that. The non-obvious cases: The way dropck works, it causes lifetimes to be extended to longer extents than they covered before. I.e. lifetimes that are attached as trait-bounds may become longer than they were previously. * This includes lifetimes that are only *implicitly* attached as trait-bounds (due to [RFC 599]). So you may have code that was e.g. taking a parameter of type `&'a Box<Trait>` (which expands to `&'a Box<Trait+'a>`), that now may need to be assigned type `&'a Box<Trait+'static>` to ensure that `'a` is not inadvertantly inferred to a region that is actually too long. (See earlier commit in this PR for an example of this.) [RFC 769]: https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#the-drop-check-rule [RFC 599]: https://github.com/rust-lang/rfcs/blob/master/text/0599-default-object-bound.md
dropck: must assume `Box<Trait + 'a>` has a destructor of interest. Fix #25199. This detail was documented in [RFC 769]; the implementation was just missing. [breaking-change] The breakage here falls into both obvious and non-obvious cases. The obvious case: if you were relying on the unsoundness this exposes (namely being able to reference dead storage from a destructor, by doing it via a boxed trait object bounded by the lifetime of the dead storage), then this change disallows that. The non-obvious cases: The way dropck works, it causes lifetimes to be extended to longer extents than they covered before. I.e. lifetimes that are attached as trait-bounds may become longer than they were previously. * This includes lifetimes that are only *implicitly* attached as trait-bounds (due to [RFC 599]). So you may have code that was e.g. taking a parameter of type `&'a Box<Trait>` (which expands to `&'a Box<Trait+'a>`), that now may need to be assigned type `&'a Box<Trait+'static>` to ensure that `'a` is not inadvertantly inferred to a region that is actually too long. (See commit ee06263 for an example of this.) [RFC 769]: https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#the-drop-check-rule [RFC 599]: https://github.com/rust-lang/rfcs/blob/master/text/0599-default-object-bound.md
dropck: must assume `Box<Trait + 'a>` has a destructor of interest. Fix #25199. This detail was documented in [RFC 769]; the implementation was just missing. [breaking-change] The breakage here falls into both obvious and non-obvious cases. The obvious case: if you were relying on the unsoundness this exposes (namely being able to reference dead storage from a destructor, by doing it via a boxed trait object bounded by the lifetime of the dead storage), then this change disallows that. The non-obvious cases: The way dropck works, it causes lifetimes to be extended to longer extents than they covered before. I.e. lifetimes that are attached as trait-bounds may become longer than they were previously. * This includes lifetimes that are only *implicitly* attached as trait-bounds (due to [RFC 599]). So you may have code that was e.g. taking a parameter of type `&'a Box<Trait>` (which expands to `&'a Box<Trait+'a>`), that now may need to be assigned type `&'a Box<Trait+'static>` to ensure that `'a` is not inadvertantly inferred to a region that is actually too long. (See commit ee06263 for an example of this.) [RFC 769]: https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#the-drop-check-rule [RFC 599]: https://github.com/rust-lang/rfcs/blob/master/text/0599-default-object-bound.md
Implements this (previously overlooked) note from [RFC 769]: > (Note: When encountering a D of the form `Box<Trait+'b>`, we > conservatively assume that such a type has a Drop implementation > parametric in 'b.) Fix rust-lang#25199. [breaking-change] The breakage here falls into both obvious and non-obvious cases. The obvious case: if you were relying on the unsoundness this exposes (namely being able to reference dead storage from a destructor, by doing it via a boxed trait object bounded by the lifetime of the dead storage), then this change disallows that. The non-obvious cases: The way dropck works, it causes lifetimes to be extended to longer extents than they covered before. I.e. lifetimes that are attached as trait-bounds may become longer than they were previously. * This includes lifetimes that are only *implicitly* attached as trait-bounds (due to [RFC 599]). So you may have code that was e.g. taking a parameter of type `&'a Box<Trait>` (which expands to `&'a Box<Trait+'a>`), that now may need to be assigned type `&'a Box<Trait+'static>` to ensure that `'a` is not inadvertantly inferred to a region that is actually too long. (See earlier commit in this PR for an example of this.) [RFC 769]: https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#the-drop-check-rule [RFC 599]: https://github.com/rust-lang/rfcs/blob/master/text/0599-default-object-bound.md Conflicts: src/librustc_typeck/check/dropck.rs
Implements this (previously overlooked) note from [RFC 769]: > (Note: When encountering a D of the form `Box<Trait+'b>`, we > conservatively assume that such a type has a Drop implementation > parametric in 'b.) Fix rust-lang#25199. [breaking-change] The breakage here falls into both obvious and non-obvious cases. The obvious case: if you were relying on the unsoundness this exposes (namely being able to reference dead storage from a destructor, by doing it via a boxed trait object bounded by the lifetime of the dead storage), then this change disallows that. The non-obvious cases: The way dropck works, it causes lifetimes to be extended to longer extents than they covered before. I.e. lifetimes that are attached as trait-bounds may become longer than they were previously. * This includes lifetimes that are only *implicitly* attached as trait-bounds (due to [RFC 599]). So you may have code that was e.g. taking a parameter of type `&'a Box<Trait>` (which expands to `&'a Box<Trait+'a>`), that now may need to be assigned type `&'a Box<Trait+'static>` to ensure that `'a` is not inadvertantly inferred to a region that is actually too long. (See earlier commit in this PR for an example of this.) [RFC 769]: https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#the-drop-check-rule [RFC 599]: https://github.com/rust-lang/rfcs/blob/master/text/0599-default-object-bound.md
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...
will reliably output the following:
You can see it in action on the playpen.
The text was updated successfully, but these errors were encountered: