Skip to content

Commit 3a9a3ac

Browse files
authored
zeroize: replace atomic_fence with optimization_barrier (#1252)
1 parent eab521d commit 3a9a3ac

File tree

4 files changed

+105
-20
lines changed

4 files changed

+105
-20
lines changed

zeroize/src/aarch64.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! [`Zeroize`] impls for ARM64 SIMD registers.
22
3-
use crate::{Zeroize, atomic_fence, volatile_write};
3+
use crate::{Zeroize, optimization_barrier, volatile_write};
44

55
use core::arch::aarch64::*;
66

@@ -11,7 +11,7 @@ macro_rules! impl_zeroize_for_simd_register {
1111
#[inline]
1212
fn zeroize(&mut self) {
1313
volatile_write(self, unsafe { core::mem::zeroed() });
14-
atomic_fence();
14+
optimization_barrier(self);
1515
}
1616
}
1717
)+

zeroize/src/barrier.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
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+
/// ```ignore
15+
/// use core::num::NonZeroU32;
16+
/// use zeroize::{ZeroizeOnDrop, zeroize_flat_type};
17+
///
18+
/// struct DataToZeroize {
19+
/// buf: [u8; 32],
20+
/// pos: NonZeroU32,
21+
/// }
22+
///
23+
/// struct SomeMoreFlatData(u64);
24+
///
25+
/// impl Drop for DataToZeroize {
26+
/// fn drop(&mut self) {
27+
/// self.buf = [0u8; 32];
28+
/// self.pos = NonZeroU32::new(32).unwrap();
29+
/// zeroize::optimization_barrier(self);
30+
/// }
31+
/// }
32+
///
33+
/// impl zeroize::ZeroizeOnDrop for DataToZeroize {}
34+
///
35+
/// let mut data = DataToZeroize {
36+
/// buf: [3u8; 32],
37+
/// pos: NonZeroU32::new(32).unwrap(),
38+
/// };
39+
///
40+
/// // data gets zeroized when dropped
41+
/// ```
42+
pub(crate) fn optimization_barrier<T: ?Sized>(val: &T) {
43+
#[cfg(all(
44+
not(miri),
45+
any(
46+
target_arch = "aarch64",
47+
target_arch = "arm",
48+
target_arch = "arm64ec",
49+
target_arch = "loongarch64",
50+
target_arch = "riscv32",
51+
target_arch = "riscv64",
52+
target_arch = "s390x",
53+
target_arch = "x86",
54+
target_arch = "x86_64",
55+
)
56+
))]
57+
unsafe {
58+
core::arch::asm!(
59+
"# {}",
60+
in(reg) val as *const T as *const (),
61+
options(readonly, preserves_flags, nostack),
62+
);
63+
}
64+
#[cfg(not(all(
65+
not(miri),
66+
any(
67+
target_arch = "aarch64",
68+
target_arch = "arm",
69+
target_arch = "arm64ec",
70+
target_arch = "loongarch64",
71+
target_arch = "riscv32",
72+
target_arch = "riscv64",
73+
target_arch = "s390x",
74+
target_arch = "x86",
75+
target_arch = "x86_64",
76+
)
77+
)))]
78+
{
79+
/// Custom version of `core::hint::black_box` implemented using
80+
/// `#[inline(never)]` and `read_volatile`.
81+
#[inline(never)]
82+
fn custom_black_box(p: *const u8) {
83+
let _ = unsafe { core::ptr::read_volatile(p) };
84+
}
85+
86+
core::hint::black_box(val);
87+
if size_of_val(val) > 0 {
88+
custom_black_box(val as *const T as *const u8);
89+
}
90+
}
91+
}

zeroize/src/lib.rs

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,9 @@ mod aarch64;
250250
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
251251
mod x86;
252252

253+
mod barrier;
254+
use barrier::optimization_barrier;
255+
253256
use core::{
254257
marker::{PhantomData, PhantomPinned},
255258
mem::{MaybeUninit, size_of},
@@ -259,7 +262,6 @@ use core::{
259262
},
260263
ops, ptr,
261264
slice::IterMut,
262-
sync::atomic,
263265
};
264266

265267
#[cfg(feature = "alloc")]
@@ -300,7 +302,7 @@ where
300302
{
301303
fn zeroize(&mut self) {
302304
volatile_write(self, Z::default());
303-
atomic_fence();
305+
optimization_barrier(self);
304306
}
305307
}
306308

@@ -334,7 +336,7 @@ macro_rules! impl_zeroize_for_non_zero {
334336
None => unreachable!(),
335337
};
336338
volatile_write(self, ONE);
337-
atomic_fence();
339+
optimization_barrier(self);
338340
}
339341
}
340342
)+
@@ -425,7 +427,7 @@ where
425427
// done so by take().
426428
unsafe { ptr::write_volatile(self, None) }
427429

428-
atomic_fence();
430+
optimization_barrier(self);
429431
}
430432
}
431433

@@ -441,7 +443,7 @@ impl<Z> Zeroize for MaybeUninit<Z> {
441443
// Safety:
442444
// `MaybeUninit` is valid for any byte pattern, including zeros.
443445
unsafe { ptr::write_volatile(self, MaybeUninit::zeroed()) }
444-
atomic_fence();
446+
optimization_barrier(self);
445447
}
446448
}
447449

@@ -467,7 +469,7 @@ impl<Z> Zeroize for [MaybeUninit<Z>] {
467469
// and 0 is a valid value for `MaybeUninit<Z>`
468470
// The memory of the slice should not wrap around the address space.
469471
unsafe { volatile_set(ptr, MaybeUninit::zeroed(), size) }
470-
atomic_fence();
472+
optimization_barrier(self);
471473
}
472474
}
473475

@@ -493,7 +495,7 @@ where
493495
// `self.len()` is also not larger than an `isize`, because of the assertion above.
494496
// The memory of the slice should not wrap around the address space.
495497
unsafe { volatile_set(self.as_mut_ptr(), Z::default(), self.len()) };
496-
atomic_fence();
498+
optimization_barrier(self);
497499
}
498500
}
499501

@@ -753,14 +755,6 @@ where
753755
}
754756
}
755757

756-
/// Use fences to prevent accesses from being reordered before this
757-
/// point, which should hopefully help ensure that all accessors
758-
/// see zeroes after this point.
759-
#[inline(always)]
760-
fn atomic_fence() {
761-
atomic::compiler_fence(atomic::Ordering::SeqCst);
762-
}
763-
764758
/// Perform a volatile write to the destination
765759
#[inline(always)]
766760
fn volatile_write<T: Copy + Sized>(dst: &mut T, src: T) {
@@ -851,7 +845,7 @@ pub unsafe fn zeroize_flat_type<F: Sized>(data: *mut F) {
851845
unsafe {
852846
volatile_set(data as *mut u8, 0, size);
853847
}
854-
atomic_fence()
848+
optimization_barrier(&data);
855849
}
856850

857851
/// Internal module used as support for `AssertZeroizeOnDrop`.

zeroize/src/x86.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! [`Zeroize`] impls for x86 SIMD registers
22
3-
use crate::{Zeroize, atomic_fence, volatile_write};
3+
use crate::{Zeroize, optimization_barrier, volatile_write};
44

55
#[cfg(target_arch = "x86")]
66
use core::arch::x86::*;
@@ -15,7 +15,7 @@ macro_rules! impl_zeroize_for_simd_register {
1515
#[inline]
1616
fn zeroize(&mut self) {
1717
volatile_write(self, unsafe { core::mem::zeroed() });
18-
atomic_fence();
18+
optimization_barrier(self);
1919
}
2020
}
2121
)*

0 commit comments

Comments
 (0)