Expand description
Windows kernel-mode pool allocator for #![no_std] Rust drivers.
ntalloc wraps the WDK pool APIs (ExAllocatePool2, ExFreePool,
ExFreePoolWithTag) behind the standard GlobalAlloc trait and, with
the allocator-api feature, the Allocator trait, so ordinary
alloc-crate types (Vec, Box, …) can be backed by kernel pool
memory from a #![no_std] driver. It builds on stable Rust 1.85 or
newer; allocator-api needs Rust 1.100, and allocator-api-markers a
nightly compiler.
§Overview
NtAllocis a zero-sized allocator type parameterized over the pool flags, the memory tag, whether deallocation uses the tagged or untagged free routine, and whether it realigns requests the pool cannot align by itself, which it does by default. The pool flags are exported asPOOL_FLAG_*constants, such asPOOL_FLAG_NON_PAGEDandPOOL_FLAG_UNINITIALIZED.PagedAllocandNonPagedAllocare convenience aliases for the paged and non-paged pool, respectively.- Allocations are tagged with a 4-byte ASCII tag (see
DEFAULT_MEMORY_TAG) so they can be attributed back to this allocator with tools such as!poolused.
§Design
ExAllocatePool2 only guarantees an allocation aligned to
MIN_ALIGNMENT (the WDK’s MEMORY_ALLOCATION_ALIGNMENT: two pointers,
i.e. 16 bytes on 64-bit targets), or to the page size for allocations of
at least one page (see NtAlloc::kernel_alignment). Rust Layouts can
request stricter alignment than that, so when a request exceeds what the
pool allocator natively guarantees, NtAlloc::allocate_aligned
over-allocates by layout.align() extra bytes, rounds the returned
pointer up to the requested alignment, and stashes the original
(unaligned) pointer in the pointer-sized slot immediately before the
aligned pointer. There is always room for that slot because
ExAllocatePool2 never returns a base below MIN_ALIGNMENT, so
rounding up moves the pointer by a multiple of MIN_ALIGNMENT — two
pointers — and never by less. NtAlloc::deallocate_aligned reads that
slot back out to recover the pointer that must be passed to
ExFreePool/ExFreePoolWithTag, which is why an over-aligned allocation
has to be released through it rather than through NtAlloc::deallocate.
Realignment is the default. A driver that never needs more than the pool’s
own alignment can turn it off with the REALIGN parameter, as described
under Turning realignment off.
§Example
use ntalloc::NonPagedAlloc;
type Alloc = NonPagedAlloc;
let ptr = Alloc::allocate(64);
assert!(!ptr.is_null());
// SAFETY: `ptr` came from `Alloc::allocate` and needed no realignment.
unsafe { Alloc::deallocate(ptr) };§Features
-
allocator-api— implements theAllocatortrait forNtAlloc, so a collection can use it directly, as inVec::new_inandBox::new_in, not only as the#[global_allocator]. It needs Rust 1.100 or newer, where the trait is stable. -
allocator-api-markers— implements theStaticAllocatorandAllocatorClonemarker traits forNtAlloc.allocrequires them in places:Box::pin_inneedsStaticAllocator, and cloning anArcorRc, or using aBTreeMapat all, needsAllocatorClone. It impliesallocator-api. The marker traits are still unstable (allocator_ext), so it needs a nightly compiler from October 2026 or later. -
usermode-backend— for tests only. Allocates from the process heap (std::alloc::System) instead of the kernel pool, so code that allocates throughNtAlloccan run in tests on a normal machine. Where the pool would bugcheck withBAD_POOL_CALLER, the test process prints what went wrong and aborts instead: on a double free, on a pointer it never handed out, and, withTAGGED_DEALLOC, on the wrong tag. On Windows any abort is reported asSTATUS_STACK_BUFFER_OVERRUN, whatever its cause, and running the tests with-- --test-threads=1shows which test it was. It brings instd, along with thehashbrownandallocator-api2crates, so enable it only under[dev-dependencies]: through a normal dependency it reaches the driver itself, which then fails to build with a duplicatepanic_impllang item, asstd’s panic handler clashes with the driver’s own.NtAlloc::kernel_allocatealways uses the pool, with or without it.[dev-dependencies] ntalloc = { version = "0.1", features = ["usermode-backend"] }
Structs§
- NtAlloc
- A stateless,
#![no_std]-friendly Windows kernel pool allocator.
Constants§
- DEFAULT_
MEMORY_ TAG - Default memory tag for allocations, visible via kernel debugger pool
inspection tools such as
!poolused/!poolfind. - MIN_
ALIGNMENT - The minimum alignment
ExAllocatePool2guarantees for allocations smaller than a page. Mirrors the WDK’sMEMORY_ALLOCATION_ALIGNMENT(two pointers: 16 bytes on 64-bit targets, 8 bytes on 32-bit). - POINTER_
SIZE - Size, in bytes, of a pointer on the target platform.
- POOL_
FLAG_ CACHE_ ALIGNED - Ask for a cache-aligned allocation (
0x8). - POOL_
FLAG_ NON_ PAGED - Allocate from non-paged pool, as non-executable memory (
0x40). - POOL_
FLAG_ PAGED - Allocate from paged pool (
0x100). - POOL_
FLAG_ SPECIAL_ POOL - Allocate from special pool, the pool used for debugging, when it is
available (
0x1_0000_0000). - POOL_
FLAG_ UNINITIALIZED - Leave the allocation uninitialized rather than zeroing it (
0x2). - POOL_
FLAG_ USE_ QUOTA - Charge the allocation to the pool quota of the process the allocating
thread runs in (
0x1).
Type Aliases§
- NonPaged
Alloc NtAllocbacked by the non-paged pool (POOL_FLAG_NON_PAGED).- POOL_
FLAGS - The pool flags passed to
ExAllocatePool2: a combination ofPOOL_FLAG_*values, like thePOOL_FLAGStype inwdm.h. - Paged
Alloc NtAllocbacked by the paged pool (POOL_FLAG_PAGED).