use std::sync::atomic::{AtomicUsize, Ordering};
use std::fmt;
pub mod patterns {
pub const ALLOC: u8 = 0xAA;
pub const FREE: u8 = 0xDD;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MkHazard {
Leak(usize), UseAfterFree,
DoubleFree,
Overflow,
Unaligned,
}
impl fmt::Display for MkHazard {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MkHazard::Leak(bytes) => write!(f, "LEAK: {} bytes remain allocated", bytes),
MkHazard::UseAfterFree => write!(f, "USE-AFTER-FREE: Accessing implementation-freed memory"),
MkHazard::DoubleFree => write!(f, "DOUBLE-FREE: Attempting to free already freed pointer"),
MkHazard::Overflow => write!(f, "OVERFLOW: Write beyond allocation boundary"),
MkHazard::Unaligned => write!(f, "UNALIGNED: Pointer alignment requirement violated"),
}
}
}
pub struct MkSentinel;
static ACTIVE_ALLOCATIONS: AtomicUsize = AtomicUsize::new(0);
static ACTIVE_BYTES: AtomicUsize = AtomicUsize::new(0);
impl MkSentinel {
#[inline]
pub unsafe fn poison_alloc(ptr: *mut u8, size: usize) {
if cfg!(feature = "sentinel") {
std::ptr::write_bytes(ptr, patterns::ALLOC, size);
ACTIVE_ALLOCATIONS.fetch_add(1, Ordering::Relaxed);
ACTIVE_BYTES.fetch_add(size, Ordering::Relaxed);
}
}
#[inline]
pub unsafe fn poison_free(ptr: *mut u8, size: usize) {
if cfg!(feature = "sentinel") {
std::ptr::write_bytes(ptr, patterns::FREE, size);
ACTIVE_ALLOCATIONS.fetch_sub(1, Ordering::Relaxed);
ACTIVE_BYTES.fetch_sub(size, Ordering::Relaxed);
}
}
pub fn report(hazard: MkHazard, ptr: Option<*const u8>) {
if cfg!(feature = "sentinel") {
let addr = ptr.map(|p| format!("{:p}", p)).unwrap_or_else(|| "N/A".to_string());
eprintln!("\n⚠️ [MEMKIT HAZARD] {} @ {}", hazard, addr);
match hazard {
MkHazard::UseAfterFree => {
eprintln!(" > Hint: Check for dangling references or race conditions.");
eprintln!(" > Memory was poisoned with 0x{:02X}", patterns::FREE);
}
MkHazard::Leak(_) => {
eprintln!(" > Hint: Objects were created but never dropped.");
}
_ => {}
}
}
}
pub fn verify_leaks() {
if cfg!(feature = "sentinel") {
let info_allocs = ACTIVE_ALLOCATIONS.load(Ordering::Relaxed);
let info_bytes = ACTIVE_BYTES.load(Ordering::Relaxed);
if info_allocs > 0 {
Self::report(MkHazard::Leak(info_bytes), None);
eprintln!(" > {} active allocations remaining.", info_allocs);
} else {
eprintln!("\n🛡️ [MEMKIT SENTINEL] Clean shutdown. No leaks detected.");
}
}
}
}