From 7b62b6c6b7f3e222dc6dc859a1f82b3c93973a12 Mon Sep 17 00:00:00 2001 From: Noah Stiltner Date: Sun, 21 Jan 2024 17:59:47 -0600 Subject: [PATCH 01/13] added zeroize_flat_type() --- zeroize/src/lib.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/zeroize/src/lib.rs b/zeroize/src/lib.rs index b67b5c95..6da3c426 100644 --- a/zeroize/src/lib.rs +++ b/zeroize/src/lib.rs @@ -795,6 +795,21 @@ unsafe fn volatile_set(dst: *mut T, src: T, count: usize) { } } +/// Zeroizes a flat type/struct. Only zeroizes the values that it owns, and it does not work on +/// dynamically sized values or trait objects. +#[inline(always)] +pub fn zeroize_flat_type(data: &mut T) { + let size = mem::size_of::(); + let data_ptr = (data as *mut T).cast::(); + // Safety: + // + // This is safe because `mem::size_of()` returns the exact size of the object in memory. + unsafe { + volatile_set(data_ptr, 0, size) + } + atomic_fence() +} + /// Internal module used as support for `AssertZeroizeOnDrop`. #[doc(hidden)] pub mod __internal { From d4999a5cfd7bfb08d996ec6e7c2612673db53806 Mon Sep 17 00:00:00 2001 From: Noah Stiltner Date: Sun, 21 Jan 2024 18:10:34 -0600 Subject: [PATCH 02/13] restricted the method for Sized types and ammended the safety note --- zeroize/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/zeroize/src/lib.rs b/zeroize/src/lib.rs index 6da3c426..55cade76 100644 --- a/zeroize/src/lib.rs +++ b/zeroize/src/lib.rs @@ -798,12 +798,13 @@ unsafe fn volatile_set(dst: *mut T, src: T, count: usize) { /// Zeroizes a flat type/struct. Only zeroizes the values that it owns, and it does not work on /// dynamically sized values or trait objects. #[inline(always)] -pub fn zeroize_flat_type(data: &mut T) { +pub fn zeroize_flat_type(data: &mut T) { let size = mem::size_of::(); let data_ptr = (data as *mut T).cast::(); // Safety: // - // This is safe because `mem::size_of()` returns the exact size of the object in memory. + // This is safe because `mem::size_of()` returns the exact size of the object in memory, and + // `data_ptr` points directly to the first byte of the data. unsafe { volatile_set(data_ptr, 0, size) } From b42151d51f46f4f0e7c2a4650f19e6f2ecb3df24 Mon Sep 17 00:00:00 2001 From: Noah Stiltner Date: Sun, 21 Jan 2024 18:20:08 -0600 Subject: [PATCH 03/13] fmt again :( --- zeroize/src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/zeroize/src/lib.rs b/zeroize/src/lib.rs index 55cade76..1db87a1e 100644 --- a/zeroize/src/lib.rs +++ b/zeroize/src/lib.rs @@ -795,7 +795,7 @@ unsafe fn volatile_set(dst: *mut T, src: T, count: usize) { } } -/// Zeroizes a flat type/struct. Only zeroizes the values that it owns, and it does not work on +/// Zeroizes a flat type/struct. Only zeroizes the values that it owns, and it does not work on /// dynamically sized values or trait objects. #[inline(always)] pub fn zeroize_flat_type(data: &mut T) { @@ -805,9 +805,7 @@ pub fn zeroize_flat_type(data: &mut T) { // // This is safe because `mem::size_of()` returns the exact size of the object in memory, and // `data_ptr` points directly to the first byte of the data. - unsafe { - volatile_set(data_ptr, 0, size) - } + unsafe { volatile_set(data_ptr, 0, size) } atomic_fence() } From 4801c84f4f645857aa6620862b2d129a7df62d58 Mon Sep 17 00:00:00 2001 From: Noah Stiltner Date: Sun, 21 Jan 2024 20:03:56 -0600 Subject: [PATCH 04/13] revised zeroize_flat_type() --- zeroize/src/lib.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/zeroize/src/lib.rs b/zeroize/src/lib.rs index 1db87a1e..ccfa2b02 100644 --- a/zeroize/src/lib.rs +++ b/zeroize/src/lib.rs @@ -797,15 +797,19 @@ unsafe fn volatile_set(dst: *mut T, src: T, count: usize) { /// Zeroizes a flat type/struct. Only zeroizes the values that it owns, and it does not work on /// dynamically sized values or trait objects. +/// +/// ## Safety: +/// - The type must not contain references to outside data or dynamically sized data +/// - This function can invalidate the type if it is used after this function is called on it. It is +/// advisable to call this method in `drop()`. #[inline(always)] -pub fn zeroize_flat_type(data: &mut T) { +pub unsafe fn zeroize_flat_type(data: *mut T) { let size = mem::size_of::(); - let data_ptr = (data as *mut T).cast::(); // Safety: // // This is safe because `mem::size_of()` returns the exact size of the object in memory, and // `data_ptr` points directly to the first byte of the data. - unsafe { volatile_set(data_ptr, 0, size) } + volatile_set(data as *mut u8, 0, size); atomic_fence() } From aace15ef3d2651a4aa0ee2c3a4cbb0901c6306ab Mon Sep 17 00:00:00 2001 From: Noah Stiltner Date: Sun, 21 Jan 2024 20:05:30 -0600 Subject: [PATCH 05/13] fmt :| --- zeroize/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zeroize/src/lib.rs b/zeroize/src/lib.rs index ccfa2b02..1166d83c 100644 --- a/zeroize/src/lib.rs +++ b/zeroize/src/lib.rs @@ -797,11 +797,11 @@ unsafe fn volatile_set(dst: *mut T, src: T, count: usize) { /// Zeroizes a flat type/struct. Only zeroizes the values that it owns, and it does not work on /// dynamically sized values or trait objects. -/// +/// /// ## Safety: /// - The type must not contain references to outside data or dynamically sized data -/// - This function can invalidate the type if it is used after this function is called on it. It is -/// advisable to call this method in `drop()`. +/// - This function can invalidate the type if it is used after this function is called on it. It is +/// advisable to call this method in `impl Drop`. #[inline(always)] pub unsafe fn zeroize_flat_type(data: *mut T) { let size = mem::size_of::(); From 8ab3c8b7f7aed42ced2be11d77977c3ba7aea26f Mon Sep 17 00:00:00 2001 From: Noah Stiltner Date: Sun, 21 Jan 2024 20:09:27 -0600 Subject: [PATCH 06/13] removed an invalid safety comment --- zeroize/src/lib.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/zeroize/src/lib.rs b/zeroize/src/lib.rs index 1166d83c..e6664c1f 100644 --- a/zeroize/src/lib.rs +++ b/zeroize/src/lib.rs @@ -805,10 +805,6 @@ unsafe fn volatile_set(dst: *mut T, src: T, count: usize) { #[inline(always)] pub unsafe fn zeroize_flat_type(data: *mut T) { let size = mem::size_of::(); - // Safety: - // - // This is safe because `mem::size_of()` returns the exact size of the object in memory, and - // `data_ptr` points directly to the first byte of the data. volatile_set(data as *mut u8, 0, size); atomic_fence() } From 9961905bfc60594ed75e8b3d79af277729b8a323 Mon Sep 17 00:00:00 2001 From: Noah Stiltner Date: Sun, 21 Jan 2024 20:13:26 -0600 Subject: [PATCH 07/13] Revert "removed an invalid safety comment"; was confused for a moment This reverts commit 8ab3c8b7f7aed42ced2be11d77977c3ba7aea26f. --- zeroize/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zeroize/src/lib.rs b/zeroize/src/lib.rs index e6664c1f..1166d83c 100644 --- a/zeroize/src/lib.rs +++ b/zeroize/src/lib.rs @@ -805,6 +805,10 @@ unsafe fn volatile_set(dst: *mut T, src: T, count: usize) { #[inline(always)] pub unsafe fn zeroize_flat_type(data: *mut T) { let size = mem::size_of::(); + // Safety: + // + // This is safe because `mem::size_of()` returns the exact size of the object in memory, and + // `data_ptr` points directly to the first byte of the data. volatile_set(data as *mut u8, 0, size); atomic_fence() } From 452420019130e27383d0d8ea10a07c63e617125a Mon Sep 17 00:00:00 2001 From: Noah Stiltner Date: Mon, 22 Jan 2024 18:36:55 -0600 Subject: [PATCH 08/13] revised docs --- zeroize/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/zeroize/src/lib.rs b/zeroize/src/lib.rs index 1166d83c..06335b7e 100644 --- a/zeroize/src/lib.rs +++ b/zeroize/src/lib.rs @@ -796,9 +796,10 @@ unsafe fn volatile_set(dst: *mut T, src: T, count: usize) { } /// Zeroizes a flat type/struct. Only zeroizes the values that it owns, and it does not work on -/// dynamically sized values or trait objects. +/// dynamically sized values or trait objects. Do not use this function on a type that already +/// implements `ZeroizeOnDrop`. /// -/// ## Safety: +/// # Safety: /// - The type must not contain references to outside data or dynamically sized data /// - This function can invalidate the type if it is used after this function is called on it. It is /// advisable to call this method in `impl Drop`. From 254822825f654d88f776eefcbfe746b8147582cb Mon Sep 17 00:00:00 2001 From: Noah Stiltner Date: Mon, 22 Jan 2024 18:38:32 -0600 Subject: [PATCH 09/13] removed colon from safety section :/ --- zeroize/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zeroize/src/lib.rs b/zeroize/src/lib.rs index 06335b7e..f19e6211 100644 --- a/zeroize/src/lib.rs +++ b/zeroize/src/lib.rs @@ -799,7 +799,7 @@ unsafe fn volatile_set(dst: *mut T, src: T, count: usize) { /// dynamically sized values or trait objects. Do not use this function on a type that already /// implements `ZeroizeOnDrop`. /// -/// # Safety: +/// # Safety /// - The type must not contain references to outside data or dynamically sized data /// - This function can invalidate the type if it is used after this function is called on it. It is /// advisable to call this method in `impl Drop`. From 9fdd1cd9841990dae9200605ec0ea31629e0eda2 Mon Sep 17 00:00:00 2001 From: Noah Stiltner Date: Tue, 23 Jan 2024 19:33:34 -0600 Subject: [PATCH 10/13] revised safety docs and added examples --- zeroize/src/lib.rs | 88 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 2 deletions(-) diff --git a/zeroize/src/lib.rs b/zeroize/src/lib.rs index f19e6211..9b03d54a 100644 --- a/zeroize/src/lib.rs +++ b/zeroize/src/lib.rs @@ -800,9 +800,93 @@ unsafe fn volatile_set(dst: *mut T, src: T, count: usize) { /// implements `ZeroizeOnDrop`. /// /// # Safety -/// - The type must not contain references to outside data or dynamically sized data +/// - The type must not contain references to outside data or dynamically sized data, such as Vec +/// or String. /// - This function can invalidate the type if it is used after this function is called on it. It is -/// advisable to call this method in `impl Drop`. +/// advisable to call this function in `impl Drop`. +/// - The bit pattern of all zeroes must be valid for the data being zeroized. This may not be true for +/// enums and pointers. +/// +/// # Examples +/// Safe usage for a simple struct: +/// ``` +/// use zeroize::{ZeroizeOnDrop, zeroize_flat_type}; +/// +/// struct DataToZeroize(u64); +/// +/// struct MoreDataToZeroize { +/// flat_data_1: [u8; 32], +/// flat_data_2: DataToZeroize, +/// } +/// +/// impl Drop for MoreDataToZeroize { +/// fn drop(&mut self) { +/// unsafe { zeroize_flat_type(self as *mut Self) } +/// } +/// } +/// impl ZeroizeOnDrop for MoreDataToZeroize {} +/// +/// let mut data = MoreDataToZeroize { +/// flat_data_1: [3u8; 32], +/// flat_data_2: DataToZeroize(123u64) +/// }; +/// +/// // data gets zeroized when dropped +/// ``` +/// ## Unsafe examples +/// Below is an unsafe example. It is not comprehensive, but if the data you want to zeroize +/// contains any of the following types, consider using `Zeroize` instead of `zeroize_flat_type`. +/// ``` +/// use std::boxed::Box; +/// use std::collections::HashMap; +/// use std::sync::Arc; +/// use std::vec::Vec; +/// +/// struct UnsafeExample<'a> { +/// // zeroizing pointers will create a null pointer, which could lead to dereferencing +/// // null pointers +/// ptr: *const u8, +/// ptr_mut: *mut u8, +/// +/// // references are non-nullable and should point to valid data +/// reference: &'a u8, +/// reference_mut: &'a mut u8, +/// +/// // smart pointers are designed to manage ownership and deallocation of heap memory. +/// // zeroizing them can mess up this management and result in memory leaks or double frees. +/// smart_ptr_1: Vec, +/// smart_ptr_2: Box, +/// smart_ptr_3: Arc, +/// hashmap: HashMap, +/// string: String +/// } +/// +/// // calling `zeroize_flat_type()` on this struct could be unsafe +/// ``` +/// +/// The below example shows what happens when `zeroize_flat_type` is ran on a type containing a +/// smart pointer. +/// ``` +/// use zeroize::zeroize_flat_type; +/// +/// struct UnsafeExample(String); +/// +/// let mut data = UnsafeExample(String::from("take care when zeroizing with zeroize_flat_type()")); +/// +/// // save the original pointer to determine if the data is still in memory after zeroizing +/// let original_ptr = data.0.as_ptr(); +/// +/// unsafe { zeroize_flat_type(&mut data as *mut UnsafeExample) } +/// +/// // the String's metadata was overwritten (such as the pointer to the heap, length, capacity) +/// assert_eq!(data.0.as_ptr().is_null(), true); +/// assert_eq!(data.0.len(), 0); +/// assert_eq!(data.0.capacity(), 0); +/// +/// // the original data still lives on the heap, possibly resulting in a little memory leak +/// let memory_inspection = unsafe { core::slice::from_raw_parts(original_ptr, 49) }; +/// assert_eq!(memory_inspection, b"take care when zeroizing with zeroize_flat_type()"); +/// ``` #[inline(always)] pub unsafe fn zeroize_flat_type(data: *mut T) { let size = mem::size_of::(); From dc91cc21e972abc9e9dfe10a5379682967bcd3b7 Mon Sep 17 00:00:00 2001 From: Noah Stiltner Date: Tue, 23 Jan 2024 19:36:02 -0600 Subject: [PATCH 11/13] add no_run for the bad example --- zeroize/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zeroize/src/lib.rs b/zeroize/src/lib.rs index 9b03d54a..ad67b725 100644 --- a/zeroize/src/lib.rs +++ b/zeroize/src/lib.rs @@ -866,7 +866,7 @@ unsafe fn volatile_set(dst: *mut T, src: T, count: usize) { /// /// The below example shows what happens when `zeroize_flat_type` is ran on a type containing a /// smart pointer. -/// ``` +/// ```no_run /// use zeroize::zeroize_flat_type; /// /// struct UnsafeExample(String); From 92d89e37420b9967b8bd4d9354bf098fc147a044 Mon Sep 17 00:00:00 2001 From: Noah Stiltner Date: Wed, 24 Jan 2024 17:20:23 -0600 Subject: [PATCH 12/13] revised docs some more --- zeroize/src/lib.rs | 99 +++++++++++++++------------------------------- 1 file changed, 32 insertions(+), 67 deletions(-) diff --git a/zeroize/src/lib.rs b/zeroize/src/lib.rs index ad67b725..bf139147 100644 --- a/zeroize/src/lib.rs +++ b/zeroize/src/lib.rs @@ -796,8 +796,8 @@ unsafe fn volatile_set(dst: *mut T, src: T, count: usize) { } /// Zeroizes a flat type/struct. Only zeroizes the values that it owns, and it does not work on -/// dynamically sized values or trait objects. Do not use this function on a type that already -/// implements `ZeroizeOnDrop`. +/// dynamically sized values or trait objects. It would be inefficient to use this function on a +/// type that already implements `ZeroizeOnDrop`. /// /// # Safety /// - The type must not contain references to outside data or dynamically sized data, such as Vec @@ -807,89 +807,54 @@ unsafe fn volatile_set(dst: *mut T, src: T, count: usize) { /// - The bit pattern of all zeroes must be valid for the data being zeroized. This may not be true for /// enums and pointers. /// +/// # Incompatible data types +/// Some data types that cannot be safely zeroized using `zeroize_flat_type` include, but are not +/// limited to: +/// - pointers such as +/// - *const u8 +/// - *mut u8 +/// - references such as +/// - &T +/// - &mut T +/// - smart pointers and collections +/// - Arc +/// - Box +/// - Vec +/// - HashMap +/// - String +/// +/// Some data types that may be invalid after calling `zeroize_flat_type`: +/// - enums +/// /// # Examples -/// Safe usage for a simple struct: +/// Safe usage for a struct containing strictly flat data: /// ``` /// use zeroize::{ZeroizeOnDrop, zeroize_flat_type}; /// -/// struct DataToZeroize(u64); -/// -/// struct MoreDataToZeroize { +/// struct DataToZeroize { /// flat_data_1: [u8; 32], -/// flat_data_2: DataToZeroize, +/// flat_data_2: SomeMoreFlatData, /// } /// -/// impl Drop for MoreDataToZeroize { +/// struct SomeMoreFlatData(u64); +/// +/// impl Drop for DataToZeroize { /// fn drop(&mut self) { /// unsafe { zeroize_flat_type(self as *mut Self) } /// } /// } -/// impl ZeroizeOnDrop for MoreDataToZeroize {} +/// impl ZeroizeOnDrop for DatatoZeroize {} /// -/// let mut data = MoreDataToZeroize { +/// let mut data = DataToZerioze { /// flat_data_1: [3u8; 32], -/// flat_data_2: DataToZeroize(123u64) +/// flat_data_2: SomeMoreFlatData(123u64) /// }; /// /// // data gets zeroized when dropped /// ``` -/// ## Unsafe examples -/// Below is an unsafe example. It is not comprehensive, but if the data you want to zeroize -/// contains any of the following types, consider using `Zeroize` instead of `zeroize_flat_type`. -/// ``` -/// use std::boxed::Box; -/// use std::collections::HashMap; -/// use std::sync::Arc; -/// use std::vec::Vec; -/// -/// struct UnsafeExample<'a> { -/// // zeroizing pointers will create a null pointer, which could lead to dereferencing -/// // null pointers -/// ptr: *const u8, -/// ptr_mut: *mut u8, -/// -/// // references are non-nullable and should point to valid data -/// reference: &'a u8, -/// reference_mut: &'a mut u8, -/// -/// // smart pointers are designed to manage ownership and deallocation of heap memory. -/// // zeroizing them can mess up this management and result in memory leaks or double frees. -/// smart_ptr_1: Vec, -/// smart_ptr_2: Box, -/// smart_ptr_3: Arc, -/// hashmap: HashMap, -/// string: String -/// } -/// -/// // calling `zeroize_flat_type()` on this struct could be unsafe -/// ``` -/// -/// The below example shows what happens when `zeroize_flat_type` is ran on a type containing a -/// smart pointer. -/// ```no_run -/// use zeroize::zeroize_flat_type; -/// -/// struct UnsafeExample(String); -/// -/// let mut data = UnsafeExample(String::from("take care when zeroizing with zeroize_flat_type()")); -/// -/// // save the original pointer to determine if the data is still in memory after zeroizing -/// let original_ptr = data.0.as_ptr(); -/// -/// unsafe { zeroize_flat_type(&mut data as *mut UnsafeExample) } -/// -/// // the String's metadata was overwritten (such as the pointer to the heap, length, capacity) -/// assert_eq!(data.0.as_ptr().is_null(), true); -/// assert_eq!(data.0.len(), 0); -/// assert_eq!(data.0.capacity(), 0); -/// -/// // the original data still lives on the heap, possibly resulting in a little memory leak -/// let memory_inspection = unsafe { core::slice::from_raw_parts(original_ptr, 49) }; -/// assert_eq!(memory_inspection, b"take care when zeroizing with zeroize_flat_type()"); -/// ``` #[inline(always)] -pub unsafe fn zeroize_flat_type(data: *mut T) { - let size = mem::size_of::(); +pub unsafe fn zeroize_flat_type(data: *mut F) { + let size = mem::size_of::(); // Safety: // // This is safe because `mem::size_of()` returns the exact size of the object in memory, and From 2ff71abfd44f41875611d30e220b7ac3b20f6750 Mon Sep 17 00:00:00 2001 From: Noah Stiltner Date: Wed, 24 Jan 2024 17:21:59 -0600 Subject: [PATCH 13/13] typo --- zeroize/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zeroize/src/lib.rs b/zeroize/src/lib.rs index bf139147..9326d075 100644 --- a/zeroize/src/lib.rs +++ b/zeroize/src/lib.rs @@ -843,9 +843,9 @@ unsafe fn volatile_set(dst: *mut T, src: T, count: usize) { /// unsafe { zeroize_flat_type(self as *mut Self) } /// } /// } -/// impl ZeroizeOnDrop for DatatoZeroize {} +/// impl ZeroizeOnDrop for DataToZeroize {} /// -/// let mut data = DataToZerioze { +/// let mut data = DataToZeroize { /// flat_data_1: [3u8; 32], /// flat_data_2: SomeMoreFlatData(123u64) /// };