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_.
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
4662use 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+
4885psm:: 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 {
0 commit comments