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;