-
Notifications
You must be signed in to change notification settings - Fork 13.1k
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
Weak::new() segfaults on uninhabited types #48493
Comments
This is a nice one :) |
Seems like we should be able to fix it by replacing the union MaybeInitialized<T> {
initialized: T,
uninitialized: (),
} right? |
@sfackler, yeah seems like that might work (admittedly, I’ve not thought about it too deeply). I’m assuming the unstable |
Or rather, std would allow |
Yeah, std doesn’t need to worry about that.
…On Sat, Feb 24, 2018 at 7:32 AM vitalyd ***@***.***> wrote:
Or rather, std would allow T: !Copy (the T: Copy is required for stable).
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#48493 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ABY2UQ-hbEs8ffqB5pkAhNiLjPvIGRkrks5tYCt0gaJpZM4SRtVH>
.
|
There is a bit of a complication unfortunately - |
Not sure - if the inner struct contains a bare |
The |
Hm yeah ideally we'd avoid changing the actual memory layout. Do you have a minimal example of where the segfault is coming from? The playground indicates that zero is being stored at the address 1, but I'm not actually sure where that comes from... |
Hm so actually this reduction indicates that the problem is actually with the destructor here, not with the constructor. Specifically That means when we attempt to access the pointer in the destructor it segfaults as we're storing the decremented refcount to address 0. I wonder if this fix doesn't need to hold off until we get unsized unions? (or rather it'd still be present even if we had unsized unions) |
|
Is struct MaybeInitialized<T> {
_align: [T; 0],
data: [u8; size_of::<T>()],
} |
@sfackler That doesn't work for DSTs either (since |
Oh right :( |
@rkruppe oh bug or not |
@alexcrichton I was trying to say that the size being zero (and hence the dangling pointer being dereferenced in the destructor) is just is a consequence of the uninhabitedness. |
Ah yeah definitely, but I also see now how |
I've sent a PR for this at #49248 |
@alexcrichton, not sure it really matters here but the constructor also fails:
The above still generates a bogus mov instruction. |
@vitalyd interesting! Apparently that's reduced down to: #![feature(box_syntax)]
use std::mem;
enum Void {}
struct RcBox<T> {
_a: usize,
_b: T,
}
pub unsafe fn bar() {
mem::forget(box RcBox {
_a: 1,
_b: mem::uninitialized::<Void>(),
});
} The |
This commit is a fix for rust-lang#48493 where calling `Weak::new` where `T` is an uninhabited type would segfault. The cause for this issue was pretty subtle and the fix here is mostly a holdover until rust-lang#47650 is implemented. The `Weak<!>` struct internally contains a `NonNull<RcBox<!>>`. The `RcBox<!>` type is uninhabited, however, as it directly embeds the `!` type. Consequently the size of `RcBox<!>` is zero, which means that `NonNull<RcBox<!>>` always contains a pointer with a value of 1. Currently all boxes of zero-sized-types are actually pointers to the address 1 (as they shouldn't be read anyway). The problem comes about when later on we have a method called `Weak::inner` which previously returned `&RcBox<T>`. This was actually invalid because the instance of `&RcBox<T> ` never existed (only the uninitialized part). This means that when we try to actually modify `&RcBox`'s weak count in the destructor for `Weak::new` we're modifying the address 1! This ends up causing a segfault. This commit takes the strategy of modifying the `Weak::inner` method to return an `Option<&RcBox<T>>` that is `None` whenever the size of `RcBox<T>` is 0 (so it couldn't actually exist). This does unfortunately add more dispatch code to operations like `Weak<Any>::clone`. Eventually the "correct" fix for this is to have `RcBox<T>` store a union of `T` and a ZST like `()`, and that way the size of `RcBox<T>` will never be 0 and we won't need this branch. Until rust-lang#47650 is implemented, though, we can't use `union`s and unsized types together. Closes rust-lang#48493
De-nominating as this is blocked on #47650 |
I cannot reproduce this segfault anymore, neither with the original test case nor the one in #48493 (comment). I believe this is because the size of With the issue fixed in practice as far as I can tell and #51851 removing the use of |
(Tested in 1.28.0-nightly (84804c3 2018-06-26).) |
Sorry, the above with only without optimization. With optimizations, the second test case fails with "illegal instruction" in Nightly and the first prints With #51851 however, the first test case |
…hton Rc: remove unused allocation and fix segfault in Weak::new() Same as rust-lang#50357 did for `Arc`. Fixes rust-lang#48493
This gives a segmentation fault:
There's no rust backtrace to show; I could reproduce this on various platforms and compiler versions.
The text was updated successfully, but these errors were encountered: