Skip to content

Commit b02d547

Browse files
committed
added std feature to enforce panic/unwind safety; moved inline(never) comment to debugging section; make no-panic feature for no branch of psm macro? handle closure panic with match?
1 parent e791a4b commit b02d547

3 files changed

Lines changed: 72 additions & 11 deletions

File tree

zeroize_stack/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ psm = "0.1.26"
2020
zeroize = { version = "1.0" }
2121

2222
[features]
23+
default = ["std"]
24+
std = []
2325

2426
[package.metadata.docs.rs]
2527
all-features = true

zeroize_stack/src/lib.rs

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
#![no_std]
2+
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
3+
#![doc(
4+
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
5+
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
6+
)]
7+
#![warn(missing_docs, unused_qualifications)]
8+
19
//! # zeroize_stack
210
//!
311
//! A crate for sanitizing stack memory after sensitive operations—sometimes referred to as _Stack Bleaching_.
@@ -31,8 +39,16 @@
3139
//! caller must ensure:
3240
//! - The stack size provided is large enough for the closure to run with.
3341
//! - The closure does not unwind or return control flow by any means other than
34-
//! directly returning.
42+
//! directly returning. `std` users do not need to worry about this due to
43+
//! the existence of `catch_unwind`.
3544
//!
45+
//! ## `nostd` Support
46+
//!
47+
//! This crate is compatible with `nostd` environments, but it is less safe
48+
//! in the event that your stack-switched stack panics. Panicking on a separate
49+
//! stack can cause undefined behavior (UB), but if it can be caught with
50+
//! `std::panic::catch_unwind`, that aspect of the safety should be more safe.
51+
//!
3652
//! ## Use Cases
3753
//!
3854
//! - Cryptographic routines
@@ -45,6 +61,27 @@ extern crate alloc;
4561

4662
use alloc::{vec, vec::Vec};
4763

64+
#[cfg(feature = "std")]
65+
extern crate std;
66+
#[cfg(feature = "std")]
67+
use core::any::Any;
68+
#[cfg(feature = "std")]
69+
use std::{
70+
boxed::Box,
71+
panic::catch_unwind,
72+
};
73+
#[cfg(feature = "std")]
74+
type StackSwitchResult<T> = Result<T, Box<dyn Any + Send>>;
75+
#[cfg(not(feature = "std"))]
76+
type StackSwitchResult<T> = T;
77+
78+
use core::panic::{AssertUnwindSafe, UnwindSafe};
79+
80+
#[derive(Debug)]
81+
enum Error {
82+
StackPanicked
83+
}
84+
4885
psm::psm_stack_manipulation! {
4986
yes {
5087
/// Executes a function/closure and clears the function's stack by using
@@ -60,34 +97,56 @@ psm::psm_stack_manipulation! {
6097
/// some architectures might consume more memory in the stack, such as SPARC.
6198
/// * `crypto_fn` - the code to run while on the separate stack.
6299
///
100+
/// ## Panicking
101+
///
102+
/// This function panics when `psm` detects that `on_stack` is unavailable.
103+
///
104+
/// ## Errors
105+
///
106+
/// With the `std` feature enabled, this function will result in an error when
107+
/// the closure panics. You may want to log these errors securely, privately,
108+
/// as cryptography panics could be a little revealing if displayed to
109+
/// the end user.
110+
///
111+
/// ## Debugging
112+
///
113+
/// Using `#[inline(never)]` on the closure's function definition could
114+
/// make it easier to debug as the function should show up.
115+
///
63116
/// # Safety
64-
///
65-
/// * `crypto_fn` should be marked as `#[inline(never)]`, preventing register
66-
/// reuse and stack layout changes.
117+
///
67118
/// * The stack needs to be large enough for `crypto_fn()` to execute without
68119
/// overflow.
69-
/// * `crypto_fn()` must not unwind or return control flow by any other means
120+
/// * `nostd` only: `crypto_fn()` must not unwind or return control flow by any other means
70121
/// than by directly returning.
71-
pub unsafe fn exec_on_sanitized_stack<F, R>(stack_size_kb: isize, crypto_fn: F) -> R
122+
pub unsafe fn exec_on_sanitized_stack<F, R>(stack_size_kb: isize, crypto_fn: F) -> StackSwitchResult<R>
72123
where
73-
F: FnOnce() -> R,
124+
F: FnOnce() -> R + UnwindSafe,
74125
{
75126
assert!(
76127
stack_size_kb * 1024 > 0,
77128
"Stack size must be greater than 0 kb and `* 1024` must not overflow `isize`"
78129
);
79-
let mut stack = create_aligned_vec(stack_size_kb as usize, core::mem::align_of::<u128>());
130+
let mut stack = create_aligned_vec(stack_size_kb as usize, align_of::<u128>());
131+
80132
let res = unsafe {
81133
psm::on_stack(stack.as_mut_ptr(), stack.len(), || {
82-
crypto_fn()
134+
#[cfg(not(feature = "std"))]
135+
{
136+
crypto_fn()
137+
}
138+
#[cfg(feature = "std")]
139+
{
140+
catch_unwind(AssertUnwindSafe(crypto_fn))
141+
}
83142
})
84143
};
85144
stack.zeroize();
86145
res
87146
}
88147
}
89148
no {
90-
pub unsafe fn exec_on_sanitized_stack<F, R>(_stack_size_kb: isize, _crypto_fn: F) -> R
149+
pub unsafe fn exec_on_sanitized_stack<F, R>(_stack_size_kb: isize, _crypto_fn: F) -> StackSwitchResult
91150
where
92151
F: FnOnce() -> R,
93152
{

zeroize_stack/tests/zeroize_stack.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ mod stack_sanitization_tests {
1313
#[test]
1414
fn stack_sanitization_v2() {
1515
let result = unsafe { exec_on_sanitized_stack(4, || dummy_fn()) };
16-
assert_eq!(result.1, 12345);
16+
assert_eq!(result.unwrap().1, 12345);
1717
// results in segmentation fault, which is somewhat normal... just wanted
1818
// to try it
1919
// assert_eq!(unsafe {*result.0}, 42);

0 commit comments

Comments
 (0)