about summary refs log tree commit diff
diff options
context:
space:
mode:
authorYuki Okushi <jtitor@2k36.org>2022-07-26 07:14:46 +0900
committerGitHub <noreply@github.com>2022-07-26 07:14:46 +0900
commitaeca079d7e91a2ae34b6c5f743689750257c094c (patch)
treedff6fe9557bb01a38aeea3a52229039e2820ab78
parentb8aab9781a76f4498cc3819674397dedad37f20b (diff)
parent1b3870e4271ca7b93de0b17ea6544e749fba3483 (diff)
downloadrust-aeca079d7e91a2ae34b6c5f743689750257c094c.tar.gz
rust-aeca079d7e91a2ae34b6c5f743689750257c094c.zip
Rollup merge of #99084 - RalfJung:write_bytes, r=thomcc
clarify how write_bytes can lead to UB due to invalid values

Fixes https://github.com/rust-lang/unsafe-code-guidelines/issues/330

Cc ``@5225225``
-rw-r--r--library/core/src/intrinsics.rs51
1 files changed, 14 insertions, 37 deletions
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index 4a595902282..7e65f4ebdad 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -2566,14 +2566,23 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
 ///
 /// * `dst` must be properly aligned.
 ///
-/// Additionally, the caller must ensure that writing `count *
-/// size_of::<T>()` bytes to the given region of memory results in a valid
-/// value of `T`. Using a region of memory typed as a `T` that contains an
-/// invalid value of `T` is undefined behavior.
-///
 /// Note that even if the effectively copied size (`count * size_of::<T>()`) is
 /// `0`, the pointer must be non-null and properly aligned.
 ///
+/// Additionally, note that changing `*dst` in this way can easily lead to undefined behavior (UB)
+/// later if the written bytes are not a valid representation of some `T`. For instance, the
+/// following is an **incorrect** use of this function:
+///
+/// ```rust,no_run
+/// unsafe {
+///     let mut value: u8 = 0;
+///     let ptr: *mut bool = &mut value as *mut u8 as *mut bool;
+///     let _bool = ptr.read(); // This is fine, `ptr` points to a valid `bool`.
+///     ptr.write_bytes(42u8, 1); // This function itself does not cause UB...
+///     let _bool = ptr.read(); // ...but it makes this operation UB! ⚠️
+/// }
+/// ```
+///
 /// [valid]: crate::ptr#safety
 ///
 /// # Examples
@@ -2590,38 +2599,6 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
 /// }
 /// assert_eq!(vec, [0xfefefefe, 0xfefefefe, 0, 0]);
 /// ```
-///
-/// Creating an invalid value:
-///
-/// ```
-/// use std::ptr;
-///
-/// let mut v = Box::new(0i32);
-///
-/// unsafe {
-///     // Leaks the previously held value by overwriting the `Box<T>` with
-///     // a null pointer.
-///     ptr::write_bytes(&mut v as *mut Box<i32>, 0, 1);
-/// }
-///
-/// // At this point, using or dropping `v` results in undefined behavior.
-/// // drop(v); // ERROR
-///
-/// // Even leaking `v` "uses" it, and hence is undefined behavior.
-/// // mem::forget(v); // ERROR
-///
-/// // In fact, `v` is invalid according to basic type layout invariants, so *any*
-/// // operation touching it is undefined behavior.
-/// // let v2 = v; // ERROR
-///
-/// unsafe {
-///     // Let us instead put in a valid value
-///     ptr::write(&mut v as *mut Box<i32>, Box::new(42i32));
-/// }
-///
-/// // Now the box is fine
-/// assert_eq!(*v, 42);
-/// ```
 #[doc(alias = "memset")]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[cfg_attr(not(bootstrap), rustc_allowed_through_unstable_modules)]