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.
| Package | Affected versions | Patched versions |
|---|---|---|
smallveccrates.io | >= 0.6.3, < 0.6.14 | 0.6.14 |
smallveccrates.io | >= 1.0.0, < 1.6.1 | 1.6.1 |
Affected products
4- Rust/smallvecdescription
- ghsa-coords3 versionspkg:cargo/smallvecpkg:rpm/suse/librsvg&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Basesystem%2015%20SP2pkg:rpm/suse/librsvg&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Desktop%20Applications%2015%20SP2
>= 0.6.3, < 0.6.14+ 2 more
- (no CPE)range: >= 0.6.3, < 0.6.14
- (no CPE)range: < 2.46.5-3.3.1
- (no CPE)range: < 2.46.5-3.3.1
Patches
25757ac500d4eFix potential buffer overflow in `insert_many`
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]); + } }
9998ba0694a6Fix potential buffer overflow in `insert_many`
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- github.com/advisories/GHSA-43w2-9j62-hq99ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2021-25900ghsaADVISORY
- github.com/servo/rust-smallvec/commit/5757ac500d4e544485d796b542e4e589749c291bghsaWEB
- github.com/servo/rust-smallvec/commit/9998ba0694a6b51aa6604748b00b6a98f0a0039eghsaWEB
- github.com/servo/rust-smallvec/issues/252ghsaWEB
- rustsec.org/advisories/RUSTSEC-2021-0003.htmlghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.