Skip to content

Commit 96f385b

Browse files
committed
RawVecInner: add missing unsafe to unsafe fns
- RawVecInner::grow_exact causes UB if called with len and additional arguments such that len + additional is less than the current capacity. Indeed, in that case it calls Allocator::grow with a new_layout that is smaller than old_layout, which violates a safety precondition. - All RawVecInner methods for resizing the buffer cause UB if called with an elem_layout different from the one used to initially allocate the buffer, because in that case Allocator::grow/shrink is called with an old_layout that does not fit the allocated block, which violates a safety precondition. - RawVecInner::current_memory might cause UB if called with an elem_layout different from the one used to initially allocate the buffer, because the unchecked_mul might overflow. - Furthermore, these methods cause UB if called with an elem_layout where the size is not a multiple of the alignment. This is because Layout::repeat is used (in layout_array) to compute the allocation's layout when allocating, which includes padding to ensure alignment of array elements, but simple multiplication is used (in current_memory) to compute the old allocation's layout when resizing or deallocating, which would cause the layout used to resize or deallocate to not fit the allocated block, which violates a safety precondition.
1 parent 91edc3e commit 96f385b

File tree

2 files changed

+123
-36
lines changed

2 files changed

+123
-36
lines changed

library/alloc/src/raw_vec/mod.rs

Lines changed: 118 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ impl<T, A: Allocator> RawVec<T, A> {
177177
/// the returned `RawVec`.
178178
#[inline]
179179
pub(crate) const fn new_in(alloc: A) -> Self {
180+
// Check assumption made in `current_memory`
181+
const { assert!(T::LAYOUT.size() % T::LAYOUT.align() == 0) };
180182
Self { inner: RawVecInner::new_in(alloc, Alignment::of::<T>()), _marker: PhantomData }
181183
}
182184

@@ -328,7 +330,8 @@ impl<T, A: Allocator> RawVec<T, A> {
328330
#[inline]
329331
#[track_caller]
330332
pub(crate) fn reserve(&mut self, len: usize, additional: usize) {
331-
self.inner.reserve(len, additional, T::LAYOUT)
333+
// SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout
334+
unsafe { self.inner.reserve(len, additional, T::LAYOUT) }
332335
}
333336

334337
/// A specialized version of `self.reserve(len, 1)` which requires the
@@ -337,7 +340,8 @@ impl<T, A: Allocator> RawVec<T, A> {
337340
#[inline(never)]
338341
#[track_caller]
339342
pub(crate) fn grow_one(&mut self) {
340-
self.inner.grow_one(T::LAYOUT)
343+
// SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout
344+
unsafe { self.inner.grow_one(T::LAYOUT) }
341345
}
342346

343347
/// The same as `reserve`, but returns on errors instead of panicking or aborting.
@@ -346,7 +350,8 @@ impl<T, A: Allocator> RawVec<T, A> {
346350
len: usize,
347351
additional: usize,
348352
) -> Result<(), TryReserveError> {
349-
self.inner.try_reserve(len, additional, T::LAYOUT)
353+
// SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout
354+
unsafe { self.inner.try_reserve(len, additional, T::LAYOUT) }
350355
}
351356

352357
/// Ensures that the buffer contains at least enough space to hold `len +
@@ -369,7 +374,8 @@ impl<T, A: Allocator> RawVec<T, A> {
369374
#[cfg(not(no_global_oom_handling))]
370375
#[track_caller]
371376
pub(crate) fn reserve_exact(&mut self, len: usize, additional: usize) {
372-
self.inner.reserve_exact(len, additional, T::LAYOUT)
377+
// SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout
378+
unsafe { self.inner.reserve_exact(len, additional, T::LAYOUT) }
373379
}
374380

375381
/// The same as `reserve_exact`, but returns on errors instead of panicking or aborting.
@@ -378,7 +384,8 @@ impl<T, A: Allocator> RawVec<T, A> {
378384
len: usize,
379385
additional: usize,
380386
) -> Result<(), TryReserveError> {
381-
self.inner.try_reserve_exact(len, additional, T::LAYOUT)
387+
// SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout
388+
unsafe { self.inner.try_reserve_exact(len, additional, T::LAYOUT) }
382389
}
383390

384391
/// Shrinks the buffer down to the specified capacity. If the given amount
@@ -395,7 +402,8 @@ impl<T, A: Allocator> RawVec<T, A> {
395402
#[track_caller]
396403
#[inline]
397404
pub(crate) fn shrink_to_fit(&mut self, cap: usize) {
398-
self.inner.shrink_to_fit(cap, T::LAYOUT)
405+
// SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout
406+
unsafe { self.inner.shrink_to_fit(cap, T::LAYOUT) }
399407
}
400408
}
401409

@@ -518,8 +526,12 @@ impl<A: Allocator> RawVecInner<A> {
518526
&self.alloc
519527
}
520528

529+
/// # Safety
530+
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
531+
/// initially construct `self`
532+
/// - `elem_layout`'s size must be a multiple of its alignment
521533
#[inline]
522-
fn current_memory(&self, elem_layout: Layout) -> Option<(NonNull<u8>, Layout)> {
534+
unsafe fn current_memory(&self, elem_layout: Layout) -> Option<(NonNull<u8>, Layout)> {
523535
if elem_layout.size() == 0 || self.cap.as_inner() == 0 {
524536
None
525537
} else {
@@ -535,48 +547,67 @@ impl<A: Allocator> RawVecInner<A> {
535547
}
536548
}
537549

550+
/// # Safety
551+
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
552+
/// initially construct `self`
553+
/// - `elem_layout`'s size must be a multiple of its alignment
538554
#[cfg(not(no_global_oom_handling))]
539555
#[inline]
540556
#[track_caller]
541-
fn reserve(&mut self, len: usize, additional: usize, elem_layout: Layout) {
557+
unsafe fn reserve(&mut self, len: usize, additional: usize, elem_layout: Layout) {
542558
// Callers expect this function to be very cheap when there is already sufficient capacity.
543559
// Therefore, we move all the resizing and error-handling logic from grow_amortized and
544560
// handle_reserve behind a call, while making sure that this function is likely to be
545561
// inlined as just a comparison and a call if the comparison fails.
546562
#[cold]
547-
fn do_reserve_and_handle<A: Allocator>(
563+
unsafe fn do_reserve_and_handle<A: Allocator>(
548564
slf: &mut RawVecInner<A>,
549565
len: usize,
550566
additional: usize,
551567
elem_layout: Layout,
552568
) {
553-
if let Err(err) = slf.grow_amortized(len, additional, elem_layout) {
569+
// SAFETY: Precondition passed to caller
570+
if let Err(err) = unsafe { slf.grow_amortized(len, additional, elem_layout) } {
554571
handle_error(err);
555572
}
556573
}
557574

558575
if self.needs_to_grow(len, additional, elem_layout) {
559-
do_reserve_and_handle(self, len, additional, elem_layout);
576+
unsafe {
577+
do_reserve_and_handle(self, len, additional, elem_layout);
578+
}
560579
}
561580
}
562581

582+
/// # Safety
583+
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
584+
/// initially construct `self`
585+
/// - `elem_layout`'s size must be a multiple of its alignment
563586
#[cfg(not(no_global_oom_handling))]
564587
#[inline]
565588
#[track_caller]
566-
fn grow_one(&mut self, elem_layout: Layout) {
567-
if let Err(err) = self.grow_amortized(self.cap.as_inner(), 1, elem_layout) {
589+
unsafe fn grow_one(&mut self, elem_layout: Layout) {
590+
// SAFETY: Precondition passed to caller
591+
if let Err(err) = unsafe { self.grow_amortized(self.cap.as_inner(), 1, elem_layout) } {
568592
handle_error(err);
569593
}
570594
}
571595

572-
fn try_reserve(
596+
/// # Safety
597+
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
598+
/// initially construct `self`
599+
/// - `elem_layout`'s size must be a multiple of its alignment
600+
unsafe fn try_reserve(
573601
&mut self,
574602
len: usize,
575603
additional: usize,
576604
elem_layout: Layout,
577605
) -> Result<(), TryReserveError> {
578606
if self.needs_to_grow(len, additional, elem_layout) {
579-
self.grow_amortized(len, additional, elem_layout)?;
607+
// SAFETY: Precondition passed to caller
608+
unsafe {
609+
self.grow_amortized(len, additional, elem_layout)?;
610+
}
580611
}
581612
unsafe {
582613
// Inform the optimizer that the reservation has succeeded or wasn't needed
@@ -585,22 +616,34 @@ impl<A: Allocator> RawVecInner<A> {
585616
Ok(())
586617
}
587618

619+
/// # Safety
620+
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
621+
/// initially construct `self`
622+
/// - `elem_layout`'s size must be a multiple of its alignment
588623
#[cfg(not(no_global_oom_handling))]
589624
#[track_caller]
590-
fn reserve_exact(&mut self, len: usize, additional: usize, elem_layout: Layout) {
591-
if let Err(err) = self.try_reserve_exact(len, additional, elem_layout) {
625+
unsafe fn reserve_exact(&mut self, len: usize, additional: usize, elem_layout: Layout) {
626+
// SAFETY: Precondition passed to caller
627+
if let Err(err) = unsafe { self.try_reserve_exact(len, additional, elem_layout) } {
592628
handle_error(err);
593629
}
594630
}
595631

596-
fn try_reserve_exact(
632+
/// # Safety
633+
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
634+
/// initially construct `self`
635+
/// - `elem_layout`'s size must be a multiple of its alignment
636+
unsafe fn try_reserve_exact(
597637
&mut self,
598638
len: usize,
599639
additional: usize,
600640
elem_layout: Layout,
601641
) -> Result<(), TryReserveError> {
602642
if self.needs_to_grow(len, additional, elem_layout) {
603-
self.grow_exact(len, additional, elem_layout)?;
643+
// SAFETY: Precondition passed to caller
644+
unsafe {
645+
self.grow_exact(len, additional, elem_layout)?;
646+
}
604647
}
605648
unsafe {
606649
// Inform the optimizer that the reservation has succeeded or wasn't needed
@@ -609,11 +652,16 @@ impl<A: Allocator> RawVecInner<A> {
609652
Ok(())
610653
}
611654

655+
/// # Safety
656+
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
657+
/// initially construct `self`
658+
/// - `elem_layout`'s size must be a multiple of its alignment
659+
/// - `cap` must be less than or equal to `self.capacity(elem_layout.size())`
612660
#[cfg(not(no_global_oom_handling))]
613661
#[inline]
614662
#[track_caller]
615-
fn shrink_to_fit(&mut self, cap: usize, elem_layout: Layout) {
616-
if let Err(err) = self.shrink(cap, elem_layout) {
663+
unsafe fn shrink_to_fit(&mut self, cap: usize, elem_layout: Layout) {
664+
if let Err(err) = unsafe { self.shrink(cap, elem_layout) } {
617665
handle_error(err);
618666
}
619667
}
@@ -632,7 +680,13 @@ impl<A: Allocator> RawVecInner<A> {
632680
self.cap = unsafe { Cap::new_unchecked(cap) };
633681
}
634682

635-
fn grow_amortized(
683+
/// # Safety
684+
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
685+
/// initially construct `self`
686+
/// - `elem_layout`'s size must be a multiple of its alignment
687+
/// - The sum of `len` and `additional` must be greater than or equal to
688+
/// `self.capacity(elem_layout.size())`
689+
unsafe fn grow_amortized(
636690
&mut self,
637691
len: usize,
638692
additional: usize,
@@ -657,14 +711,25 @@ impl<A: Allocator> RawVecInner<A> {
657711

658712
let new_layout = layout_array(cap, elem_layout)?;
659713

660-
let ptr = finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)?;
661-
// SAFETY: layout_array would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items
714+
// SAFETY:
715+
// - For the `current_memory` call: Precondition passed to caller
716+
// - For the `finish_grow` call: Precondition passed to caller
717+
// + `current_memory` does the right thing
718+
let ptr =
719+
unsafe { finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)? };
662720

721+
// SAFETY: layout_array would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items
663722
unsafe { self.set_ptr_and_cap(ptr, cap) };
664723
Ok(())
665724
}
666725

667-
fn grow_exact(
726+
/// # Safety
727+
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
728+
/// initially construct `self`
729+
/// - `elem_layout`'s size must be a multiple of its alignment
730+
/// - The sum of `len` and `additional` must be greater than or equal to
731+
/// `self.capacity(elem_layout.size())`
732+
unsafe fn grow_exact(
668733
&mut self,
669734
len: usize,
670735
additional: usize,
@@ -679,17 +744,27 @@ impl<A: Allocator> RawVecInner<A> {
679744
let cap = len.checked_add(additional).ok_or(CapacityOverflow)?;
680745
let new_layout = layout_array(cap, elem_layout)?;
681746

682-
let ptr = finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)?;
747+
// SAFETY:
748+
// - For the `current_memory` call: Precondition passed to caller
749+
// - For the `finish_grow` call: Precondition passed to caller
750+
// + `current_memory` does the right thing
751+
let ptr =
752+
unsafe { finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)? };
683753
// SAFETY: layout_array would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items
684754
unsafe {
685755
self.set_ptr_and_cap(ptr, cap);
686756
}
687757
Ok(())
688758
}
689759

760+
/// # Safety
761+
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
762+
/// initially construct `self`
763+
/// - `elem_layout`'s size must be a multiple of its alignment
764+
/// - `cap` must be less than or equal to `self.capacity(elem_layout.size())`
690765
#[cfg(not(no_global_oom_handling))]
691766
#[inline]
692-
fn shrink(&mut self, cap: usize, elem_layout: Layout) -> Result<(), TryReserveError> {
767+
unsafe fn shrink(&mut self, cap: usize, elem_layout: Layout) -> Result<(), TryReserveError> {
693768
assert!(cap <= self.capacity(elem_layout.size()), "Tried to shrink to a larger capacity");
694769
// SAFETY: Just checked this isn't trying to grow
695770
unsafe { self.shrink_unchecked(cap, elem_layout) }
@@ -711,8 +786,12 @@ impl<A: Allocator> RawVecInner<A> {
711786
cap: usize,
712787
elem_layout: Layout,
713788
) -> Result<(), TryReserveError> {
714-
let (ptr, layout) =
715-
if let Some(mem) = self.current_memory(elem_layout) { mem } else { return Ok(()) };
789+
// SAFETY: Precondition passed to caller
790+
let (ptr, layout) = if let Some(mem) = unsafe { self.current_memory(elem_layout) } {
791+
mem
792+
} else {
793+
return Ok(());
794+
};
716795

717796
// If shrinking to 0, deallocate the buffer. We don't reach this point
718797
// for the T::IS_ZST case since current_memory() will have returned
@@ -748,18 +827,26 @@ impl<A: Allocator> RawVecInner<A> {
748827
/// Ideally this function would take `self` by move, but it cannot because it exists to be
749828
/// called from a `Drop` impl.
750829
unsafe fn deallocate(&mut self, elem_layout: Layout) {
751-
if let Some((ptr, layout)) = self.current_memory(elem_layout) {
830+
// SAFETY: Precondition passed to caller
831+
if let Some((ptr, layout)) = unsafe { self.current_memory(elem_layout) } {
752832
unsafe {
753833
self.alloc.deallocate(ptr, layout);
754834
}
755835
}
756836
}
757837
}
758838

839+
/// # Safety
840+
/// If `current_memory` matches `Some((ptr, old_layout))`:
841+
/// - `ptr` must denote a block of memory *currently allocated* via `alloc`
842+
/// - `old_layout` must *fit* that block of memory
843+
/// - `new_layout` must have the same alignment as `old_layout`
844+
/// - `new_layout.size()` must be greater than or equal to `old_layout.size()`
845+
/// If `current_memory` is `None`, this function is safe.
759846
// not marked inline(never) since we want optimizers to be able to observe the specifics of this
760847
// function, see tests/codegen-llvm/vec-reserve-extend.rs.
761848
#[cold]
762-
fn finish_grow<A>(
849+
unsafe fn finish_grow<A>(
763850
new_layout: Layout,
764851
current_memory: Option<(NonNull<u8>, Layout)>,
765852
alloc: &mut A,

library/alloc/src/raw_vec/tests.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ struct ZST;
8585
fn zst_sanity<T>(v: &RawVec<T>) {
8686
assert_eq!(v.capacity(), usize::MAX);
8787
assert_eq!(v.ptr(), core::ptr::Unique::<T>::dangling().as_ptr());
88-
assert_eq!(v.inner.current_memory(T::LAYOUT), None);
88+
assert_eq!(unsafe { v.inner.current_memory(T::LAYOUT) }, None);
8989
}
9090

9191
#[test]
@@ -126,12 +126,12 @@ fn zst() {
126126
assert_eq!(v.try_reserve_exact(101, usize::MAX - 100), cap_err);
127127
zst_sanity(&v);
128128

129-
assert_eq!(v.inner.grow_amortized(100, usize::MAX - 100, ZST::LAYOUT), cap_err);
130-
assert_eq!(v.inner.grow_amortized(101, usize::MAX - 100, ZST::LAYOUT), cap_err);
129+
assert_eq!(unsafe { v.inner.grow_amortized(100, usize::MAX - 100, ZST::LAYOUT) }, cap_err);
130+
assert_eq!(unsafe { v.inner.grow_amortized(101, usize::MAX - 100, ZST::LAYOUT) }, cap_err);
131131
zst_sanity(&v);
132132

133-
assert_eq!(v.inner.grow_exact(100, usize::MAX - 100, ZST::LAYOUT), cap_err);
134-
assert_eq!(v.inner.grow_exact(101, usize::MAX - 100, ZST::LAYOUT), cap_err);
133+
assert_eq!(unsafe { v.inner.grow_exact(100, usize::MAX - 100, ZST::LAYOUT) }, cap_err);
134+
assert_eq!(unsafe { v.inner.grow_exact(101, usize::MAX - 100, ZST::LAYOUT) }, cap_err);
135135
zst_sanity(&v);
136136
}
137137

0 commit comments

Comments
 (0)