Call low-level `ArrayVec` methods from high-level
What changed, and why it matters
This commit refactors internal helper methods in a fixed-size array type so that the safer, lower-level methods are reused instead of directly touching internal fields. It is a code-quality and safety-clarity improvement rather than a fix for a known exploitable bug. No public security advisory or researcher credit is present.
Treat as a routine defensive refactor. Review the new unsafe `set_len` justifications during normal code review, but no urgent security response is warranted absent additional evidence of a vulnerability.
Security signals we found
Refactoring of unsafe-adjacent code to centralize invariant enforcement
Addition of SOUNDNESS comments for unsafe `set_len` calls
Use of `spare_capacity_mut` and `last` to avoid direct field access
No removal of public API, bounds checks, or error paths
Evidence from the diff
The change modifies ArrayVec in internals/src/array_vec.rs so that try_push, pop, and extend_from_slice route through spare_capacity_mut, last, and set_len rather than directly mutating self.len and self.data. The commit adds explicit SOUNDNESS comments justifying the unsafe set_len calls. It does not change from_slice because that method is const. There is no diff evidence of a prior memory-safety bug being patched; the commit is framed as making safety easier to reason about.
Changed components
internals/src/array_vec.rsArrayVec::try_pushArrayVec::popArrayVec::extend_from_sliceInspect captured patch +21 / −18
diff --git a/internals/src/array_vec.rs b/internals/src/array_vec.rs
index 090638a0..4465c7b0 100644
--- a/internals/src/array_vec.rs
+++ b/internals/src/array_vec.rs
@@ -93,11 +93,14 @@ mod safety_boundary {
///
/// Returns `CapacityExceeded` if the `ArrayVec` is full.
pub fn try_push(&mut self, element: T) -> Result<(), Error> {
- if self.len >= CAP {
- return Err(Error::CapacityExceeded(CAP));
- }
- self.data[self.len] = MaybeUninit::new(element);
- self.len += 1;
+ let first = self.spare_capacity_mut().first_mut().ok_or(Error::CapacityExceeded(CAP))?;
+ *first = MaybeUninit::new(element);
+ let old_len = self.len();
+ // SOUNDNESS:
+ // * first being non-None implies the element exists therefore one-past the length <=
+ // CAP
+ // * all elements up to old_len were already filled and we just added one
+ unsafe { self.set_len(old_len + 1); }
Ok(())
}
@@ -107,15 +110,13 @@ mod safety_boundary {
///
/// None if the `ArrayVec` is empty.
pub fn pop(&mut self) -> Option<T> {
- if self.len > 0 {
- self.len -= 1;
- // SAFETY: All elements in 0..len are initialized
- let res = self.data[self.len];
- self.data[self.len] = MaybeUninit::uninit();
- Some(unsafe { res.assume_init() })
- } else {
- None
- }
+ let res = *self.last()?;
+ let old_len = self.len();
+ // SOUNDNESS:
+ // * decreasing the already-valid len keeps the len <= CAP invariant
+ // * decreasing the already-valid len does not mark any new elements as initialized
+ unsafe { self.set_len(old_len - 1) }
+ Some(res)
}
/// Copies and appends all elements from `slice` into `self`.
@@ -124,15 +125,17 @@ mod safety_boundary {
///
/// If the length would increase past CAP.
pub fn extend_from_slice(&mut self, slice: &[T]) {
- let new_len = self.len.checked_add(slice.len()).expect("integer/buffer overflow");
- assert!(new_len <= CAP, "buffer overflow");
// SAFETY: MaybeUninit<T> has the same layout as T
let slice = unsafe {
let ptr = slice.as_ptr();
core::slice::from_raw_parts(ptr.cast::<MaybeUninit<T>>(), slice.len())
};
- self.data[self.len..new_len].copy_from_slice(slice);
- self.len = new_len;
+ self.spare_capacity_mut()
+ .get_mut(..slice.len())
+ .expect("buffer overflow")
+ .copy_from_slice(slice);
+ let old_len = self.len();
+ unsafe { self.set_len(old_len + slice.len()) }
}
}
}
Why this scored 27/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.