secmem_alloc/zeroize/
mod.rs

1//! Functions for securely wiping memory.
2//!
3//! This module contains functions for securely and efficiently zeroizing
4//! memory. These operations won't be optimized away be the compiler. They
5//! operate on raw memory regions and can invalidate the memory for types that
6//! do not have all zeros (binary) as a valid value. They should be used during
7//! deallocation, because the memory is unused and the memory needs not contain
8//! a value of a certain type than.
9//!
10//! For good general purpose memory wiping use the [`zeroize`](https://crates.io/crates/zeroize)
11//! crate.
12
13use crate::macros::precondition_memory_range;
14
15cfg_if::cfg_if! {
16    if #[cfg(miri)] {
17        // when running miri we chose a pure rust zeroizer
18        pub use fallback::zeroize_mem;
19    } else if #[cfg(feature = "nightly_core_intrinsics")] {
20        pub use nightly::zeroize_mem;
21    } else {
22        pub use asm_barier::zeroize_mem;
23    }
24}
25
26/// Very fast nightly-only zeroizer.
27///
28/// This zeroizer uses the volatile memset intrinsic which does not
29/// yet have a stable counterpart. It should be very fast, but requires
30/// nightly.
31#[cfg(feature = "nightly_core_intrinsics")]
32mod nightly {
33    use super::*;
34
35    /// Zeroize the memory pointed to by `ptr` and of size `len` bytes.
36    ///
37    /// This is guarantied to be not elided by the compiler.
38    ///
39    /// # Safety
40    /// The caller *must* ensure that `ptr` is valid for writes of `len` bytes,
41    /// see the [`std::ptr`] documentation. In particular this function is
42    /// not atomic.
43    pub unsafe fn zeroize_mem(ptr: *mut u8, len: usize) {
44        precondition_memory_range!(ptr, len);
45        // SAFETY: the caller must uphold the safety contract
46        unsafe {
47            core::intrinsics::volatile_set_memory(ptr, 0, len);
48        }
49    }
50}
51
52/// Fast zeroizer that works on stable and all target platforms.
53///
54/// This zeroizer uses a non-volatile memset, followed by an empty asm block
55/// acting as an optimisation barier. It should be very fast, and according
56/// to my current understanding of the op.sem. the compiler is not
57/// allowed to remove the writes.
58mod asm_barier {
59    use super::*;
60
61    /// Zeroize the memory pointed to by `ptr` and of size `len` bytes.
62    ///
63    /// This is guarantied to be not elided by the compiler.
64    ///
65    /// # Safety
66    /// The caller *must* ensure that `ptr` is valid for writes of `len` bytes,
67    /// see the [`std::ptr`] documentation. In particular this function is
68    /// not atomic.
69    pub unsafe fn zeroize_mem(ptr: *mut u8, len: usize) {
70        precondition_memory_range!(ptr, len);
71        // SAFETY: the caller must uphold the safety contract of `write_bytes`
72        unsafe { ptr.write_bytes(0, len) };
73        // Optimisation barier, so the writes can not be optimised out
74        unsafe {
75            core::arch::asm!(
76                "/* {0} */",
77                in(reg) ptr,
78                options(nostack, readonly, preserves_flags),
79            )
80        };
81    }
82}
83
84/// Simple zeroize a byte at a time zeroizer, useful for tests and works on
85/// miri.
86///
87/// This zeroizer uses a volatile write per byte. This zeroization technique
88/// is pure Rust and available for all target platforms on stable, but very
89/// slow.
90mod fallback {
91    use super::*;
92
93    /// Zeroize the memory pointed to by `ptr` and of size `len` bytes.
94    ///
95    /// This is guarantied to be not elided by the compiler.
96    ///
97    /// # Safety
98    /// The caller *must* ensure that `ptr` is valid for writes of `len` bytes,
99    /// see the [`std::ptr`] documentation. In particular this function is
100    /// not atomic.
101    pub unsafe fn zeroize_mem(mut ptr: *mut u8, len: usize) {
102        precondition_memory_range!(ptr, len);
103        for _i in 0..len {
104            // SAFETY: `ptr` originally pointed into an allocation of `len` bytes so now,
105            // after `_i` steps `len - _i > 0` bytes are left, so `ptr` is valid for
106            // a byte write
107            unsafe {
108                core::ptr::write_volatile(ptr, 0u8);
109            }
110            // SAFETY: after increment, `ptr` points into the same allocation if `_i == len`
111            // or one byte past it, so `add` is sound
112            ptr = unsafe { ptr.add(1) };
113        }
114    }
115}
116
117#[cfg(test)]
118mod tests;