diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index fa5eb1823f1a0..e325bb4b3efa8 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -3,7 +3,7 @@ #![stable(feature = "alloc_module", since = "1.28.0")] use core::intrinsics::{self, min_align_of_val, size_of_val}; -use core::ptr::{NonNull, Unique}; +use core::ptr::{self, NonNull, Unique}; #[stable(feature = "alloc_module", since = "1.28.0")] #[doc(inline)] @@ -180,36 +180,45 @@ impl Global { unsafe fn grow_impl( &mut self, ptr: NonNull, - layout: Layout, - new_size: usize, + old_layout: Layout, + new_layout: Layout, zeroed: bool, ) -> Result, AllocErr> { debug_assert!( - new_size >= layout.size(), - "`new_size` must be greater than or equal to `layout.size()`" + new_layout.size() >= old_layout.size(), + "`new_layout.size()` must be greater than or equal to `old_layout.size()`" ); - match layout.size() { - // SAFETY: the caller must ensure that the `new_size` does not overflow. - // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout. - 0 => unsafe { - let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); - self.alloc_impl(new_layout, zeroed) - }, + match old_layout.size() { + 0 => self.alloc_impl(new_layout, zeroed), // SAFETY: `new_size` is non-zero as `old_size` is greater than or equal to `new_size` // as required by safety conditions. Other conditions must be upheld by the caller - old_size => unsafe { - // `realloc` probably checks for `new_size >= size` or something similar. - intrinsics::assume(new_size >= layout.size()); + old_size if old_layout.align() == new_layout.align() => unsafe { + let new_size = new_layout.size(); + + // `realloc` probably checks for `new_size >= old_layout.size()` or something similar. + intrinsics::assume(new_size >= old_layout.size()); - let raw_ptr = realloc(ptr.as_ptr(), layout, new_size); + let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size); let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; if zeroed { raw_ptr.add(old_size).write_bytes(0, new_size - old_size); } Ok(NonNull::slice_from_raw_parts(ptr, new_size)) }, + + // SAFETY: because `new_layout.size()` must be greater than or equal to `old_size`, + // both the old and new memory allocation are valid for reads and writes for `old_size` + // bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap + // `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract + // for `dealloc` must be upheld by the caller. + old_size => unsafe { + let new_ptr = self.alloc_impl(new_layout, zeroed)?; + ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_size); + self.dealloc(ptr, old_layout); + Ok(new_ptr) + }, } } } @@ -239,52 +248,64 @@ unsafe impl AllocRef for Global { unsafe fn grow( &mut self, ptr: NonNull, - layout: Layout, - new_size: usize, + old_layout: Layout, + new_layout: Layout, ) -> Result, AllocErr> { // SAFETY: all conditions must be upheld by the caller - unsafe { self.grow_impl(ptr, layout, new_size, false) } + unsafe { self.grow_impl(ptr, old_layout, new_layout, false) } } #[inline] unsafe fn grow_zeroed( &mut self, ptr: NonNull, - layout: Layout, - new_size: usize, + old_layout: Layout, + new_layout: Layout, ) -> Result, AllocErr> { // SAFETY: all conditions must be upheld by the caller - unsafe { self.grow_impl(ptr, layout, new_size, true) } + unsafe { self.grow_impl(ptr, old_layout, new_layout, true) } } #[inline] unsafe fn shrink( &mut self, ptr: NonNull, - layout: Layout, - new_size: usize, + old_layout: Layout, + new_layout: Layout, ) -> Result, AllocErr> { debug_assert!( - new_size <= layout.size(), - "`new_size` must be smaller than or equal to `layout.size()`" + new_layout.size() <= old_layout.size(), + "`new_layout.size()` must be smaller than or equal to `old_layout.size()`" ); - match new_size { + match new_layout.size() { // SAFETY: conditions must be upheld by the caller 0 => unsafe { - self.dealloc(ptr, layout); - Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)) + self.dealloc(ptr, old_layout); + Ok(NonNull::slice_from_raw_parts(new_layout.dangling(), 0)) }, // SAFETY: `new_size` is non-zero. Other conditions must be upheld by the caller - new_size => unsafe { - // `realloc` probably checks for `new_size <= size` or something similar. - intrinsics::assume(new_size <= layout.size()); + new_size if old_layout.align() == new_layout.align() => unsafe { + // `realloc` probably checks for `new_size <= old_layout.size()` or something similar. + intrinsics::assume(new_size <= old_layout.size()); - let raw_ptr = realloc(ptr.as_ptr(), layout, new_size); + let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size); let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; Ok(NonNull::slice_from_raw_parts(ptr, new_size)) }, + + // SAFETY: because `new_size` must be smaller than or equal to `old_layout.size()`, + // both the old and new memory allocation are valid for reads and writes for `new_size` + // bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap + // `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract + // for `dealloc` must be upheld by the caller. + new_size => unsafe { + let new_ptr = self.alloc(new_layout)?; + ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_size); + self.dealloc(ptr, old_layout); + Ok(new_ptr) + }, } } } @@ -297,7 +318,7 @@ unsafe impl AllocRef for Global { unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { let layout = unsafe { Layout::from_size_align_unchecked(size, align) }; match Global.alloc(layout) { - Ok(ptr) => ptr.as_non_null_ptr().as_ptr(), + Ok(ptr) => ptr.as_mut_ptr(), Err(_) => handle_alloc_error(layout), } } diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 247b636c808ac..05382d0b5594e 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -3,6 +3,7 @@ use core::alloc::LayoutErr; use core::cmp; +use core::intrinsics; use core::mem::{self, ManuallyDrop, MaybeUninit}; use core::ops::Drop; use core::ptr::{NonNull, Unique}; @@ -465,8 +466,9 @@ impl RawVec { let new_size = amount * mem::size_of::(); let ptr = unsafe { - self.alloc.shrink(ptr, layout, new_size).map_err(|_| TryReserveError::AllocError { - layout: Layout::from_size_align_unchecked(new_size, layout.align()), + let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); + self.alloc.shrink(ptr, layout, new_layout).map_err(|_| TryReserveError::AllocError { + layout: new_layout, non_exhaustive: (), })? }; @@ -494,13 +496,16 @@ where let memory = if let Some((ptr, old_layout)) = current_memory { debug_assert_eq!(old_layout.align(), new_layout.align()); - unsafe { alloc.grow(ptr, old_layout, new_layout.size()) } + unsafe { + // The allocator checks for alignment equality + intrinsics::assume(old_layout.align() == new_layout.align()); + alloc.grow(ptr, old_layout, new_layout) + } } else { alloc.alloc(new_layout) - } - .map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })?; + }; - Ok(memory) + memory.map_err(|_| AllocError { layout: new_layout, non_exhaustive: () }) } unsafe impl<#[may_dangle] T, A: AllocRef> Drop for RawVec { diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs index ad4f8bf1397c2..c1fda2fce641f 100644 --- a/library/core/src/alloc/mod.rs +++ b/library/core/src/alloc/mod.rs @@ -147,9 +147,8 @@ pub unsafe trait AllocRef { /// Attempts to extend the memory block. /// /// Returns a new [`NonNull<[u8]>`] containing a pointer and the actual size of the allocated - /// memory. The pointer is suitable for holding data described by a new layout with `layout`’s - /// alignment and a size given by `new_size`. To accomplish this, the allocator may extend the - /// allocation referenced by `ptr` to fit the new layout. + /// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish + /// this, the allocator may extend the allocation referenced by `ptr` to fit the new layout. /// /// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been /// transferred to this allocator. The memory may or may not have been freed, and should be @@ -163,11 +162,9 @@ pub unsafe trait AllocRef { /// /// # Safety /// - /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator, - /// * `layout` must [*fit*] that block of memory (The `new_size` argument need not fit it.), - /// * `new_size` must be greater than or equal to `layout.size()`, and - /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, must not overflow - /// (i.e., the rounded value must be less than or equal to `usize::MAX`). + /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator. + /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.). + /// * `new_layout.size()` must be greater than or equal to `old_layout.size()`. /// /// [*currently allocated*]: #currently-allocated-memory /// [*fit*]: #memory-fitting @@ -188,28 +185,24 @@ pub unsafe trait AllocRef { unsafe fn grow( &mut self, ptr: NonNull, - layout: Layout, - new_size: usize, + old_layout: Layout, + new_layout: Layout, ) -> Result, AllocErr> { - let size = layout.size(); debug_assert!( - new_size >= size, - "`new_size` must be greater than or equal to `layout.size()`" + new_layout.size() >= old_layout.size(), + "`new_layout.size()` must be greater than or equal to `old_layout.size()`" ); - // SAFETY: the caller must ensure that the `new_size` does not overflow. - // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout. - let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) }; let new_ptr = self.alloc(new_layout)?; - // SAFETY: because `new_size` must be greater than or equal to `size`, both the old and new - // memory allocation are valid for reads and writes for `size` bytes. Also, because the old - // allocation wasn't yet deallocated, it cannot overlap `new_ptr`. Thus, the call to - // `copy_nonoverlapping` is safe. - // The safety contract for `dealloc` must be upheld by the caller. + // SAFETY: because `new_layout.size()` must be greater than or equal to + // `old_layout.size()`, both the old and new memory allocation are valid for reads and + // writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet + // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is + // safe. The safety contract for `dealloc` must be upheld by the caller. unsafe { - ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), size); - self.dealloc(ptr, layout); + ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_layout.size()); + self.dealloc(ptr, old_layout); } Ok(new_ptr) @@ -220,21 +213,19 @@ pub unsafe trait AllocRef { /// /// The memory block will contain the following contents after a successful call to /// `grow_zeroed`: - /// * Bytes `0..layout.size()` are preserved from the original allocation. - /// * Bytes `layout.size()..old_size` will either be preserved or zeroed, depending on the - /// allocator implementation. `old_size` refers to the size of the memory block prior to - /// the `grow_zeroed` call, which may be larger than the size that was originally requested - /// when it was allocated. + /// * Bytes `0..old_layout.size()` are preserved from the original allocation. + /// * Bytes `old_layout.size()..old_size` will either be preserved or zeroed, depending on + /// the allocator implementation. `old_size` refers to the size of the memory block prior + /// to the `grow_zeroed` call, which may be larger than the size that was originally + /// requested when it was allocated. /// * Bytes `old_size..new_size` are zeroed. `new_size` refers to the size of the memory - /// block returned by the `grow` call. + /// block returned by the `grow_zeroed` call. /// /// # Safety /// - /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator, - /// * `layout` must [*fit*] that block of memory (The `new_size` argument need not fit it.), - /// * `new_size` must be greater than or equal to `layout.size()`, and - /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, must not overflow - /// (i.e., the rounded value must be less than or equal to `usize::MAX`). + /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator. + /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.). + /// * `new_layout.size()` must be greater than or equal to `old_layout.size()`. /// /// [*currently allocated*]: #currently-allocated-memory /// [*fit*]: #memory-fitting @@ -255,28 +246,24 @@ pub unsafe trait AllocRef { unsafe fn grow_zeroed( &mut self, ptr: NonNull, - layout: Layout, - new_size: usize, + old_layout: Layout, + new_layout: Layout, ) -> Result, AllocErr> { - let size = layout.size(); debug_assert!( - new_size >= size, - "`new_size` must be greater than or equal to `layout.size()`" + new_layout.size() >= old_layout.size(), + "`new_layout.size()` must be greater than or equal to `old_layout.size()`" ); - // SAFETY: the caller must ensure that the `new_size` does not overflow. - // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout. - let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) }; let new_ptr = self.alloc_zeroed(new_layout)?; - // SAFETY: because `new_size` must be greater than or equal to `size`, both the old and new - // memory allocation are valid for reads and writes for `size` bytes. Also, because the old - // allocation wasn't yet deallocated, it cannot overlap `new_ptr`. Thus, the call to - // `copy_nonoverlapping` is safe. - // The safety contract for `dealloc` must be upheld by the caller. + // SAFETY: because `new_layout.size()` must be greater than or equal to + // `old_layout.size()`, both the old and new memory allocation are valid for reads and + // writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet + // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is + // safe. The safety contract for `dealloc` must be upheld by the caller. unsafe { - ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), size); - self.dealloc(ptr, layout); + ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_layout.size()); + self.dealloc(ptr, old_layout); } Ok(new_ptr) @@ -285,9 +272,8 @@ pub unsafe trait AllocRef { /// Attempts to shrink the memory block. /// /// Returns a new [`NonNull<[u8]>`] containing a pointer and the actual size of the allocated - /// memory. The pointer is suitable for holding data described by a new layout with `layout`’s - /// alignment and a size given by `new_size`. To accomplish this, the allocator may shrink the - /// allocation referenced by `ptr` to fit the new layout. + /// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish + /// this, the allocator may shrink the allocation referenced by `ptr` to fit the new layout. /// /// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been /// transferred to this allocator. The memory may or may not have been freed, and should be @@ -301,9 +287,9 @@ pub unsafe trait AllocRef { /// /// # Safety /// - /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator, - /// * `layout` must [*fit*] that block of memory (The `new_size` argument need not fit it.), and - /// * `new_size` must be smaller than or equal to `layout.size()`. + /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator. + /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.). + /// * `new_layout.size()` must be smaller than or equal to `old_layout.size()`. /// /// [*currently allocated*]: #currently-allocated-memory /// [*fit*]: #memory-fitting @@ -324,28 +310,24 @@ pub unsafe trait AllocRef { unsafe fn shrink( &mut self, ptr: NonNull, - layout: Layout, - new_size: usize, + old_layout: Layout, + new_layout: Layout, ) -> Result, AllocErr> { - let size = layout.size(); debug_assert!( - new_size <= size, - "`new_size` must be smaller than or equal to `layout.size()`" + new_layout.size() <= old_layout.size(), + "`new_layout.size()` must be smaller than or equal to `old_layout.size()`" ); - // SAFETY: the caller must ensure that the `new_size` does not overflow. - // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout. - let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) }; let new_ptr = self.alloc(new_layout)?; - // SAFETY: because `new_size` must be lower than or equal to `size`, both the old and new - // memory allocation are valid for reads and writes for `new_size` bytes. Also, because the - // old allocation wasn't yet deallocated, it cannot overlap `new_ptr`. Thus, the call to - // `copy_nonoverlapping` is safe. - // The safety contract for `dealloc` must be upheld by the caller. + // SAFETY: because `new_layout.size()` must be lower than or equal to + // `old_layout.size()`, both the old and new memory allocation are valid for reads and + // writes for `new_layout.size()` bytes. Also, because the old allocation wasn't yet + // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is + // safe. The safety contract for `dealloc` must be upheld by the caller. unsafe { - ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), size); - self.dealloc(ptr, layout); + ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_layout.size()); + self.dealloc(ptr, old_layout); } Ok(new_ptr) @@ -385,32 +367,32 @@ where unsafe fn grow( &mut self, ptr: NonNull, - layout: Layout, - new_size: usize, + old_layout: Layout, + new_layout: Layout, ) -> Result, AllocErr> { // SAFETY: the safety contract must be upheld by the caller - unsafe { (**self).grow(ptr, layout, new_size) } + unsafe { (**self).grow(ptr, old_layout, new_layout) } } #[inline] unsafe fn grow_zeroed( &mut self, ptr: NonNull, - layout: Layout, - new_size: usize, + old_layout: Layout, + new_layout: Layout, ) -> Result, AllocErr> { // SAFETY: the safety contract must be upheld by the caller - unsafe { (**self).grow_zeroed(ptr, layout, new_size) } + unsafe { (**self).grow_zeroed(ptr, old_layout, new_layout) } } #[inline] unsafe fn shrink( &mut self, ptr: NonNull, - layout: Layout, - new_size: usize, + old_layout: Layout, + new_layout: Layout, ) -> Result, AllocErr> { // SAFETY: the safety contract must be upheld by the caller - unsafe { (**self).shrink(ptr, layout, new_size) } + unsafe { (**self).shrink(ptr, old_layout, new_layout) } } } diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index b4009c86419d5..64d8edf33bd3b 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -154,36 +154,45 @@ impl System { unsafe fn grow_impl( &mut self, ptr: NonNull, - layout: Layout, - new_size: usize, + old_layout: Layout, + new_layout: Layout, zeroed: bool, ) -> Result, AllocErr> { debug_assert!( - new_size >= layout.size(), - "`new_size` must be greater than or equal to `layout.size()`" + new_layout.size() >= old_layout.size(), + "`new_layout.size()` must be greater than or equal to `old_layout.size()`" ); - match layout.size() { - // SAFETY: the caller must ensure that the `new_size` does not overflow. - // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout. - 0 => unsafe { - let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); - self.alloc_impl(new_layout, zeroed) - }, + match old_layout.size() { + 0 => self.alloc_impl(new_layout, zeroed), // SAFETY: `new_size` is non-zero as `old_size` is greater than or equal to `new_size` // as required by safety conditions. Other conditions must be upheld by the caller - old_size => unsafe { - // `realloc` probably checks for `new_size >= size` or something similar. - intrinsics::assume(new_size >= layout.size()); + old_size if old_layout.align() == new_layout.align() => unsafe { + let new_size = new_layout.size(); + + // `realloc` probably checks for `new_size >= old_layout.size()` or something similar. + intrinsics::assume(new_size >= old_layout.size()); - let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size); + let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size); let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; if zeroed { raw_ptr.add(old_size).write_bytes(0, new_size - old_size); } Ok(NonNull::slice_from_raw_parts(ptr, new_size)) }, + + // SAFETY: because `new_layout.size()` must be greater than or equal to `old_size`, + // both the old and new memory allocation are valid for reads and writes for `old_size` + // bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap + // `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract + // for `dealloc` must be upheld by the caller. + old_size => unsafe { + let new_ptr = self.alloc_impl(new_layout, zeroed)?; + ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_size); + self.dealloc(ptr, old_layout); + Ok(new_ptr) + }, } } } @@ -215,52 +224,64 @@ unsafe impl AllocRef for System { unsafe fn grow( &mut self, ptr: NonNull, - layout: Layout, - new_size: usize, + old_layout: Layout, + new_layout: Layout, ) -> Result, AllocErr> { // SAFETY: all conditions must be upheld by the caller - unsafe { self.grow_impl(ptr, layout, new_size, false) } + unsafe { self.grow_impl(ptr, old_layout, new_layout, false) } } #[inline] unsafe fn grow_zeroed( &mut self, ptr: NonNull, - layout: Layout, - new_size: usize, + old_layout: Layout, + new_layout: Layout, ) -> Result, AllocErr> { // SAFETY: all conditions must be upheld by the caller - unsafe { self.grow_impl(ptr, layout, new_size, true) } + unsafe { self.grow_impl(ptr, old_layout, new_layout, true) } } #[inline] unsafe fn shrink( &mut self, ptr: NonNull, - layout: Layout, - new_size: usize, + old_layout: Layout, + new_layout: Layout, ) -> Result, AllocErr> { debug_assert!( - new_size <= layout.size(), - "`new_size` must be smaller than or equal to `layout.size()`" + new_layout.size() <= old_layout.size(), + "`new_layout.size()` must be smaller than or equal to `old_layout.size()`" ); - match new_size { + match new_layout.size() { // SAFETY: conditions must be upheld by the caller 0 => unsafe { - self.dealloc(ptr, layout); - Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)) + self.dealloc(ptr, old_layout); + Ok(NonNull::slice_from_raw_parts(new_layout.dangling(), 0)) }, // SAFETY: `new_size` is non-zero. Other conditions must be upheld by the caller - new_size => unsafe { - // `realloc` probably checks for `new_size <= size` or something similar. - intrinsics::assume(new_size <= layout.size()); + new_size if old_layout.align() == new_layout.align() => unsafe { + // `realloc` probably checks for `new_size <= old_layout.size()` or something similar. + intrinsics::assume(new_size <= old_layout.size()); - let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size); + let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size); let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; Ok(NonNull::slice_from_raw_parts(ptr, new_size)) }, + + // SAFETY: because `new_size` must be smaller than or equal to `old_layout.size()`, + // both the old and new memory allocation are valid for reads and writes for `new_size` + // bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap + // `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract + // for `dealloc` must be upheld by the caller. + new_size => unsafe { + let new_ptr = self.alloc(new_layout)?; + ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_size); + self.dealloc(ptr, old_layout); + Ok(new_ptr) + }, } } } diff --git a/src/test/ui/realloc-16687.rs b/src/test/ui/realloc-16687.rs index bdcd47a7260d3..2e07fdcbe830c 100644 --- a/src/test/ui/realloc-16687.rs +++ b/src/test/ui/realloc-16687.rs @@ -48,7 +48,7 @@ unsafe fn test_triangle() -> bool { println!("allocate({:?}) = {:?}", layout, ptr); } - ptr.as_non_null_ptr().as_ptr() + ptr.as_mut_ptr() } unsafe fn deallocate(ptr: *mut u8, layout: Layout) { @@ -65,23 +65,17 @@ unsafe fn test_triangle() -> bool { } let memory = if new.size() > old.size() { - Global.grow( - NonNull::new_unchecked(ptr), - old, - new.size(), - ) + Global.grow(NonNull::new_unchecked(ptr), old, new) } else { - Global.shrink(NonNull::new_unchecked(ptr), old, new.size()) + Global.shrink(NonNull::new_unchecked(ptr), old, new) }; - let ptr = memory.unwrap_or_else(|_| { - handle_alloc_error(Layout::from_size_align_unchecked(new.size(), old.align())) - }); + let ptr = memory.unwrap_or_else(|_| handle_alloc_error(new)); if PRINT { println!("reallocate({:?}, old={:?}, new={:?}) = {:?}", ptr, old, new, ptr); } - ptr.as_non_null_ptr().as_ptr() + ptr.as_mut_ptr() } fn idx_to_size(i: usize) -> usize {