CVE-2018-25023 (published 2021-12-27T00:15Z)

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.

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:N/I:P/A:N)
CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N)

Weakness information:
cwe_id: CWE-908
cwe_name: Use of Uninitialized Resource
description: The product uses or accesses a resource that has not been initialized.
extended_description: When a resource has not been properly initialized, the product may behave unexpectedly. This may lead to a crash or invalid memory access, but the consequences vary depending on the type of resource and how it is used within the product.
         

Fix information:
repo_url: https://github.com/servo/rust-smallvec
commit_details: repo_url: https://github.com/servo/rust-smallvec
author: Thom Chiovoloni
author_date: 2019-09-08 01:24:45-07:00
msg: 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.
repository: repo_url: https://github.com/servo/rust-smallvec
repo_name: servo/rust-smallvec
description: "Small vector" optimization for Rust: store up to a small number of items on the stack
repo_language: Rust
owner: servo
file_changes: filename: Cargo.toml
change_type: ModificationType.MODIFY
diff: @@ -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"

programming_language: Lua
filename: lib.rs
change_type: ModificationType.MODIFY
diff: @@ -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() {

programming_language: Rust