use crate::bump::BumpAlloc;
use std::sync::Arc;
pub struct WitnessArena {
inner: Arc<BumpAlloc>,
}
impl WitnessArena {
#[inline]
pub fn new(inner: Arc<BumpAlloc>) -> Self {
Self { inner }
}
#[inline]
pub fn alloc(&self, size: usize, align: usize) -> *mut u8 {
debug_assert!(size > 0);
debug_assert!(align > 0);
let was_recycled = self.inner.is_recycled();
let ptr = self.inner.alloc(size, align);
if !ptr.is_null() && was_recycled {
unsafe {
volatile_zero(ptr, size);
}
}
ptr
}
#[inline]
pub fn alloc_zeroed(&self, size: usize, align: usize) -> *mut u8 {
debug_assert!(size > 0);
debug_assert!(align > 0);
let ptr = self.inner.alloc(size, align);
if !ptr.is_null() {
unsafe {
volatile_zero(ptr, size);
}
}
ptr
}
#[inline]
pub unsafe fn secure_wipe(&self) {
self.inner.secure_reset();
}
#[inline]
pub fn remaining(&self) -> usize {
self.inner.remaining()
}
#[inline]
pub fn used(&self) -> usize {
self.inner.used()
}
#[inline]
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
#[inline]
pub fn is_recycled(&self) -> bool {
self.inner.is_recycled()
}
}
#[inline(never)]
unsafe fn volatile_zero(ptr: *mut u8, len: usize) {
let word_size = std::mem::size_of::<usize>();
let full_words = len / word_size;
let remainder = len % word_size;
let ptr_usize = ptr as *mut usize;
for i in 0..full_words {
std::ptr::write_volatile(ptr_usize.add(i), 0usize);
}
let tail = ptr.add(full_words * word_size);
for i in 0..remainder {
std::ptr::write_volatile(tail.add(i), 0u8);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::arena::ArenaManager;
#[test]
fn test_fresh_memory_not_double_zeroed() {
let manager = ArenaManager::with_sizes(1024 * 1024, 1024 * 1024, 1024 * 1024).unwrap();
let witness = WitnessArena::new(manager.witness());
assert!(!witness.is_recycled());
let ptr = witness.alloc(1024, 8);
assert!(!ptr.is_null());
unsafe {
for i in 0..1024 {
assert_eq!(*ptr.add(i), 0);
}
}
}
#[test]
fn test_recycled_memory_is_zeroed() {
let manager = ArenaManager::with_sizes(1024 * 1024, 1024 * 1024, 1024 * 1024).unwrap();
let witness = WitnessArena::new(manager.witness());
let ptr = witness.alloc(1024, 8);
unsafe {
std::ptr::write_bytes(ptr, 0xFF, 1024);
}
unsafe { witness.secure_wipe() };
assert!(witness.is_recycled());
let ptr2 = witness.alloc(1024, 8);
assert!(!ptr2.is_null());
unsafe {
for i in 0..1024 {
assert_eq!(*ptr2.add(i), 0);
}
}
}
#[test]
fn test_alloc_zeroed_always_zeroes() {
let manager = ArenaManager::with_sizes(1024 * 1024, 1024 * 1024, 1024 * 1024).unwrap();
let witness = WitnessArena::new(manager.witness());
let ptr = witness.alloc_zeroed(1024, 8);
assert!(!ptr.is_null());
unsafe {
for i in 0..1024 {
assert_eq!(*ptr.add(i), 0);
}
}
}
}