Skip to content

Document MaybeUninit bit validity #140463

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

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions library/core/src/mem/maybe_uninit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,44 @@ use crate::{fmt, intrinsics, ptr, slice};
/// std::process::exit(*code); // UB! Accessing uninitialized memory.
/// }
/// ```
///
/// # Validity
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving this discussion here:

The MaybeUninit docs probably make sense for this. We now do have a definition of "byte" in the reference that this can link to.

Okay, awesome. And what wording would you recommend? Would it be accurate to say something like the following?

The value of a [MaybeUninit<u8>; N] may contain pointer provenance, and so p: P -> [MaybeUninit<u8>; N] -> P preserves the value of p, including provenance

@RalfJung would you like me to add language like this to this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: I've added the following as a more concrete and fleshed out draft. I can edit or remove as preferred.

/// # Provenance
///
/// `MaybeUninit` values may contain [pointer provenance][provenance]. Concretely, for any
/// pointer type, `P`, which contains provenance, transmuting `p: P` to
/// `MaybeUninit<[u8; size_of::<P>]>` and then back to `P` will produce a value identical to
/// `p`, including provenance.
///
/// [provenance]: ../ptr/index.html#provenance

///
/// A `MaybeUninit<T>` has no validity requirement – any sequence of bytes of the appropriate length,
/// initialized to any value or uninitialized, are a valid value of `MaybeUninit<T>`. Equivalently,
/// it is always sound to perform `transmute::<[MaybeUninit<u8>; size_of::<T>()], MaybeUninit<T>>(...)`.
///
/// Note that "round-tripping" via `MaybeUninit` does not always result in the original value.
/// Concretely, given distinct `T` and `U` where `size_of::<T>() == size_of::<U>()`, the following
/// code is not guaranteed to be sound:
///
/// ```rust,no_run
/// # use core::mem::{MaybeUninit, transmute};
/// # struct T; struct U;
/// fn identity(t: T) -> T {
/// unsafe {
/// let u: MaybeUninit<U> = transmute(t);
/// transmute(u)
/// }
/// }
/// ```
///
/// If `T` contains initialized bytes at byte offsets where `U` contains padding bytes, these
/// may not be preserved in `MaybeUninit<U>`, and so `transmute(u)` may produce a `T` with
/// uninitialized bytes in these positions. This is an active area of discussion, and this code
/// may become sound in the future.
Comment on lines +277 to +280
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RalfJung I'd like some advice on this. I'm confident that this is correct as written, but could we perhaps make a stronger statement?

In particular, what happens if we round-trip a value which is invalid for U but where U nonetheless contains initialized bytes? For example, is 3u8 -> MaybeUninit<bool> -> u8 guaranteed to produce 3u8?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per rust-lang/unsafe-code-guidelines#555 (comment), I've updated to the following text. Does that look good?

/// Note that, so long as every byte position which is initialized in `T` is also initialized
/// in `U`, then the preceding `identity` example *is* sound.

///
/// Note that, so long as every byte position which is initialized in `T` is also initialized
/// in `U`, then the preceding `identity` example *is* sound.
///
/// # Provenance
///
/// `MaybeUninit` values may contain [pointer provenance][provenance]. Concretely, for any
/// pointer type, `P`, which contains provenance, transmuting `p: P` to
/// `MaybeUninit<[u8; size_of::<P>]>` and then back to `P` will produce a value identical to
/// `p`, including provenance.
///
/// [provenance]: ../ptr/index.html#provenance
#[stable(feature = "maybe_uninit", since = "1.36.0")]
// Lang item so we can wrap other types in it. This is useful for coroutines.
#[lang = "maybe_uninit"]
Expand Down
Loading