VYPR
Critical severityNVD Advisory· Published Jan 22, 2021· Updated Aug 3, 2024

CVE-2021-25900

CVE-2021-25900

Description

An issue was discovered in the smallvec crate before 0.6.14 and 1.x before 1.6.1 for Rust. There is a heap-based buffer overflow in SmallVec::insert_many.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

CVE-2021-25900 is a critical heap buffer overflow in the Rust smallvec crate's `insert_many` method, allowing memory corruption without authentication.

The vulnerability resides in the SmallVec::insert_many method of the smallvec crate (versions before 0.6.14 and 1.x before 1.6.1). The method relies on the iterator's size_hint to reserve space and move existing elements, but if the iterator produces fewer elements than the hint, the code fails to adjust the length accordingly, leaving a gap that is filled with uninitialized memory. Conversely, if more elements are provided than hinted, the copy operation can write past the allocated buffer, causing a heap-based buffer overflow [1][3].

The attack surface is broad: the crate is widely used in Rust ecosystems, and the bug can be triggered by any user-controlled input that influences the iterator passed to insert_many. No special privileges are required — an attacker with network access could exploit this in a service parsing untrusted data [3]. The vulnerability is classified as critical (CVSS 9.8) with low attack complexity and no user interaction needed [3].

Successful exploitation allows an attacker to corrupt adjacent heap memory, potentially leading to arbitrary code execution, data leakage, or denial of service. Since smallvec is a fundamental data structure, the impact extends to any application depending on it, making this a systemic memory-safety risk [1][3].

The maintainers fixed the issue in versions 0.6.14 and 1.6.1 by refactoring insert_many to correctly track the number of added elements, pad with remaining space, and handle iterator depletions properly [2][4]. Users should update to patched versions immediately; no workaround exists. The vulnerability is also tracked under RUSTSEC-2021-0003 and GHSA-43w2-9j62-hq99 [3].

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
smallveccrates.io
>= 0.6.3, < 0.6.140.6.14
smallveccrates.io
>= 1.0.0, < 1.6.11.6.1

Affected products

4

Patches

2
5757ac500d4e

Fix potential buffer overflow in `insert_many`

https://github.com/servo/rust-smallvecMatt BrubeckJan 8, 2021via ghsa
2 files changed · +29 14
  • Cargo.toml+1 1 modified
    @@ -1,6 +1,6 @@
     [package]
     name = "smallvec"
    -version = "0.6.13"
    +version = "0.6.14"
     authors = ["Simon Sapin <simon.sapin@exyr.org>"]
     license = "MIT/Apache-2.0"
     repository = "https://github.com/servo/rust-smallvec"
    
  • lib.rs+28 13 modified
    @@ -823,7 +823,7 @@ impl<A: Array> SmallVec<A> {
         /// Insert multiple elements at position `index`, shifting all following elements toward the
         /// back.
         pub fn insert_many<I: IntoIterator<Item=A::Item>>(&mut self, index: usize, iterable: I) {
    -        let iter = iterable.into_iter();
    +        let mut iter = iterable.into_iter();
             if index == self.len() {
                 return self.extend(iter);
             }
    @@ -832,38 +832,40 @@ impl<A: Array> SmallVec<A> {
             assert!(lower_size_bound <= std::isize::MAX as usize);  // Ensure offset is indexable
             assert!(index + lower_size_bound >= index);  // Protect against overflow
             self.reserve(lower_size_bound);
    +        let mut num_added = 0;
     
             unsafe {
                 let old_len = self.len();
                 assert!(index <= old_len);
    -            let mut ptr = self.as_mut_ptr().offset(index as isize);
    +            let ptr = self.as_mut_ptr().offset(index as isize);
     
                 // Move the trailing elements.
                 ptr::copy(ptr, ptr.offset(lower_size_bound as isize), old_len - index);
     
                 // In case the iterator panics, don't double-drop the items we just copied above.
                 self.set_len(index);
     
    -            let mut num_added = 0;
    -            for element in iter {
    -                let mut cur = ptr.offset(num_added as isize);
    -                if num_added >= lower_size_bound {
    -                    // Iterator provided more elements than the hint.  Move trailing items again.
    -                    self.reserve(1);
    -                    ptr = self.as_mut_ptr().offset(index as isize);
    -                    cur = ptr.offset(num_added as isize);
    -                    ptr::copy(cur, cur.offset(1), old_len - index);
    -                }
    +            while num_added < lower_size_bound {
    +                let element = match iter.next() {
    +                    Some(x) => x,
    +                    None => break,
    +                };
    +                let cur = ptr.offset(num_added as isize);
                     ptr::write(cur, element);
                     num_added += 1;
                 }
                 if num_added < lower_size_bound {
                     // Iterator provided fewer elements than the hint
                     ptr::copy(ptr.offset(lower_size_bound as isize), ptr.offset(num_added as isize), old_len - index);
                 }
    -
                 self.set_len(old_len + num_added);
             }
    +
    +        // If the iterator has more than `lower_size_bound` elements, insert the rest one-by-one.
    +        for element in iter {
    +            self.insert(index + num_added, element);
    +            num_added += 1;
    +        }
         }
     
         /// Convert a SmallVec to a Vec, without reallocating if the SmallVec has already spilled onto
    @@ -2371,4 +2373,17 @@ mod tests {
             assert_eq!(v.capacity(), 4);
             assert_eq!(v[..], [0, 1, 2]);
         }
    +
    +    #[test]
    +    fn test_insert_many_overflow() {
    +        let mut v: SmallVec<[u8; 1]> = SmallVec::new();
    +        v.push(123);
    +
    +        // Prepare an iterator with small lower bound
    +        let iter = (0u8..5).filter(|n| n % 2 == 0);
    +        assert_eq!(iter.size_hint().0, 0);
    +
    +        v.insert_many(0, iter);
    +        assert_eq!(&*v, &[0, 2, 4, 123]);
    +    }
     }
    
9998ba0694a6

Fix potential buffer overflow in `insert_many`

https://github.com/servo/rust-smallvecMatt BrubeckJan 8, 2021via ghsa
3 files changed · +38 25
  • Cargo.toml+1 1 modified
    @@ -1,6 +1,6 @@
     [package]
     name = "smallvec"
    -version = "1.6.0"
    +version = "1.6.1"
     edition = "2018"
     authors = ["The Servo Project Developers"]
     license = "MIT/Apache-2.0"
    
  • src/lib.rs+24 24 modified
    @@ -1009,21 +1009,24 @@ impl<A: Array> SmallVec<A> {
         /// Insert multiple elements at position `index`, shifting all following elements toward the
         /// back.
         pub fn insert_many<I: IntoIterator<Item = A::Item>>(&mut self, index: usize, iterable: I) {
    -        let iter = iterable.into_iter();
    +        let mut iter = iterable.into_iter();
             if index == self.len() {
                 return self.extend(iter);
             }
     
             let (lower_size_bound, _) = iter.size_hint();
             assert!(lower_size_bound <= core::isize::MAX as usize); // Ensure offset is indexable
             assert!(index + lower_size_bound >= index); // Protect against overflow
    -        self.reserve(lower_size_bound);
    +
    +        let mut num_added = 0;
    +        let old_len = self.len();
    +        assert!(index <= old_len);
     
             unsafe {
    -            let old_len = self.len();
    -            assert!(index <= old_len);
    +            // Reserve space for `lower_size_bound` elements.
    +            self.reserve(lower_size_bound);
                 let start = self.as_mut_ptr();
    -            let mut ptr = start.add(index);
    +            let ptr = start.add(index);
     
                 // Move the trailing elements.
                 ptr::copy(ptr, ptr.add(lower_size_bound), old_len - index);
    @@ -1036,42 +1039,39 @@ impl<A: Array> SmallVec<A> {
                     len: old_len + lower_size_bound,
                 };
     
    -            let mut num_added = 0;
    -            for element in iter {
    -                let mut cur = ptr.add(num_added);
    -                if num_added >= lower_size_bound {
    -                    // Iterator provided more elements than the hint.  Move trailing items again.
    -                    self.reserve(1);
    -                    let start = self.as_mut_ptr();
    -                    ptr = start.add(index);
    -                    cur = ptr.add(num_added);
    -                    ptr::copy(cur, cur.add(1), old_len - index);
    -
    -                    guard.start = start;
    -                    guard.len += 1;
    -                    guard.skip.end += 1;
    -                }
    +            while num_added < lower_size_bound {
    +                let element = match iter.next() {
    +                    Some(x) => x,
    +                    None => break,
    +                };
    +                let cur = ptr.add(num_added);
                     ptr::write(cur, element);
                     guard.skip.start += 1;
                     num_added += 1;
                 }
    -            mem::forget(guard);
     
                 if num_added < lower_size_bound {
    -                // Iterator provided fewer elements than the hint
    +                // Iterator provided fewer elements than the hint. Move the tail backward.
                     ptr::copy(
                         ptr.add(lower_size_bound),
                         ptr.add(num_added),
                         old_len - index,
                     );
                 }
    -
    +            // There are no more duplicate or uninitialized slots, so the guard is not needed.
                 self.set_len(old_len + num_added);
    +            mem::forget(guard);
    +        }
    +
    +        // Insert any remaining elements one-by-one.
    +        for element in iter {
    +            self.insert(index + num_added, element);
    +            num_added += 1;
             }
     
             struct DropOnPanic<T> {
                 start: *mut T,
    -            skip: Range<usize>,
    +            skip: Range<usize>, // Space we copied-out-of, but haven't written-to yet.
                 len: usize,
             }
     
    
  • src/tests.rs+13 0 modified
    @@ -905,3 +905,16 @@ fn empty_macro() {
     fn zero_size_items() {
         SmallVec::<[(); 0]>::new().push(());
     }
    +
    +#[test]
    +fn test_insert_many_overflow() {
    +    let mut v: SmallVec<[u8; 1]> = SmallVec::new();
    +    v.push(123);
    +
    +    // Prepare an iterator with small lower bound
    +    let iter = (0u8..5).filter(|n| n % 2 == 0);
    +    assert_eq!(iter.size_hint().0, 0);
    +
    +    v.insert_many(0, iter);
    +    assert_eq!(&*v, &[0, 2, 4, 123]);
    +}
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.