-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Can I create and use a *mut
to an uninitialized local?
#90718
Comments
cc @rust-lang/wg-unsafe-code-guidelines |
Why that? I think it is perfectly fine to disallow code that tries to handle uninit memory without
This seems to be the motivating factor here. Is this an example that the compiler accepts that raises similar questions? If yes, could you describe that example (or a simplified version of it)? |
Not that I can think of. This type of code was UB prior to
Both the borrow checker and Polonius use variable liveness in some cases to determine whether an outlives relationship between two regions will result in an error. However, in the following example, a borrow of a local variable is associated with a placeholder lifetime via another local, fn foo<'a>(_: &'a i32) -> &'a i32 {
let y = 42;
let x: &'a i32;
let px = std::ptr::addr_of_mut!(x);
unsafe {
std::ptr::write(px, &y);
std::ptr::read(px)
}
} This code is not UB, and Alternatively, we could decide that it's illegal to take the address of an uninitialized local (or to write to that address). That way, we don't have to handle cases like this in the borrow checker. It seems we have already done this, but I doubt it was a conscious decision, hence this issue. We could also decide that this particular use of |
Let's say you do fn main() {
let mut x: Vec<u32>;
unsafe {
std::ptr::write(std::ptr::addr_of_mut!(x), vec![0]);
}
x = vec[1];
} Should the drop impl of |
@bjorn3, that's true, but it's a problem we already have today. Consider the following: fn main() {
let mut x = vec![0u32];
let px = &mut x as *mut _;
let y = x;
// unsafe { *px = vec![1]; } // Would drop old value in `x` that has been moved into `y`, see below.
unsafe { std::ptr::write(px, vec![1]) }
// `vec![1]` will leak, since `x` is not initialized according to `rustc`.
} |
I would have expected that
|
Oh, Yeah, does stacked borrows consider moves? |
Not sure. |
Are both of these 'not' intentional? I don't quite understand the causal chain here. But without a
I think it's fine for the borrow checker to reject such code even if executing that code might or might not be UB. Whether the code is UB or not interacts not only with drop elaboration but also with the
No. But we might want to consider a move as 'writing uninit to the source of the move', which should then be considered a write for Stacked Borrows as well, which would clearly invalidate |
The following code does not compile on current nightly or stable (1.56.1).
I would expect this to work, since part of the rationale for the
addr_of
macro and raw reference operator (#64490) was to allow creation of pointers to uninitialized memory. On the other hand, I don't think the behavior of locals that are only assigned/accessed indirectly is fully specified (though I don't think my example is UB). Such locals will never be dropped, which might be surprising, and they cannot be used directly, even after being written via the pointer, without encountering the same error.MaybeUninit
is basically always a better choice for these kinds of things, so it shouldn't be of much importance to users. However, we should avoid depending on the illegality of such code inside the compiler–a more complex example arose while discussing region inference–unless we have made a conscious decision to forbid it. Alternatively, if interacting with uninitialized locals is well-defined, we should probably makeaddr_of
work on them.The text was updated successfully, but these errors were encountered: