use core::alloc::{GlobalAlloc, Layout};
use core::ptr::null_mut;
use crate::NtAlloc;
use crate::bindings::POOL_FLAGS;
struct UsermodeBlock {
layout: Layout,
tag: u32,
}
type UsermodeBlockTable = hashbrown::HashMap<
usize,
UsermodeBlock,
std::hash::BuildHasherDefault<std::hash::DefaultHasher>,
std::alloc::System,
>;
static USERMODE_BLOCKS: std::sync::Mutex<UsermodeBlockTable> = std::sync::Mutex::new(
UsermodeBlockTable::with_hasher_in(std::hash::BuildHasherDefault::new(), std::alloc::System),
);
fn with_usermode_blocks<R>(f: impl FnOnce(&mut UsermodeBlockTable) -> R) -> R {
let mut table = USERMODE_BLOCKS
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
f(&mut table)
}
#[cold]
fn usermode_misuse(message: core::fmt::Arguments<'_>) -> ! {
use std::io::Write as _;
let _ = writeln!(std::io::stderr(), "ntalloc: {message}");
std::process::abort()
}
impl<
const ALLOC_POOL_FLAGS: POOL_FLAGS,
const MEMORY_TAG: u32,
const TAGGED_DEALLOC: bool,
const REALIGN: bool,
> NtAlloc<ALLOC_POOL_FLAGS, MEMORY_TAG, TAGGED_DEALLOC, REALIGN>
{
#[inline]
pub(crate) fn usermode_allocate(size: usize) -> *mut u8 {
let () = Self::CONFIGURATION_SUPPORTED;
if size == 0 {
return null_mut();
}
let Ok(layout) = Layout::from_size_align(size, Self::kernel_alignment(size)) else {
return null_mut();
};
let block = unsafe {
if Self::POOL_ZEROES {
std::alloc::System.alloc_zeroed(layout)
} else {
std::alloc::System.alloc(layout)
}
};
if !block.is_null() {
let record = UsermodeBlock {
layout,
tag: MEMORY_TAG,
};
let previous = with_usermode_blocks(|blocks| blocks.insert(block.addr(), record));
if previous.is_some() {
usermode_misuse(format_args!(
"the heap handed out {block:p} again while it was live: freed outside NtAlloc?"
));
}
}
block
}
pub(crate) fn usermode_free(ptr: *mut u8) {
let block = with_usermode_blocks(|blocks| blocks.remove(&ptr.addr()));
let Some(block) = block else {
usermode_misuse(format_args!(
"freed {ptr:p}, which the usermode backend never allocated or has already freed"
));
};
if TAGGED_DEALLOC && block.tag != MEMORY_TAG {
usermode_misuse(format_args!(
"freed {ptr:p} with tag '{}', but it was allocated with tag '{}'",
MEMORY_TAG.to_ne_bytes().escape_ascii(),
block.tag.to_ne_bytes().escape_ascii(),
));
}
unsafe { std::alloc::System.dealloc(ptr, block.layout) };
}
}