Created
August 3, 2021 21:55
-
-
Save thomcc/cdcd370bcfe3f58404ccc7291f466345 to your computer and use it in GitHub Desktop.
alternative (more efficient, but still sound) way to implement `zeroize`'s memory wiping
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! Sketch of a alternative way to implement the backend of `zeroize` (the part | |
//! that does memory zeroing). | |
//! | |
//! It should be: | |
//! | |
//! - More efficient, possibly much more (for byte arrays), as it can zero in | |
//! the most efficient way available, rather than e.g. being forced to | |
//! perform element-at-a-time writes (where "element" often is "byte"). | |
//! - Still guaranteed even in the face of aggressive compiler optimizations | |
//! as the semantics of volatile loads forbid the compiler from being able | |
//! to determine what function is being called. | |
//! | |
//! That said, a better solution is still a volatile fill intrinsic, | |
//! which supposedly is on the way. | |
struct RacyCell<T>(core::cell::UnsafeCell<T>); | |
unsafe impl<T> Sync for RacyCell<T> {} | |
unsafe impl<T> Send for RacyCell<T> {} | |
impl<T> RacyCell<T> { | |
const fn new(v: T) -> Self { Self(core::cell::UnsafeCell::new(v)) } | |
} | |
// Same signature as `bzero`. TODO: support using `libc::bzero` / | |
// `libc::explicit_bzero` behind a feature. | |
type ZeroBytesImpl = unsafe extern "C" fn(*mut core::ffi::c_void, usize); | |
#[used] | |
static ZERO_BYTES_HOLDER: RacyCell<*mut ZeroBytesImpl> = { | |
#[inline(never)] // not needed, but doesn't hurt. | |
unsafe extern "C" fn zero_impl(p: *mut core::ffi::c_void, s: usize) { | |
let slice = core::slice::from_raw_parts_mut(p as *mut u8, s); | |
slice.fill(0); | |
} | |
// Note: This second level of indirection is basically pure paranoia, | |
// but should also defend against most future semantic changes that I can | |
// imagine which could break this. | |
#[used] static ZEROB: RacyCell<ZeroBytesImpl> = RacyCell::new(zero_impl); | |
RacyCell::new(ZEROB.0.get()) | |
}; | |
pub fn zero_bytes(v: &mut [u8]) { | |
unsafe { | |
let zero_bytes_impl: ZeroBytesImpl = ZERO_BYTES_HOLDER.0.get() | |
.read_volatile() | |
.read_volatile(); | |
let len = v.len(); | |
zero_bytes_impl(v.as_mut_ptr().cast(), len); | |
} | |
} | |
#[test] | |
fn smoke() { | |
let mut v = [0xffu8; 100]; | |
zero_bytes(&mut v); | |
assert!(v.iter().all(|b| *b == 0), "{:?}", v); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment