CVE-2018-25023: Use of Uninitialized Resource (CWE-908)
value: 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.
CWE description: The product uses or accesses a resource that has not been initialized.
CVSS v3 base score: 7.5
Published: 2021-12-27T00:15Z
Repository: https://github.com/servo/rust-smallvec
Language: Rust
Files changed: Cargo.toml, lib.rs
Commit date: 2019-11-04 11:03:04 -0800
Commit message: Use MaybeUninit for storage of inline items.

This is a backport of #162 to the smallvec 0.6 branch. To avoid bumping the
minimum Rust version, the `maybe-uninit` crate is used in place of
`std::mem::MaybeUninit`.  To avoid breaking changes, the `Array::ptr` and
`ptr_mut` methods are retained but are no longer used, and the API to
`from_buf_and_len_unchecked` is unchanged.

Diff: diff --git a/Cargo.toml b/Cargo.toml
index 8622f80..299a646 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,8 +1,8 @@
 [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"
 description = "'Small vector' optimization: store up to a small number of items on the stack"
 keywords = ["small", "vec", "vector", "stack", "no_std"]
@@ -21,8 +21,9 @@ may_dangle = []
 name = "smallvec"
 path = "lib.rs"
 
 [dependencies]
 serde = { version = "1", optional = true }
+maybe-uninit = "2.0"
 
 [dev_dependencies]
 bincode = "1.0.1"
diff --git a/lib.rs b/lib.rs
index cedf2bd..ffb8766 100644
--- a/lib.rs
+++ b/lib.rs
@@ -43,22 +43,25 @@ extern crate alloc;
 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;
 #[cfg(feature = "std")]
 use std::io;
@@ -273,30 +276,32 @@ 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
     }
     #[inline]
@@ -309,38 +314,38 @@ 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!(),
         }
     }
     #[inline]
     unsafe fn heap(&self) -> (*mut A::Item, usize) {
@@ -401,15 +406,19 @@ pub struct SmallVec<A: Array> {
 
 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()),
         }
     }
 
     /// Construct an empty vector with enough capacity pre-allocated to store at least `n`
     /// elements.
@@ -445,14 +454,14 @@ impl<A: Array> SmallVec<A> {
     /// ```
     #[inline]
     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,
                     data,
                 }
@@ -481,11 +490,11 @@ impl<A: Array> SmallVec<A> {
     /// ```
     #[inline]
     pub fn from_buf(buf: A) -> SmallVec<A> {
         SmallVec {
             capacity: A::size(),
-            data: SmallVecData::from_inline(buf),
+            data: SmallVecData::from_inline(MaybeUninit::new(buf)),
         }
     }
 
     /// Constructs a new `SmallVec` on the stack from an `A` without
     /// copying elements. Also sets the length, which must be less or
@@ -521,11 +530,11 @@ impl<A: Array> SmallVec<A> {
     /// ```
     #[inline]
     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)),
         }
     }
 
 
     /// Sets the length of a vector.
@@ -569,11 +578,11 @@ impl<A: Array> SmallVec<A> {
         unsafe {
             if self.spilled() {
                 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())
             }
         }
     }
 
     /// Returns a tuple with (data ptr, len ptr, capacity)
@@ -582,11 +591,11 @@ impl<A: Array> SmallVec<A> {
         unsafe {
             if self.spilled() {
                 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())
             }
         }
     }
 
     /// Returns `true` if the data has spilled into a separate heap-allocated buffer.
@@ -649,12 +658,12 @@ impl<A: Array> SmallVec<A> {
             assert!(new_cap >= len);
             if new_cap <= self.inline_size() {
                 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);
                 let new_alloc = vec.as_mut_ptr();
                 mem::forget(vec);
@@ -715,12 +724,12 @@ impl<A: Array> SmallVec<A> {
         }
         let len = self.len();
         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;
             }
         } else if self.capacity() > len {
             self.grow(len);
@@ -881,11 +890,11 @@ impl<A: Array> SmallVec<A> {
             Err(self)
         } else {
             unsafe {
                 let data = ptr::read(&self.data);
                 mem::forget(self);
-                Ok(data.into_inline())
+                Ok(data.into_inline().assume_init())
             }
         }
     }
 
     /// Retains only the elements specified by the predicate.
@@ -1039,12 +1048,16 @@ impl<A: Array> SmallVec<A> where A::Item: Copy {
         let len = slice.len();
         if len <= A::size() {
             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
                 })
             }
         } else {
             let mut b = slice.to_vec();
@@ -1585,12 +1598,12 @@ macro_rules! impl_array(
     ($($size:expr),+) => {
         $(
             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!() }
             }
         )+
     }
 );
 
@@ -1887,11 +1900,11 @@ mod tests {
         assert_eq!(v.len(), 4);
         v.insert_many(1, MockHintIter{x: [5, 6].iter().cloned(), hint: 1});
         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() {
         struct PanicOnDoubleDrop {
             dropped: Box<bool>

Vulnerable code: version = "0.6.12"
use std::mem::ManuallyDrop;
    inline: ManuallyDrop<A>,
    unsafe fn inline(&self) -> &A {
        &self.inline
    unsafe fn inline_mut(&mut self) -> &mut A {
        &mut self.inline
    fn from_inline(inline: A) -> SmallVecData<A> {
        SmallVecData { inline: ManuallyDrop::new(inline) }
    unsafe fn into_inline(self) -> A { ManuallyDrop::into_inner(self.inline) }
    Inline(ManuallyDrop<A>),
    unsafe fn inline(&self) -> &A {
            SmallVecData::Inline(ref a) => a,
    unsafe fn inline_mut(&mut self) -> &mut A {
            SmallVecData::Inline(ref mut a) => a,
    fn from_inline(inline: A) -> SmallVecData<A> {
        SmallVecData::Inline(ManuallyDrop::new(inline))
    unsafe fn into_inline(self) -> A {
            SmallVecData::Inline(a) => ManuallyDrop::into_inner(a),
        unsafe {
            SmallVec {
                capacity: 0,
                data: SmallVecData::from_inline(mem::uninitialized()),
            }
                let mut data = SmallVecData::<A>::from_inline(mem::uninitialized());
                ptr::copy_nonoverlapping(vec.as_ptr(), data.inline_mut().ptr_mut(), len);
            data: SmallVecData::from_inline(buf),
            data: SmallVecData::from_inline(buf),
                (self.data.inline().ptr(), self.capacity, A::size())
                (self.data.inline_mut().ptr_mut(), &mut self.capacity, A::size())
                self.data = SmallVecData::from_inline(mem::uninitialized());
                ptr::copy_nonoverlapping(ptr, self.data.inline_mut().ptr_mut(), len);
                self.data = SmallVecData::from_inline(mem::uninitialized());
                ptr::copy_nonoverlapping(ptr, self.data.inline_mut().ptr_mut(), len);
                Ok(data.into_inline())
                    let mut data: A = mem::uninitialized();
                    ptr::copy_nonoverlapping(slice.as_ptr(), data.ptr_mut(), len);
                fn ptr(&self) -> *const T { self.as_ptr() }
                fn ptr_mut(&mut self) -> *mut T { self.as_mut_ptr() }
    #[cfg(feature = "std")]
Fixed code: version = "0.6.13"
maybe-uninit = "2.0"
extern crate maybe_uninit;

use maybe_uninit::MaybeUninit;

    inline: MaybeUninit<A>,
    unsafe fn inline(&self) -> *const A::Item {
        self.inline.as_ptr() as *const A::Item
    unsafe fn inline_mut(&mut self) -> *mut A::Item {
        self.inline.as_mut_ptr() as *mut A::Item
    fn from_inline(inline: MaybeUninit<A>) -> SmallVecData<A> {
        SmallVecData { inline }
    unsafe fn into_inline(self) -> MaybeUninit<A> {
        self.inline
    }
    Inline(MaybeUninit<A>),
    unsafe fn inline(&self) -> *const A::Item {
            SmallVecData::Inline(ref a) => a.as_ptr() as *const A::Item,
    unsafe fn inline_mut(&mut self) -> *mut A::Item {
            SmallVecData::Inline(ref mut a) => a.as_mut_ptr() as *mut A::Item,
    fn from_inline(inline: MaybeUninit<A>) -> SmallVecData<A> {
        SmallVecData::Inline(inline)
    unsafe fn into_inline(self) -> MaybeUninit<A> {
            SmallVecData::Inline(a) => a,
        // 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()),
                let mut data = SmallVecData::<A>::from_inline(MaybeUninit::uninit());
                ptr::copy_nonoverlapping(vec.as_ptr(), data.inline_mut(), len);
            data: SmallVecData::from_inline(MaybeUninit::new(buf)),
            data: SmallVecData::from_inline(MaybeUninit::new(buf)),
                (self.data.inline(), self.capacity, A::size())
                (self.data.inline_mut(), &mut self.capacity, A::size())
                self.data = SmallVecData::from_inline(MaybeUninit::uninit());
                ptr::copy_nonoverlapping(ptr, self.data.inline_mut(), len);
                self.data = SmallVecData::from_inline(MaybeUninit::uninit());
                ptr::copy_nonoverlapping(ptr, self.data.inline_mut(), len);
                Ok(data.into_inline().assume_init())
                    let mut data: MaybeUninit<A> = MaybeUninit::uninit();
                    ptr::copy_nonoverlapping(
                        slice.as_ptr(),
                        data.as_mut_ptr() as *mut A::Item,
                        len,
                    );
                fn ptr(&self) -> *const T { unimplemented!() }
                fn ptr_mut(&mut self) -> *mut T { unimplemented!() }
    #[cfg(all(feature = "std", not(miri)))] // Miri currently does not support unwinding