Skip to content

Instantly share code, notes, and snippets.

@thomcc
Created August 3, 2021 21:55
Show Gist options
  • Save thomcc/cdcd370bcfe3f58404ccc7291f466345 to your computer and use it in GitHub Desktop.
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
//! 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