Skip to main content

zeroize/
barrier.rs

1/// Observe the referenced data and prevent the compiler from removing previous writes to it.
2///
3/// This function acts like [`core::hint::black_box`] but takes a reference and
4/// does not return the passed value.
5///
6/// It's implemented using the [`core::arch::asm!`] macro on target arches where `asm!` is stable,
7/// i.e. `aarch64`, `arm`, `arm64ec`, `loongarch64`, `riscv32`, `riscv64`, `s390x`, `x86`, and
8/// `x86_64`.
9///
10/// On all other targets it's implemented using [`core::hint::black_box`] and custom `black_box`
11/// implemented using `#[inline(never)]` and `read_volatile`.
12///
13/// # Examples
14/// ```
15/// use core::num::NonZeroU32;
16/// use zeroize::{ZeroizeOnDrop, zeroize_flat_type};
17///
18/// # type ThirdPartyType = u32;
19///
20/// struct DataToZeroize {
21///     buf: [u8; 32],
22///     // `ThirdPartyType` can be a type with private fields
23///     // defined in a third-party crate and which does not implement
24///     // `Zeroize` or zeroization on drop.
25///     data: ThirdPartyType,
26///     pos: NonZeroU32,
27/// }
28///
29/// struct SomeMoreFlatData(u64);
30///
31/// impl Drop for DataToZeroize {
32///     fn drop(&mut self) {
33///         self.buf = [0u8; 32];
34///         self.data = ThirdPartyType::default();
35///         self.pos = NonZeroU32::new(32).unwrap();
36///         zeroize::optimization_barrier(self);
37///     }
38/// }
39///
40/// impl zeroize::ZeroizeOnDrop for DataToZeroize {}
41///
42/// let mut data = DataToZeroize {
43///     buf: [3u8; 32],
44///     data: ThirdPartyType::default(),
45///     pos: NonZeroU32::new(32).unwrap(),
46/// };
47///
48/// // data gets zeroized when dropped
49/// ```
50///
51/// Note that erasure of `ThirdPartyType` demonstrated above can be fragile if it contains
52/// `MaybeUninit` or `union` data. It also does not perform erasure of types like `Box` or `Vec`.
53pub fn optimization_barrier<T: ?Sized>(val: &T) {
54    #[cfg(all(
55        not(miri),
56        any(
57            target_arch = "aarch64",
58            target_arch = "arm",
59            target_arch = "arm64ec",
60            target_arch = "loongarch64",
61            target_arch = "riscv32",
62            target_arch = "riscv64",
63            target_arch = "s390x",
64            target_arch = "x86",
65            target_arch = "x86_64",
66        )
67    ))]
68    unsafe {
69        core::arch::asm!(
70            "# {}",
71            in(reg) core::ptr::from_ref::<T>(val).cast::<()>(),
72            options(readonly, preserves_flags, nostack),
73        );
74    }
75    #[cfg(not(all(
76        not(miri),
77        any(
78            target_arch = "aarch64",
79            target_arch = "arm",
80            target_arch = "arm64ec",
81            target_arch = "loongarch64",
82            target_arch = "riscv32",
83            target_arch = "riscv64",
84            target_arch = "s390x",
85            target_arch = "x86",
86            target_arch = "x86_64",
87        )
88    )))]
89    {
90        /// Custom version of `core::hint::black_box` implemented using
91        /// `#[inline(never)]` and `read_volatile`.
92        #[inline(never)]
93        fn custom_black_box(p: *const u8) {
94            let _ = unsafe { core::ptr::read_volatile(p) };
95        }
96
97        core::hint::black_box(val);
98        if size_of_val(val) > 0 {
99            custom_black_box(core::ptr::from_ref(val).cast::<u8>());
100        }
101    }
102}