diff --git a/AUTHORS.md b/AUTHORS.md index 8fee696f..24fa6f80 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -15,5 +15,6 @@ contributions under the terms of the [Apache License, Version 2.0] * David Tolnay ([@dtolnay](https://github.com/dtolnay)) * Kai Ren ([@tyranron](https://github.com/tyranron)) * Murarth ([@murarth](https://github.com/murarth)) +* Niclas Schwarzlose ([@aticu](https://github.com/aticu)) * Yin Guanhao ([@sopium](https://github.com/sopium)) * Will Speak ([@iwillspeak](https://github.com/iwillspeak)) diff --git a/zeroize/src/lib.rs b/zeroize/src/lib.rs index c03ee953..158b51d4 100644 --- a/zeroize/src/lib.rs +++ b/zeroize/src/lib.rs @@ -325,6 +325,32 @@ where /// previous reallocations did not leave values on the heap. fn zeroize(&mut self) { self.iter_mut().zeroize(); + + // Zero the capacity of the `Vec` that is not initialized. + { + // Safety: + // + // This is safe, because `Vec` never allocates more than `isize::MAX` bytes. + // This exact use case is even mentioned in the documentation of `pointer::add`. + let extra_capacity_start = unsafe { self.as_mut_ptr().add(self.len()) as *mut u8 }; + let extra_capacity_len = self.capacity().saturating_sub(self.len()); + + for i in 0..(extra_capacity_len * core::mem::size_of::()) { + // Safety: + // + // This is safe, because `Vec` never allocates more than `isize::MAX` bytes. + let current_ptr = unsafe { extra_capacity_start.add(i) }; + // Safety: + // + // `current_ptr` is valid, because it lies within the allocation of the `Vec`. + // It is also properly aligned, because we write a `u8`, which has an alignment of + // 1. + unsafe { ptr::write_volatile(current_ptr, 0) }; + } + + atomic_fence(); + } + self.clear(); } } @@ -458,6 +484,43 @@ mod tests { assert!(vec.is_empty()); } + #[cfg(feature = "alloc")] + #[test] + fn zeroize_vec_entire_capacity() { + #[derive(Clone)] + struct PanicOnNonZeroDrop(u64); + + impl Zeroize for PanicOnNonZeroDrop { + fn zeroize(&mut self) { + self.0 = 0; + } + } + + impl Drop for PanicOnNonZeroDrop { + fn drop(&mut self) { + if self.0 != 0 { + panic!("dropped non-zeroized data"); + } + } + } + + // Ensure that the entire capacity of the vec is zeroized and that no unitinialized data + // is ever interpreted as initialized + let mut vec = vec![PanicOnNonZeroDrop(42); 2]; + + unsafe { + vec.set_len(1); + } + + vec.zeroize(); + + unsafe { + vec.set_len(2); + } + + drop(vec); + } + #[cfg(feature = "alloc")] #[test] fn zeroize_string() {