Skip to main content

Crate ntalloc

Crate ntalloc 

Source
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

  • NtAlloc is 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 as POOL_FLAG_* constants, such as POOL_FLAG_NON_PAGED and POOL_FLAG_UNINITIALIZED.
  • PagedAlloc and NonPagedAlloc are 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 the Allocator trait for NtAlloc, so a collection can use it directly, as in Vec::new_in and Box::new_in, not only as the #[global_allocator]. It needs Rust 1.100 or newer, where the trait is stable.

  • allocator-api-markers — implements the StaticAllocator and AllocatorClone marker traits for NtAlloc. alloc requires them in places: Box::pin_in needs StaticAllocator, and cloning an Arc or Rc, or using a BTreeMap at all, needs AllocatorClone. It implies allocator-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 through NtAlloc can run in tests on a normal machine. Where the pool would bugcheck with BAD_POOL_CALLER, the test process prints what went wrong and aborts instead: on a double free, on a pointer it never handed out, and, with TAGGED_DEALLOC, on the wrong tag. On Windows any abort is reported as STATUS_STACK_BUFFER_OVERRUN, whatever its cause, and running the tests with -- --test-threads=1 shows which test it was. It brings in std, along with the hashbrown and allocator-api2 crates, so enable it only under [dev-dependencies]: through a normal dependency it reaches the driver itself, which then fails to build with a duplicate panic_impl lang item, as std’s panic handler clashes with the driver’s own. NtAlloc::kernel_allocate always 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 ExAllocatePool2 guarantees for allocations smaller than a page. Mirrors the WDK’s MEMORY_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§

NonPagedAlloc
NtAlloc backed by the non-paged pool (POOL_FLAG_NON_PAGED).
POOL_FLAGS
The pool flags passed to ExAllocatePool2: a combination of POOL_FLAG_* values, like the POOL_FLAGS type in wdm.h.
PagedAlloc
NtAlloc backed by the paged pool (POOL_FLAG_PAGED).