flex_alloc_secure/
stack.rs1use core::any::type_name;
4use core::fmt;
5use core::mem::MaybeUninit;
6use core::ptr;
7use core::slice;
8
9use const_default::ConstDefault;
10use rand_core::RngCore;
11use zeroize::{DefaultIsZeroes, Zeroize};
12
13use crate::{
14 alloc::{lock_pages, unlock_pages, UNINIT_ALLOC_BYTE},
15 bytes::FillBytes,
16 protect::SecureRef,
17};
18
19#[repr(align(4096))]
21#[cfg_attr(all(target_arch = "aarch64", target_os = "macos"), repr(align(16384)))]
22pub struct Secured<T: Copy>(MaybeUninit<T>);
23
24impl<T: Copy> Secured<T> {
25 pub fn borrow_default<F, R>(&mut self, f: F) -> R
28 where
29 F: FnOnce(SecureRef<&mut T>) -> R,
30 T: Default,
31 {
32 let lock = SecuredGuard::new(&mut self.0);
33 lock.0.write(T::default());
34 unsafe { lock.eval_inited(f) }
35 }
36
37 pub fn borrow_random<F, R>(&mut self, rng: impl RngCore, f: F) -> R
41 where
42 F: FnOnce(SecureRef<&mut T>) -> R,
43 T: FillBytes,
44 {
45 let mut lock = SecuredGuard::new(&mut self.0);
46 lock.fill_random(rng);
47 unsafe { lock.eval_inited(f) }
48 }
49
50 pub fn borrow_take<F, R>(&mut self, take: &mut T, f: F) -> R
53 where
54 F: FnOnce(SecureRef<&mut T>) -> R,
55 T: DefaultIsZeroes,
56 {
57 let lock = SecuredGuard::new(&mut self.0);
58 lock.0.write(*take);
59 take.zeroize();
60 unsafe { lock.eval_inited(f) }
61 }
62
63 pub fn borrow_uninit<F, R>(&mut self, f: F) -> R
66 where
67 F: FnOnce(SecureRef<&mut MaybeUninit<T>>) -> R,
68 {
69 let mut lock = SecuredGuard::new(&mut self.0);
70 lock.fill_bytes(UNINIT_ALLOC_BYTE);
71 f(SecureRef::new_mut(lock.0))
72 }
73
74 pub fn default<F, R>(f: F) -> R
77 where
78 F: FnOnce(SecureRef<&mut T>) -> R,
79 T: Default,
80 {
81 let mut slf = Secured::DEFAULT;
82 slf.borrow_default(f)
83 }
84
85 pub fn random<F, R>(rng: impl RngCore, f: F) -> R
88 where
89 F: FnOnce(SecureRef<&mut T>) -> R,
90 T: FillBytes,
91 {
92 let mut slf = Secured::DEFAULT;
93 slf.borrow_random(rng, f)
94 }
95
96 pub fn take<F, R>(take: &mut T, f: F) -> R
100 where
101 F: FnOnce(SecureRef<&mut T>) -> R,
102 T: DefaultIsZeroes,
103 {
104 let mut slf = Secured::DEFAULT;
105 slf.borrow_take(take, f)
106 }
107
108 pub fn uninit<F, R>(f: F) -> R
111 where
112 F: FnOnce(SecureRef<&mut MaybeUninit<T>>) -> R,
113 {
114 let mut slf = Secured::DEFAULT;
115 slf.borrow_uninit(f)
116 }
117}
118
119impl<const N: usize> Secured<[u8; N]> {
120 pub fn borrow_bytes<F, R>(&mut self, f: F) -> R
124 where
125 F: FnOnce(SecureRef<&mut [u8; N]>) -> R,
126 {
127 let mut lock = SecuredGuard::new(&mut self.0);
128 lock.fill_bytes(UNINIT_ALLOC_BYTE);
129 unsafe { lock.eval_inited(f) }
130 }
131
132 pub fn bytes<F, R>(f: F) -> R
136 where
137 F: FnOnce(SecureRef<&mut [u8; N]>) -> R,
138 {
139 let mut slf = Secured::DEFAULT;
140 slf.borrow_bytes(f)
141 }
142}
143
144impl<T: Copy> fmt::Debug for Secured<T> {
145 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146 f.write_fmt(format_args!("Secured<{}>", type_name::<T>()))
147 }
148}
149
150impl<T: Copy> ConstDefault for Secured<T> {
151 const DEFAULT: Self = Secured(MaybeUninit::<T>::uninit());
152}
153
154impl<T: Copy> Default for Secured<T> {
155 fn default() -> Self {
156 Self::DEFAULT
157 }
158}
159
160struct SecuredGuard<'a, T>(&'a mut MaybeUninit<T>);
161
162impl<'a, T> SecuredGuard<'a, T> {
163 pub fn new(data: &'a mut MaybeUninit<T>) -> Self {
164 lock_pages(data.as_mut_ptr().cast(), size_of::<T>()).expect("Error locking stack memory");
165 Self(data)
166 }
167
168 #[inline]
169 pub unsafe fn eval_inited<R>(self, f: impl FnOnce(SecureRef<&mut T>) -> R) -> R {
171 struct Dropper<'d, D>(&'d mut D);
172
173 impl<D> Drop for Dropper<'_, D> {
174 fn drop(&mut self) {
175 unsafe {
176 ptr::drop_in_place(self.0);
177 }
178 }
179 }
180
181 let drop = Dropper(self.0.assume_init_mut());
182 f(SecureRef::new_mut(drop.0))
183 }
184}
185
186unsafe impl<T> FillBytes for SecuredGuard<'_, T> {
187 fn as_bytes_mut(&mut self) -> &mut [u8] {
188 let len: usize = size_of_val(self.0);
189 unsafe { slice::from_raw_parts_mut(self.0 as *mut MaybeUninit<T> as *mut u8, len) }
190 }
191}
192
193impl<T> Drop for SecuredGuard<'_, T> {
194 fn drop(&mut self) {
195 self.0.zeroize();
196 match unlock_pages(self.0.as_mut_ptr().cast(), size_of::<T>()) {
197 Ok(_) => (),
198 Err(_) => {
199 if !std::thread::panicking() {
200 panic!("Error unlocking memory");
201 }
202 }
203 };
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use const_default::ConstDefault;
210 use rand_core::OsRng;
211
212 use super::Secured;
213 use crate::alloc::UNINIT_ALLOC_BYTE;
214
215 #[test]
216 fn secured_default() {
217 let mut sec = Secured::<usize>::DEFAULT;
218 #[cfg_attr(miri, allow(unused))]
219 let ptr = sec.borrow_default(|mut b| {
220 assert_eq!(&*b, &0);
221 *b = 99usize;
222 &*b as *const usize
223 });
224 #[cfg(not(miri))]
226 assert_eq!(unsafe { *ptr }, 0usize);
227
228 Secured::<[u8; 10]>::default(|r| {
229 assert_eq!(&*r, &[0; 10]);
230 });
231 }
232
233 #[test]
234 fn secured_random() {
235 Secured::<[u8; 10]>::random(OsRng, |r| {
238 assert_ne!(&*r, &[0u8; 10]);
239 });
240
241 let mut sec = Secured::<[u8; 20]>::DEFAULT;
242 sec.borrow_random(OsRng, |r| {
243 assert_ne!(&*r, &[0u8; 20]);
244 });
245 }
246
247 #[test]
248 fn secured_take() {
249 let mut value = 99usize;
250
251 Secured::take(&mut value, |v| {
252 assert_eq!(&*v, &99);
253 });
254 assert_eq!(value, 0);
256 }
257
258 #[test]
259 fn secured_uninit() {
260 Secured::<[u8; 10]>::bytes(|r| {
261 assert_eq!(&*r, &[UNINIT_ALLOC_BYTE; 10]);
262 });
263 Secured::<u32>::uninit(|m| {
264 let val = unsafe { m.assume_init() };
265 assert_eq!(val.to_ne_bytes(), [UNINIT_ALLOC_BYTE; 4]);
266 });
267 }
268}