From 0792da3d4823dd61ad49f8b43789006b4a9aae4d Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Tue, 29 Apr 2025 07:55:37 -0700 Subject: [PATCH 1/6] Document MaybeUninit bit validity --- library/core/src/mem/maybe_uninit.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index d0be82adb6b16..ecdae885a5360 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -252,6 +252,30 @@ use crate::{fmt, intrinsics, ptr, slice}; /// std::process::exit(*code); // UB! Accessing uninitialized memory. /// } /// ``` +/// +/// # Validity +/// +/// A `MaybeUninit` has no validity requirement – any sequence of bytes of the appropriate length, +/// initialized to any value or uninitialized, are a valid value of `MaybeUninit`. Equivalently, +/// it is always sound to perform `transmute::<[MaybeUninit; size_of::()], MaybeUninit>(...)`. +/// +/// Note that "round-tripping" via `MaybeUninit` does not always result in the original value. +/// Concretely, given distinct `T` and `U` where `size_of::() == size_of::()`, 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 { +/// let u: MaybeUninit = transmute(t); +/// transmute(u) +/// } +/// ``` +/// +/// If `T` contains initialized bytes at byte offsets where `U` contains padding bytes, these +/// may not be preserved in `MaybeUninit`, 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. #[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"] From 21626ac072d2d15df29fc3ec94f84d3331675e02 Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Tue, 29 Apr 2025 08:11:51 -0700 Subject: [PATCH 2/6] Clarify that round-tripping is sound so long as bytes are initialized --- library/core/src/mem/maybe_uninit.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index ecdae885a5360..74f4688bdc0a9 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -276,6 +276,9 @@ use crate::{fmt, intrinsics, ptr, slice}; /// may not be preserved in `MaybeUninit`, 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. +/// +/// 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. #[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"] From 1494ec77daa4cc43bb06788cf882d833f9be2a13 Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Tue, 29 Apr 2025 08:39:25 -0700 Subject: [PATCH 3/6] Add unsafe block to example --- library/core/src/mem/maybe_uninit.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 74f4688bdc0a9..6c225dc46c9d4 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -267,8 +267,10 @@ use crate::{fmt, intrinsics, ptr, slice}; /// # use core::mem::{MaybeUninit, transmute}; /// # struct T; struct U; /// fn identity(t: T) -> T { -/// let u: MaybeUninit = transmute(t); -/// transmute(u) +/// unsafe { +/// let u: MaybeUninit = transmute(t); +/// transmute(u) +/// } /// } /// ``` /// From 05e6a344b67a7a880d669ed3f3390971b6dc14bd Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Tue, 29 Apr 2025 08:43:35 -0700 Subject: [PATCH 4/6] Document provenance --- library/core/src/mem/maybe_uninit.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 6c225dc46c9d4..d25f4297dbb84 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -281,6 +281,15 @@ use crate::{fmt, intrinsics, ptr, slice}; /// /// 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::

]>` 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"] From 8d3a47ec19a4f2e05df04d576d0f65e1e06ff837 Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Wed, 7 May 2025 10:31:35 -0700 Subject: [PATCH 5/6] Clarify provenance --- library/core/src/mem/maybe_uninit.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index d25f4297dbb84..8d7ebebdea50e 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -285,9 +285,8 @@ use crate::{fmt, intrinsics, ptr, slice}; /// # 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::

]>` and then back to `P` will produce a value identical to -/// `p`, including provenance. +/// value, `p: P`, which contains provenance, transmuting `p` to `MaybeUninit<[u8; size_of::

]>` +/// 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")] From 75380ea840213ebcb666837240c9e4eba871ed43 Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Wed, 7 May 2025 10:34:11 -0700 Subject: [PATCH 6/6] Clarify validity regarding initialization --- library/core/src/mem/maybe_uninit.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 8d7ebebdea50e..8a461ffcb79a2 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -278,9 +278,13 @@ use crate::{fmt, intrinsics, ptr, slice}; /// may not be preserved in `MaybeUninit`, 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. + +/// If byte offsets exists at which `T`'s representation does not permit uninitialized bytes but +/// `U`'s representation does (e.g. due to padding), then the bytes in `T` at these offsets may +/// not be preserved in `u`, and so `transmute(u)` may produce a `T` with uninitialized bytes at +/// these offsets. This is an active area of discussion, and this code may become sound in the future. /// -/// 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 no such byte offsets exist, then the preceding `identity` example *is* sound. /// /// # Provenance ///