use crate::error::Result;
use crate::policy::Policy;
use core::sync::atomic::{AtomicBool, Ordering};
mod fallback;
#[cfg(unix)]
mod unix;
#[cfg(windows)]
mod windows;
#[allow(dead_code)]
pub fn page_size() -> usize {
platform_impl::page_size()
}
#[allow(dead_code)]
pub fn allocate(size: usize, policy: Policy) -> Result<MemoryRegion> {
allocate_aligned(size, 1, policy)
}
pub fn allocate_aligned(size: usize, alignment: usize, policy: Policy) -> Result<MemoryRegion> {
platform_impl::allocate_aligned(size, alignment, policy)
}
pub struct MemoryRegion {
ptr: *mut u8,
len: usize,
alloc_ptr: *mut u8,
alloc_len: usize,
alloc_align: usize,
is_locked: bool,
#[allow(dead_code)]
has_guard_pages: bool,
is_protected: AtomicBool,
}
unsafe impl Send for MemoryRegion {}
unsafe impl Sync for MemoryRegion {}
impl MemoryRegion {
#[inline]
#[allow(dead_code)]
pub fn as_ptr(&self) -> *const u8 {
self.ptr
}
#[inline]
#[allow(dead_code)]
pub fn as_mut_ptr(&mut self) -> *mut u8 {
self.ptr
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
#[allow(dead_code)]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub unsafe fn as_slice(&self) -> &[u8] {
core::slice::from_raw_parts(self.ptr, self.len)
}
#[inline]
pub unsafe fn as_mut_slice(&mut self) -> &mut [u8] {
core::slice::from_raw_parts_mut(self.ptr, self.len)
}
#[inline]
pub fn is_protected(&self) -> bool {
self.is_protected.load(Ordering::Acquire)
}
#[allow(dead_code)]
pub fn make_readable(&self) -> Result<()> {
if !self.is_protected.load(Ordering::Acquire) {
return Ok(());
}
platform_impl::protect(self.ptr, self.len, Protection::Read)?;
self.is_protected.store(false, Ordering::Release);
Ok(())
}
pub fn make_writable(&self) -> Result<()> {
if !self.is_protected.load(Ordering::Acquire) {
return Ok(());
}
platform_impl::protect(self.ptr, self.len, Protection::ReadWrite)?;
self.is_protected.store(false, Ordering::Release);
Ok(())
}
pub fn make_inaccessible(&self) -> Result<()> {
if self.is_protected.load(Ordering::Acquire) {
return Ok(());
}
platform_impl::protect(self.ptr, self.len, Protection::None)?;
self.is_protected.store(true, Ordering::Release);
Ok(())
}
fn zeroize(&self) {
if self.len == 0 {
return;
}
let was_protected = self.is_protected.load(Ordering::Acquire);
if was_protected {
if let Err(_e) = self.make_writable() {
#[cfg(debug_assertions)]
eprintln!("shrouded: WARNING - failed to unprotect memory for zeroization");
return;
}
}
unsafe {
let slice = core::slice::from_raw_parts_mut(self.ptr, self.len);
for byte in slice.iter_mut() {
core::ptr::write_volatile(byte, 0);
}
}
core::sync::atomic::compiler_fence(Ordering::SeqCst);
if was_protected {
let _ = self.make_inaccessible();
}
}
}
impl Drop for MemoryRegion {
fn drop(&mut self) {
self.zeroize();
if self.is_locked {
let _ = platform_impl::unlock(self.alloc_ptr, self.alloc_len);
}
let _ = platform_impl::deallocate(self.alloc_ptr, self.alloc_len, self.alloc_align);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(dead_code)]
pub enum Protection {
None,
Read,
ReadWrite,
}
#[cfg(unix)]
use unix as platform_impl;
#[cfg(windows)]
use windows as platform_impl;
#[cfg(not(any(unix, windows)))]
use fallback as platform_impl;