Skip to content

Commit

Permalink
Validate AlignedVec::resize safety requirements
Browse files Browse the repository at this point in the history
This is a necessary check for soundness, as demonstrated by the test
which can SIGSEGV without the check. Before the check, an overflow in
the underlying buffer calculation can create incoherent state where the
vector believes in an impossibly large buffer of the item type which is
not actually backed by a correctly sized buffer of chunks.
  • Loading branch information
HeroicKatora committed Sep 10, 2024
1 parent 7d72409 commit 3b72c9a
Showing 1 changed file with 28 additions and 0 deletions.
28 changes: 28 additions & 0 deletions src/align.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,15 @@ pub struct AlignedVec<T: Copy, C: AlignedByteChunk> {
}

impl<T: Copy, C: AlignedByteChunk> AlignedVec<T, C> {
// Note that in Rust, no single allocation can exceed `isize::MAX` __bytes_.
const MAX_LEN: usize = {
if core::mem::size_of::<T>() == 0 {
usize::MAX
} else {
(isize::MAX as usize) / core::mem::size_of::<T>()
}
};

/// Must check in all constructors.
const fn check_byte_chunk_type_is_aligned() {
assert!(mem::size_of::<C>() == mem::align_of::<C>());
Expand Down Expand Up @@ -183,6 +192,11 @@ impl<T: Copy, C: AlignedByteChunk> AlignedVec<T, C> {
}

pub fn resize(&mut self, new_len: usize, value: T) {
// In addition to the obvious effect, this verifies the wrapping behavior of the
// `new_bytes` calculation. That can not overflow as the length limit does not overflow
// when multiplied with the size of `T`. Note that we one can still pass ludicrous
// requested buffer lengths, just not unsound ones.
assert!(new_len <= Self::MAX_LEN, "Resizing would overflow the underlying aligned buffer");
let old_len = self.len();

// Resize the underlying vector to have enough chunks for the new length.
Expand Down Expand Up @@ -267,3 +281,17 @@ unsafe impl<T: Copy, C: AlignedByteChunk> AsMutPtr for AlignedVec<T, C> {
self.len()
}
}

#[test]
#[should_panic]
fn align_vec_fails() {
let mut v = AlignedVec::<u16, Align8<[u8; 8]>>::new();
// This resize must fail. Otherwise, the code below creates a very small actual allocation, and
// consequently a slice reference that points to memory outside the buffer.
v.resize(isize::MAX as usize + 2, 0u16);
// Note that in Rust, no single allocation can exceed `isize::MAX` __bytes_. _Meaning it is
// impossible to soundly create a slice of `u16` with `isize::MAX` elements. If we got to this
// point, everything is broken already. The indexing will the probably also wrap and appear to
// work.
assert_eq!(v.as_slice()[isize::MAX as usize], 0);
}

0 comments on commit 3b72c9a

Please sign in to comment.