diff options
| author | Matthias Krüger <matthias.krueger@famsik.de> | 2024-12-22 21:59:24 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-12-22 21:59:24 +0100 |
| commit | 6ade237b6c624b5b19214b321395ca43e60a18ad (patch) | |
| tree | 36e26188c457c6cca58b262f6955b5ced5615065 /library/core | |
| parent | bd160f11f00823c5a3a9ee615907e238e44f2e81 (diff) | |
| parent | 2305012e6a819805fdcf9207d865fa4dbda58f81 (diff) | |
| download | rust-6ade237b6c624b5b19214b321395ca43e60a18ad.tar.gz rust-6ade237b6c624b5b19214b321395ca43e60a18ad.zip | |
Rollup merge of #134583 - Enselic:maybe-uninit-transmute, r=workingjubilee
docs: `transmute<&mut T, &mut MaybeUninit<T>>` is unsound when exposed to safe code Closes #66699 On my system (Edit: And also in the [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=90529e2a9900599cb759e4bfaa5b5efe)) the example program terminates with an unpredictable exit code: ```console $ cargo +nightly build && target/debug/bin ; echo $? 255 $ cargo +nightly build && target/debug/bin ; echo $? 253 ``` And miri considers the code to have undefined behavior: ```console $ cargo +nightly miri run error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory --> src/main.rs:12:24 | 12 | std::process::exit(*code); // UB! Accessing uninitialized memory | ^^^^^ using uninitialized data, but this operation requires initialized memory | error: aborting due to 1 previous error ```
Diffstat (limited to 'library/core')
| -rw-r--r-- | library/core/src/mem/maybe_uninit.rs | 20 |
1 files changed, 20 insertions, 0 deletions
diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 9b3d6902098..58fb5be5812 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -232,6 +232,26 @@ use crate::{fmt, intrinsics, ptr, slice}; /// remain `#[repr(transparent)]`. That said, `MaybeUninit<T>` will *always* guarantee that it has /// the same size, alignment, and ABI as `T`; it's just that the way `MaybeUninit` implements that /// guarantee may evolve. +/// +/// Note that even though `T` and `MaybeUninit<T>` are ABI compatible it is still unsound to +/// transmute `&mut T` to `&mut MaybeUninit<T>` and expose that to safe code because it would allow +/// safe code to access uninitialized memory: +/// +/// ```rust,no_run +/// use core::mem::MaybeUninit; +/// +/// fn unsound_transmute<T>(val: &mut T) -> &mut MaybeUninit<T> { +/// unsafe { core::mem::transmute(val) } +/// } +/// +/// fn main() { +/// let mut code = 0; +/// let code = &mut code; +/// let code2 = unsound_transmute(code); +/// *code2 = MaybeUninit::uninit(); +/// std::process::exit(*code); // UB! Accessing uninitialized memory. +/// } +/// ``` #[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"] |
