CVE-2018-25023
Description
An issue was discovered in the smallvec crate before 0.6.13 for Rust. It can create an uninitialized value of any type, including a reference type.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
The smallvec crate before 0.6.13 for Rust uses mem::uninitialized() to create values of a user-supplied type, allowing uninitialized references and undefined behavior.
Vulnerability
The smallvec crate for Rust, versions prior to 0.6.13, uses mem::uninitialized() to create values of a user-supplied type T [4]. This is unsound when T is a reference type, which must be non-null and cannot remain uninitialized. The flaw was corrected by replacing mem::uninitialized() with MaybeUninit [1][2][4].
Exploitation
An attacker can trigger this vulnerability by providing a type T that is a reference (e.g., &u8) as the element type for a SmallVec. The crate's implementation then calls mem::uninitialized() to create an instance of that reference type, which results in an uninitialized reference. No special privileges or user interaction beyond controlling the generic type parameter is required.
Impact
This unsoundness can lead to undefined behavior (UB) because reading uninitialized memory as a reference violates Rust's safety guarantees. In practice, this could cause crashes, memory corruption, or potentially information disclosure, depending on how the uninitialized reference is used [4].
Mitigation
The flaw is patched in smallvec version 0.6.13 and later [4]. Users should upgrade to 0.6.13 or later. The RustSec advisory (RUSTSEC-2018-0018) also lists patched versions. No workarounds are known for unpatched versions [4].
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.13 | 0.6.13 |
Affected products
2- rust/smallvecdescription
Patches
1e64afc8c473dUse MaybeUninit for storage of inline items.
2 files changed · +54 −40
Cargo.toml+2 −1 modified@@ -1,6 +1,6 @@ [package] name = "smallvec" -version = "0.6.12" +version = "0.6.13" authors = ["Simon Sapin <simon.sapin@exyr.org>"] license = "MIT/Apache-2.0" repository = "https://github.com/servo/rust-smallvec" @@ -23,6 +23,7 @@ path = "lib.rs" [dependencies] serde = { version = "1", optional = true } +maybe-uninit = "2.0" [dev_dependencies] bincode = "1.0.1"
lib.rs+52 −39 modified@@ -45,18 +45,21 @@ use alloc::vec::Vec; #[cfg(feature = "serde")] extern crate serde; +extern crate maybe_uninit; + #[cfg(not(feature = "std"))] mod std { pub use core::*; } +use maybe_uninit::MaybeUninit; + use std::borrow::{Borrow, BorrowMut}; use std::cmp; use std::fmt; use std::hash::{Hash, Hasher}; use std::iter::{IntoIterator, FromIterator, repeat}; use std::mem; -use std::mem::ManuallyDrop; use std::ops; use std::ptr; use std::slice; @@ -275,26 +278,28 @@ impl<'a, T: 'a> Drop for Drain<'a,T> { #[cfg(feature = "union")] union SmallVecData<A: Array> { - inline: ManuallyDrop<A>, + inline: MaybeUninit<A>, heap: (*mut A::Item, usize), } #[cfg(feature = "union")] impl<A: Array> SmallVecData<A> { #[inline] - unsafe fn inline(&self) -> &A { - &self.inline + unsafe fn inline(&self) -> *const A::Item { + self.inline.as_ptr() as *const A::Item } #[inline] - unsafe fn inline_mut(&mut self) -> &mut A { - &mut self.inline + unsafe fn inline_mut(&mut self) -> *mut A::Item { + self.inline.as_mut_ptr() as *mut A::Item } #[inline] - fn from_inline(inline: A) -> SmallVecData<A> { - SmallVecData { inline: ManuallyDrop::new(inline) } + fn from_inline(inline: MaybeUninit<A>) -> SmallVecData<A> { + SmallVecData { inline } } #[inline] - unsafe fn into_inline(self) -> A { ManuallyDrop::into_inner(self.inline) } + unsafe fn into_inline(self) -> MaybeUninit<A> { + self.inline + } #[inline] unsafe fn heap(&self) -> (*mut A::Item, usize) { self.heap @@ -311,34 +316,34 @@ impl<A: Array> SmallVecData<A> { #[cfg(not(feature = "union"))] enum SmallVecData<A: Array> { - Inline(ManuallyDrop<A>), + Inline(MaybeUninit<A>), Heap((*mut A::Item, usize)), } #[cfg(not(feature = "union"))] impl<A: Array> SmallVecData<A> { #[inline] - unsafe fn inline(&self) -> &A { + unsafe fn inline(&self) -> *const A::Item { match *self { - SmallVecData::Inline(ref a) => a, + SmallVecData::Inline(ref a) => a.as_ptr() as *const A::Item, _ => debug_unreachable!(), } } #[inline] - unsafe fn inline_mut(&mut self) -> &mut A { + unsafe fn inline_mut(&mut self) -> *mut A::Item { match *self { - SmallVecData::Inline(ref mut a) => a, + SmallVecData::Inline(ref mut a) => a.as_mut_ptr() as *mut A::Item, _ => debug_unreachable!(), } } #[inline] - fn from_inline(inline: A) -> SmallVecData<A> { - SmallVecData::Inline(ManuallyDrop::new(inline)) + fn from_inline(inline: MaybeUninit<A>) -> SmallVecData<A> { + SmallVecData::Inline(inline) } #[inline] - unsafe fn into_inline(self) -> A { + unsafe fn into_inline(self) -> MaybeUninit<A> { match self { - SmallVecData::Inline(a) => ManuallyDrop::into_inner(a), + SmallVecData::Inline(a) => a, _ => debug_unreachable!(), } } @@ -403,11 +408,15 @@ impl<A: Array> SmallVec<A> { /// Construct an empty vector #[inline] pub fn new() -> SmallVec<A> { - unsafe { - SmallVec { - capacity: 0, - data: SmallVecData::from_inline(mem::uninitialized()), - } + // Try to detect invalid custom implementations of `Array`. Hopefuly, + // this check should be optimized away entirely for valid ones. + assert!( + mem::size_of::<A>() == A::size() * mem::size_of::<A::Item>() + && mem::align_of::<A>() >= mem::align_of::<A::Item>() + ); + SmallVec { + capacity: 0, + data: SmallVecData::from_inline(MaybeUninit::uninit()), } } @@ -447,10 +456,10 @@ impl<A: Array> SmallVec<A> { pub fn from_vec(mut vec: Vec<A::Item>) -> SmallVec<A> { if vec.capacity() <= A::size() { unsafe { - let mut data = SmallVecData::<A>::from_inline(mem::uninitialized()); + let mut data = SmallVecData::<A>::from_inline(MaybeUninit::uninit()); let len = vec.len(); vec.set_len(0); - ptr::copy_nonoverlapping(vec.as_ptr(), data.inline_mut().ptr_mut(), len); + ptr::copy_nonoverlapping(vec.as_ptr(), data.inline_mut(), len); SmallVec { capacity: len, @@ -483,7 +492,7 @@ impl<A: Array> SmallVec<A> { pub fn from_buf(buf: A) -> SmallVec<A> { SmallVec { capacity: A::size(), - data: SmallVecData::from_inline(buf), + data: SmallVecData::from_inline(MaybeUninit::new(buf)), } } @@ -523,7 +532,7 @@ impl<A: Array> SmallVec<A> { pub unsafe fn from_buf_and_len_unchecked(buf: A, len: usize) -> SmallVec<A> { SmallVec { capacity: len, - data: SmallVecData::from_inline(buf), + data: SmallVecData::from_inline(MaybeUninit::new(buf)), } } @@ -571,7 +580,7 @@ impl<A: Array> SmallVec<A> { let (ptr, len) = self.data.heap(); (ptr, len, self.capacity) } else { - (self.data.inline().ptr(), self.capacity, A::size()) + (self.data.inline(), self.capacity, A::size()) } } } @@ -584,7 +593,7 @@ impl<A: Array> SmallVec<A> { let &mut (ptr, ref mut len_ptr) = self.data.heap_mut(); (ptr, len_ptr, self.capacity) } else { - (self.data.inline_mut().ptr_mut(), &mut self.capacity, A::size()) + (self.data.inline_mut(), &mut self.capacity, A::size()) } } } @@ -651,8 +660,8 @@ impl<A: Array> SmallVec<A> { if unspilled { return; } - self.data = SmallVecData::from_inline(mem::uninitialized()); - ptr::copy_nonoverlapping(ptr, self.data.inline_mut().ptr_mut(), len); + self.data = SmallVecData::from_inline(MaybeUninit::uninit()); + ptr::copy_nonoverlapping(ptr, self.data.inline_mut(), len); self.capacity = len; } else if new_cap != cap { let mut vec = Vec::with_capacity(new_cap); @@ -717,8 +726,8 @@ impl<A: Array> SmallVec<A> { if self.inline_size() >= len { unsafe { let (ptr, len) = self.data.heap(); - self.data = SmallVecData::from_inline(mem::uninitialized()); - ptr::copy_nonoverlapping(ptr, self.data.inline_mut().ptr_mut(), len); + self.data = SmallVecData::from_inline(MaybeUninit::uninit()); + ptr::copy_nonoverlapping(ptr, self.data.inline_mut(), len); deallocate(ptr, self.capacity); self.capacity = len; } @@ -883,7 +892,7 @@ impl<A: Array> SmallVec<A> { unsafe { let data = ptr::read(&self.data); mem::forget(self); - Ok(data.into_inline()) + Ok(data.into_inline().assume_init()) } } } @@ -1041,8 +1050,12 @@ impl<A: Array> SmallVec<A> where A::Item: Copy { SmallVec { capacity: len, data: SmallVecData::from_inline(unsafe { - let mut data: A = mem::uninitialized(); - ptr::copy_nonoverlapping(slice.as_ptr(), data.ptr_mut(), len); + let mut data: MaybeUninit<A> = MaybeUninit::uninit(); + ptr::copy_nonoverlapping( + slice.as_ptr(), + data.as_mut_ptr() as *mut A::Item, + len, + ); data }) } @@ -1587,8 +1600,8 @@ macro_rules! impl_array( unsafe impl<T> Array for [T; $size] { type Item = T; fn size() -> usize { $size } - fn ptr(&self) -> *const T { self.as_ptr() } - fn ptr_mut(&mut self) -> *mut T { self.as_mut_ptr() } + fn ptr(&self) -> *const T { unimplemented!() } + fn ptr_mut(&mut self) -> *mut T { unimplemented!() } } )+ } @@ -1889,7 +1902,7 @@ mod tests { assert_eq!(&v.iter().map(|v| *v).collect::<Vec<_>>(), &[0, 5, 6, 1, 2, 3]); } - #[cfg(feature = "std")] + #[cfg(all(feature = "std", not(miri)))] // Miri currently does not support unwinding #[test] // https://github.com/servo/rust-smallvec/issues/96 fn test_insert_many_panic() {
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-55m5-whcv-c49cghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2018-25023ghsaADVISORY
- github.com/servo/rust-smallvec/commit/e64afc8c473d43e375ab42bd33db2d0d4ac4e41bghsaWEB
- github.com/servo/rust-smallvec/issues/126ghsaWEB
- github.com/servo/rust-smallvec/pull/162ghsaWEB
- raw.githubusercontent.com/rustsec/advisory-db/main/crates/smallvec/RUSTSEC-2018-0018.mdghsaWEB
- rustsec.org/advisories/RUSTSEC-2018-0018.htmlghsaWEB
News mentions
0No linked articles in our index yet.