Skip to content

Commit 9e555db

Browse files
authored
zeroize: add zeroize_stack function (#1331)
Adds a simple `optimization_barrier`-based function for stack zeroization. It requires an explicitly provided size since erasing the whole stack (2-8 MiB) can be expensive and it would be an overkill in most cases. Additionally, detection of remaining stack size (e.g. using `pthread_attr_getstack`) is platform-dependent and not available on all targets.
1 parent 5455147 commit 9e555db

File tree

3 files changed

+59
-2
lines changed

3 files changed

+59
-2
lines changed

zeroize/CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77
## 1.9.0 (unreleased)
88
### Added
99
- `optimization_barrier` function ([#1261])
10+
- `zeroize_stack` function ([#1331])
1011

1112
### Changed
1213
- Edition changed to 2024 and MSRV bumped to 1.85 ([#1149])
1314

1415
[#1149]: https://github.com/RustCrypto/utils/pull/1149
1516
[#1261]: https://github.com/RustCrypto/utils/pull/1261
17+
[#1331]: https://github.com/RustCrypto/utils/pull/1331
1618

1719
## 1.8.2 (2025-09-29)
1820
### Changed
@@ -164,15 +166,15 @@ if you would like to support older Rust versions.
164166
## 1.3.0 (2021-04-19)
165167
### Added
166168
- impl `Zeroize` for `Box<[Z]>`
167-
- Clear residual space within `Option
169+
- Clear residual space within `Option`
168170

169171
### Changed
170172
- Ensure `Option` is `None` when zeroized
171173
- Bump MSRV to 1.47
172174

173175
## 1.2.0 (2020-12-09)
174176
### Added
175-
- `Zeroize` support for x86(_64) SIMD registers
177+
- `Zeroize` support for x86(-64) SIMD registers
176178

177179
### Changed
178180
- Simplify `String::zeroize`

zeroize/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,9 @@ mod x86;
253253
mod barrier;
254254
pub use barrier::optimization_barrier;
255255

256+
mod stack;
257+
pub use stack::zeroize_stack;
258+
256259
use core::{
257260
marker::{PhantomData, PhantomPinned},
258261
mem::{MaybeUninit, size_of},

zeroize/src/stack.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/// Zeroize `N` bytes of stack space.
2+
///
3+
/// Most algorithm implementations use stack to store temporary data.
4+
/// Such temporaries may contain sensitive information (e.g. cryptgraphic keys)
5+
/// and can stay on stack after the computation is finished. If an attacker
6+
/// is able for some reasons to read stack data freely, it may result in
7+
/// leaking of the sensitive data.
8+
///
9+
/// # WARNING
10+
/// This function requires you to estimate how much stack space is used by your
11+
/// sensitive computation. This can be done by tools like [`cargo-call-stack`],
12+
/// but note that stack usage depends on optimization level and compiler flags.
13+
///
14+
/// [`cargo-call-stack`]: https://github.com/japaric/cargo-call-stack
15+
///
16+
/// Additionally, you must annotate your sensitive function with `#[inline(never)]`.
17+
///
18+
/// For example, the following example **DOES NOT** erase stack properly:
19+
/// ```
20+
/// pub fn encrypt_data(key: &[u8; 16], data: &mut [u8]) {
21+
/// leaking_encryption(key, data);
22+
/// zeroize::zeroize_stack::<65_536>();
23+
/// }
24+
/// # fn leaking_encryption(_: &[u8; 16], _: &mut [u8]) {}
25+
/// ```
26+
/// `leaking_encryption` may get inlined and `zeroize_stack` will erase
27+
/// stack memory above the stack frame reserved by `encrypt_data`, i.e.
28+
/// it will **NOT** erase stack memory used by `leaking_encryption`.
29+
///
30+
/// You should wrap your computation in the following way:
31+
/// ```
32+
/// #[inline(never)]
33+
/// fn encrypt_data_inner(key: &[u8; 16], data: &mut [u8]) {
34+
/// leaking_encryption(key, data);
35+
/// }
36+
///
37+
/// pub fn encrypt_data(key: &[u8; 16], data: &mut [u8]) {
38+
/// encrypt_data_inner(key, data);
39+
/// zeroize::zeroize_stack::<65_536>();
40+
/// }
41+
/// # fn leaking_encryption(_: &[u8; 16], _: &mut [u8]) {}
42+
/// ```
43+
/// Finally, note that `#[inline(never)]` is just a hint and may be ignored
44+
/// by the compiler. It works properly in practice, but such stack zeroization
45+
/// should be considered as "best effort" and in cases where it's not enough
46+
/// you should inspect the generated binary to verify that you got a desired
47+
/// codegen.
48+
#[inline(never)]
49+
pub fn zeroize_stack<const N: usize>() {
50+
let buf = [0u8; N];
51+
crate::optimization_barrier(&buf);
52+
}

0 commit comments

Comments
 (0)