-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Defuse the bomb that is mem::uninitialized
#87032
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -627,12 +627,22 @@ pub unsafe fn zeroed<T>() -> T { | |
} | ||
} | ||
|
||
/// Bypasses Rust's normal memory-initialization checks by pretending to | ||
/// produce a value of type `T`, while doing nothing at all. | ||
/// | ||
/// **This function is deprecated.** Use [`MaybeUninit<T>`] instead. | ||
/// | ||
/// The reason for deprecation is that the function basically cannot be used | ||
/// **This function is deprecated with extreme prejudice.** | ||
/// It is replaced by [`MaybeUninit<T>`], which must be used instead of this function. | ||
/// | ||
/// This function produces a value of type `T` that may be of any imaginable | ||
/// bit representation in memory. | ||
/// The exact value that is produced by this function is subject to no | ||
/// guarantees whatsoever, and cannot be relied upon. | ||
/// The value returned by this function may differ between implementations, | ||
/// between different versions of the same implementation, | ||
/// between different compilations of the exact same code with the exact same compiler, | ||
/// between different executions of the same program, | ||
/// between different invocations within a single program, | ||
/// between different *uses* of the same returned value, | ||
/// or for any other reason whatsoever, with no warning. | ||
/// | ||
/// The reason this function is deprecated is that it basically cannot be used | ||
/// correctly: it has the same effect as [`MaybeUninit::uninit().assume_init()`][uninit]. | ||
/// As the [`assume_init` documentation][assume_init] explains, | ||
/// [the Rust compiler assumes][inv] that values are properly initialized. | ||
|
@@ -645,21 +655,40 @@ pub unsafe fn zeroed<T>() -> T { | |
/// (Notice that the rules around uninitialized integers are not finalized yet, but | ||
/// until they are, it is advisable to avoid them.) | ||
bstrie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// | ||
/// Note: because of the aforementioned lack of guarantee about the values returned | ||
/// by this function, and because of the aforementioned inherent unsafety of this API, | ||
/// it is legal for an implementation of Rust to return initialized memory from | ||
/// this function if it so chooses, as a last-ditch safety net. | ||
/// To emphatically reiterate, neither this behavior nor the exact value of any | ||
/// initialized memory may be relied upon. | ||
/// If truly uninitialized memory is desired, then `MaybeUninit` must be used. | ||
/// | ||
/// [uninit]: MaybeUninit::uninit | ||
/// [assume_init]: MaybeUninit::assume_init | ||
/// [inv]: MaybeUninit#initialization-invariant | ||
#[inline(always)] | ||
#[rustc_deprecated(since = "1.39.0", reason = "use `mem::MaybeUninit` instead")] | ||
#[stable(feature = "rust1", since = "1.0.0")] | ||
#[allow(deprecated_in_future)] | ||
#[allow(deprecated)] | ||
#[rustc_diagnostic_item = "mem_uninitialized"] | ||
pub unsafe fn uninitialized<T>() -> T { | ||
// Under sanitizers we actually return uninitialized memory, | ||
// as they should be able to check for improper use. | ||
#[cfg(any(miri, sanitize = "memory"))] | ||
// SAFETY: the caller must guarantee that an unitialized value is valid for `T`. | ||
unsafe { | ||
intrinsics::assert_uninit_valid::<T>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you leave this call in place? That will allow us to keep the "attempted to leave type There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was under the impression from your previous comments that you were content with only panicking on can't-be-zero types. If your concern is just the diagnostic regression, I would much rather solve this some other way than reintroducing this check, since one of the points of this PR is to deliberately make it possible to use this function with types like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I may have misunderstood what was happening. These panics were intentionally added in #66059, as a way of catching misuses of uninitialized memory. Even though There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Aaron1011 I thought the whole point was that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See also @thomcc's comments about how the panics have broken several of his old crates; I would love for this to fix them. https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/disarming.20mem.3A.3Auninitialized/near/244037069 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Are there any types that are allowed to be uninitialized other than MaybeUninit (and arrays and tuples of MaybeUninit)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes:
Now, I am not saying that this is all that useful, I just want to get our facts straight so we have a solid basis to make decisions on. :) The only legitimate reason I can think of to call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Worth noting here too that even with this PR removing the panic and changing the implementation to
So even though the panic no longer exists (to the benefit of people using libraries that do this), people writing this code themselves (who will therefore be seeing warnings) will still be made aware that this isn't a good idea (even if, for the moment, it may not actually be invoking UB from the compiler's perspective). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That lint however has a lot of false negatives. For example, it will not fire when the code that calls |
||
MaybeUninit::uninit().assume_init() | ||
} | ||
|
||
// When not being sanitized we paper over the problems with this API by returning | ||
// initialized memory, so that legacy code isn't broken | ||
// and has an actual chance to be safe. | ||
#[cfg(not(any(miri, sanitize = "memory")))] | ||
// SAFETY: Because an uninitialized value does not guarantee any specific bit | ||
// representation, it is therefore no less safe to return a zeroed value. | ||
unsafe { | ||
zeroed::<T>() | ||
} | ||
} | ||
|
||
/// Swaps the values at two mutable locations, without deinitializing either one. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you looked at how this renders in rustdoc? The standard is one summary sentence, trying to keep it succinct so it renders on, in this case, the
std::mem
page. The previous one was already a teeny bit long.