diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 7c1a9b82f99b2..305e4d707a35f 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1730,6 +1730,157 @@ extern "rust-intrinsic" { /// Allocate at compile time. Should not be called at runtime. #[rustc_const_unstable(feature = "const_heap", issue = "79597")] pub fn const_allocate(size: usize, align: usize) -> *mut u8; + + /// Copies `count * size_of::()` bytes from `src` to `dst`. The source + /// and destination must *not* overlap. + /// + /// For regions of memory which might overlap, use [`copy`] instead. + /// + /// `copy_nonoverlapping` is semantically equivalent to C's [`memcpy`], but + /// with the argument order swapped. + /// + /// [`memcpy`]: https://en.cppreference.com/w/c/string/byte/memcpy + /// + /// # Safety + /// + /// Behavior is undefined if any of the following conditions are violated: + /// + /// * `src` must be [valid] for reads of `count * size_of::()` bytes. + /// + /// * `dst` must be [valid] for writes of `count * size_of::()` bytes. + /// + /// * Both `src` and `dst` must be properly aligned. + /// + /// * The region of memory beginning at `src` with a size of `count * + /// size_of::()` bytes must *not* overlap with the region of memory + /// beginning at `dst` with the same size. + /// + /// Like [`read`], `copy_nonoverlapping` creates a bitwise copy of `T`, regardless of + /// whether `T` is [`Copy`]. If `T` is not [`Copy`], using *both* the values + /// in the region beginning at `*src` and the region beginning at `*dst` can + /// [violate memory safety][read-ownership]. + /// + /// Note that even if the effectively copied size (`count * size_of::()`) is + /// `0`, the pointers must be non-NULL and properly aligned. + /// + /// [`read`]: crate::ptr::read + /// [read-ownership]: crate::ptr::read#ownership-of-the-returned-value + /// [valid]: crate::ptr#safety + /// + /// # Examples + /// + /// Manually implement [`Vec::append`]: + /// + /// ``` + /// use std::ptr; + /// + /// /// Moves all the elements of `src` into `dst`, leaving `src` empty. + /// fn append(dst: &mut Vec, src: &mut Vec) { + /// let src_len = src.len(); + /// let dst_len = dst.len(); + /// + /// // Ensure that `dst` has enough capacity to hold all of `src`. + /// dst.reserve(src_len); + /// + /// unsafe { + /// // The call to offset is always safe because `Vec` will never + /// // allocate more than `isize::MAX` bytes. + /// let dst_ptr = dst.as_mut_ptr().offset(dst_len as isize); + /// let src_ptr = src.as_ptr(); + /// + /// // Truncate `src` without dropping its contents. We do this first, + /// // to avoid problems in case something further down panics. + /// src.set_len(0); + /// + /// // The two regions cannot overlap because mutable references do + /// // not alias, and two different vectors cannot own the same + /// // memory. + /// ptr::copy_nonoverlapping(src_ptr, dst_ptr, src_len); + /// + /// // Notify `dst` that it now holds the contents of `src`. + /// dst.set_len(dst_len + src_len); + /// } + /// } + /// + /// let mut a = vec!['r']; + /// let mut b = vec!['u', 's', 't']; + /// + /// append(&mut a, &mut b); + /// + /// assert_eq!(a, &['r', 'u', 's', 't']); + /// assert!(b.is_empty()); + /// ``` + /// + /// [`Vec::append`]: ../../std/vec/struct.Vec.html#method.append + #[doc(alias = "memcpy")] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] + pub fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); + + /// Copies `count * size_of::()` bytes from `src` to `dst`. The source + /// and destination may overlap. + /// + /// If the source and destination will *never* overlap, + /// [`copy_nonoverlapping`] can be used instead. + /// + /// `copy` is semantically equivalent to C's [`memmove`], but with the argument + /// order swapped. Copying takes place as if the bytes were copied from `src` + /// to a temporary array and then copied from the array to `dst`. + /// + /// [`memmove`]: https://en.cppreference.com/w/c/string/byte/memmove + /// + /// # Safety + /// + /// Behavior is undefined if any of the following conditions are violated: + /// + /// * `src` must be [valid] for reads of `count * size_of::()` bytes. + /// + /// * `dst` must be [valid] for writes of `count * size_of::()` bytes. + /// + /// * Both `src` and `dst` must be properly aligned. + /// + /// Like [`read`], `copy` creates a bitwise copy of `T`, regardless of + /// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the values + /// in the region beginning at `*src` and the region beginning at `*dst` can + /// [violate memory safety][read-ownership]. + /// + /// Note that even if the effectively copied size (`count * size_of::()`) is + /// `0`, the pointers must be non-NULL and properly aligned. + /// + /// [`read`]: crate::ptr::read + /// [read-ownership]: crate::ptr::read#ownership-of-the-returned-value + /// [valid]: crate::ptr#safety + /// + /// # Examples + /// + /// Efficiently create a Rust vector from an unsafe buffer: + /// + /// ``` + /// use std::ptr; + /// + /// /// # Safety + /// /// + /// /// * `ptr` must be correctly aligned for its type and non-zero. + /// /// * `ptr` must be valid for reads of `elts` contiguous elements of type `T`. + /// /// * Those elements must not be used after calling this function unless `T: Copy`. + /// # #[allow(dead_code)] + /// unsafe fn from_buf_raw(ptr: *const T, elts: usize) -> Vec { + /// let mut dst = Vec::with_capacity(elts); + /// + /// // SAFETY: Our precondition ensures the source is aligned and valid, + /// // and `Vec::with_capacity` ensures that we have usable space to write them. + /// ptr::copy(ptr, dst.as_mut_ptr(), elts); + /// + /// // SAFETY: We created it with this much capacity earlier, + /// // and the previous `copy` has initialized these elements. + /// dst.set_len(elts); + /// dst + /// } + /// ``` + #[doc(alias = "memmove")] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] + pub fn copy(src: *const T, dst: *mut T, count: usize); } // Some functions are defined here because they accidentally got made @@ -1755,192 +1906,6 @@ pub(crate) fn is_nonoverlapping(src: *const T, dst: *const T, count: usize) - diff >= size } -/// Copies `count * size_of::()` bytes from `src` to `dst`. The source -/// and destination must *not* overlap. -/// -/// For regions of memory which might overlap, use [`copy`] instead. -/// -/// `copy_nonoverlapping` is semantically equivalent to C's [`memcpy`], but -/// with the argument order swapped. -/// -/// [`memcpy`]: https://en.cppreference.com/w/c/string/byte/memcpy -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `src` must be [valid] for reads of `count * size_of::()` bytes. -/// -/// * `dst` must be [valid] for writes of `count * size_of::()` bytes. -/// -/// * Both `src` and `dst` must be properly aligned. -/// -/// * The region of memory beginning at `src` with a size of `count * -/// size_of::()` bytes must *not* overlap with the region of memory -/// beginning at `dst` with the same size. -/// -/// Like [`read`], `copy_nonoverlapping` creates a bitwise copy of `T`, regardless of -/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using *both* the values -/// in the region beginning at `*src` and the region beginning at `*dst` can -/// [violate memory safety][read-ownership]. -/// -/// Note that even if the effectively copied size (`count * size_of::()`) is -/// `0`, the pointers must be non-NULL and properly aligned. -/// -/// [`read`]: crate::ptr::read -/// [read-ownership]: crate::ptr::read#ownership-of-the-returned-value -/// [valid]: crate::ptr#safety -/// -/// # Examples -/// -/// Manually implement [`Vec::append`]: -/// -/// ``` -/// use std::ptr; -/// -/// /// Moves all the elements of `src` into `dst`, leaving `src` empty. -/// fn append(dst: &mut Vec, src: &mut Vec) { -/// let src_len = src.len(); -/// let dst_len = dst.len(); -/// -/// // Ensure that `dst` has enough capacity to hold all of `src`. -/// dst.reserve(src_len); -/// -/// unsafe { -/// // The call to offset is always safe because `Vec` will never -/// // allocate more than `isize::MAX` bytes. -/// let dst_ptr = dst.as_mut_ptr().offset(dst_len as isize); -/// let src_ptr = src.as_ptr(); -/// -/// // Truncate `src` without dropping its contents. We do this first, -/// // to avoid problems in case something further down panics. -/// src.set_len(0); -/// -/// // The two regions cannot overlap because mutable references do -/// // not alias, and two different vectors cannot own the same -/// // memory. -/// ptr::copy_nonoverlapping(src_ptr, dst_ptr, src_len); -/// -/// // Notify `dst` that it now holds the contents of `src`. -/// dst.set_len(dst_len + src_len); -/// } -/// } -/// -/// let mut a = vec!['r']; -/// let mut b = vec!['u', 's', 't']; -/// -/// append(&mut a, &mut b); -/// -/// assert_eq!(a, &['r', 'u', 's', 't']); -/// assert!(b.is_empty()); -/// ``` -/// -/// [`Vec::append`]: ../../std/vec/struct.Vec.html#method.append -#[doc(alias = "memcpy")] -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] -#[inline] -pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { - extern "rust-intrinsic" { - #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] - fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); - } - - // FIXME: Perform these checks only at run time - /*if cfg!(debug_assertions) - && !(is_aligned_and_not_null(src) - && is_aligned_and_not_null(dst) - && is_nonoverlapping(src, dst, count)) - { - // Not panicking to keep codegen impact smaller. - abort(); - }*/ - - // SAFETY: the safety contract for `copy_nonoverlapping` must be - // upheld by the caller. - unsafe { copy_nonoverlapping(src, dst, count) } -} - -/// Copies `count * size_of::()` bytes from `src` to `dst`. The source -/// and destination may overlap. -/// -/// If the source and destination will *never* overlap, -/// [`copy_nonoverlapping`] can be used instead. -/// -/// `copy` is semantically equivalent to C's [`memmove`], but with the argument -/// order swapped. Copying takes place as if the bytes were copied from `src` -/// to a temporary array and then copied from the array to `dst`. -/// -/// [`memmove`]: https://en.cppreference.com/w/c/string/byte/memmove -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `src` must be [valid] for reads of `count * size_of::()` bytes. -/// -/// * `dst` must be [valid] for writes of `count * size_of::()` bytes. -/// -/// * Both `src` and `dst` must be properly aligned. -/// -/// Like [`read`], `copy` creates a bitwise copy of `T`, regardless of -/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the values -/// in the region beginning at `*src` and the region beginning at `*dst` can -/// [violate memory safety][read-ownership]. -/// -/// Note that even if the effectively copied size (`count * size_of::()`) is -/// `0`, the pointers must be non-NULL and properly aligned. -/// -/// [`read`]: crate::ptr::read -/// [read-ownership]: crate::ptr::read#ownership-of-the-returned-value -/// [valid]: crate::ptr#safety -/// -/// # Examples -/// -/// Efficiently create a Rust vector from an unsafe buffer: -/// -/// ``` -/// use std::ptr; -/// -/// /// # Safety -/// /// -/// /// * `ptr` must be correctly aligned for its type and non-zero. -/// /// * `ptr` must be valid for reads of `elts` contiguous elements of type `T`. -/// /// * Those elements must not be used after calling this function unless `T: Copy`. -/// # #[allow(dead_code)] -/// unsafe fn from_buf_raw(ptr: *const T, elts: usize) -> Vec { -/// let mut dst = Vec::with_capacity(elts); -/// -/// // SAFETY: Our precondition ensures the source is aligned and valid, -/// // and `Vec::with_capacity` ensures that we have usable space to write them. -/// ptr::copy(ptr, dst.as_mut_ptr(), elts); -/// -/// // SAFETY: We created it with this much capacity earlier, -/// // and the previous `copy` has initialized these elements. -/// dst.set_len(elts); -/// dst -/// } -/// ``` -#[doc(alias = "memmove")] -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] -#[inline] -pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { - extern "rust-intrinsic" { - #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] - fn copy(src: *const T, dst: *mut T, count: usize); - } - - // FIXME: Perform these checks only at run time - /*if cfg!(debug_assertions) && !(is_aligned_and_not_null(src) && is_aligned_and_not_null(dst)) { - // Not panicking to keep codegen impact smaller. - abort(); - }*/ - - // SAFETY: the safety contract for `copy` must be upheld by the caller. - unsafe { copy(src, dst, count) } -} - /// Sets `count * size_of::()` bytes of memory starting at `dst` to /// `val`. /// diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 8d901c08f91a3..fd92ed6fe47f0 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -881,17 +881,12 @@ pub const unsafe fn read_unaligned(src: *const T) -> T { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn write(dst: *mut T, src: T) { - // We are calling the intrinsics directly to avoid function calls in the generated code - // as `intrinsics::copy_nonoverlapping` is a wrapper function. - extern "rust-intrinsic" { - fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); - } - // SAFETY: the caller must guarantee that `dst` is valid for writes. // `dst` cannot overlap `src` because the caller has mutable access // to `dst` while `src` is owned by this function. unsafe { copy_nonoverlapping(&src as *const T, dst, 1); + // We are calling the intrinsic directly to avoid function calls in the generated code. intrinsics::forget(src); } } diff --git a/src/test/ui/const-ptr/out_of_bounds_read.stderr b/src/test/ui/const-ptr/out_of_bounds_read.stderr index ca65a079947e0..c208883c5a803 100644 --- a/src/test/ui/const-ptr/out_of_bounds_read.stderr +++ b/src/test/ui/const-ptr/out_of_bounds_read.stderr @@ -1,13 +1,12 @@ error: any use of this value will cause an error - --> $SRC_DIR/core/src/intrinsics.rs:LL:COL + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL | -LL | unsafe { copy_nonoverlapping(src, dst, count) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | memory access failed: pointer must be in-bounds at offset 8, but is outside bounds of alloc6 which has size 4 - | inside `copy_nonoverlapping::` at $SRC_DIR/core/src/intrinsics.rs:LL:COL - | inside `std::ptr::read::` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL - | inside `_READ` at $DIR/out_of_bounds_read.rs:13:33 +LL | copy_nonoverlapping(src, tmp.as_mut_ptr(), 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | memory access failed: pointer must be in-bounds at offset 8, but is outside bounds of alloc6 which has size 4 + | inside `std::ptr::read::` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + | inside `_READ` at $DIR/out_of_bounds_read.rs:13:33 | ::: $DIR/out_of_bounds_read.rs:13:5 | @@ -17,16 +16,15 @@ LL | const _READ: u32 = unsafe { ptr::read(PAST_END_PTR) }; = note: `#[deny(const_err)]` on by default error: any use of this value will cause an error - --> $SRC_DIR/core/src/intrinsics.rs:LL:COL + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL | -LL | unsafe { copy_nonoverlapping(src, dst, count) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | memory access failed: pointer must be in-bounds at offset 8, but is outside bounds of alloc6 which has size 4 - | inside `copy_nonoverlapping::` at $SRC_DIR/core/src/intrinsics.rs:LL:COL - | inside `std::ptr::read::` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL - | inside `ptr::const_ptr::::read` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL - | inside `_CONST_READ` at $DIR/out_of_bounds_read.rs:14:39 +LL | copy_nonoverlapping(src, tmp.as_mut_ptr(), 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | memory access failed: pointer must be in-bounds at offset 8, but is outside bounds of alloc6 which has size 4 + | inside `std::ptr::read::` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + | inside `ptr::const_ptr::::read` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL + | inside `_CONST_READ` at $DIR/out_of_bounds_read.rs:14:39 | ::: $DIR/out_of_bounds_read.rs:14:5 | @@ -34,16 +32,15 @@ LL | const _CONST_READ: u32 = unsafe { PAST_END_PTR.read() }; | -------------------------------------------------------- error: any use of this value will cause an error - --> $SRC_DIR/core/src/intrinsics.rs:LL:COL + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL | -LL | unsafe { copy_nonoverlapping(src, dst, count) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | memory access failed: pointer must be in-bounds at offset 8, but is outside bounds of alloc6 which has size 4 - | inside `copy_nonoverlapping::` at $SRC_DIR/core/src/intrinsics.rs:LL:COL - | inside `std::ptr::read::` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL - | inside `ptr::mut_ptr::::read` at $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL - | inside `_MUT_READ` at $DIR/out_of_bounds_read.rs:15:37 +LL | copy_nonoverlapping(src, tmp.as_mut_ptr(), 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | memory access failed: pointer must be in-bounds at offset 8, but is outside bounds of alloc6 which has size 4 + | inside `std::ptr::read::` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + | inside `ptr::mut_ptr::::read` at $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL + | inside `_MUT_READ` at $DIR/out_of_bounds_read.rs:15:37 | ::: $DIR/out_of_bounds_read.rs:15:5 |