Windows Kernel Memory Allocators
This document explores the various memory allocation mechanisms available within the Windows kernel. Understanding these allocators is crucial for developing efficient and stable kernel-mode drivers and components.
Overview of Kernel Memory Allocation
The Windows kernel provides several functions and structures for managing memory dynamically. These allocators are designed to handle different scenarios, from small, frequent allocations to large, infrequent ones, while ensuring memory safety and efficiency. Kernel memory is a precious resource, and its management must be handled with care to avoid system instability.
Types of Kernel Allocators
The primary kernel memory allocators are:
- Non-Paged Pool: Memory that is always resident in physical memory and cannot be paged out to disk. This is essential for critical kernel structures that must be accessible at all times, regardless of system paging activity.
- Paged Pool: Memory that can be paged out to disk when system memory pressure is high. This is suitable for less critical data structures that can tolerate potential delays if they need to be reloaded from disk.
Core Allocation Functions
The following functions are the fundamental building blocks for kernel memory allocation:
ExAllocatePoolWithTag
This is the most commonly used function for allocating memory from the non-paged or paged pool. It takes the desired pool type, the size of the allocation, and a four-character tag that can be used for debugging and memory analysis.
PVOID ExAllocatePoolWithTag(
POOL_TYPE PoolType,
SIZE_T NumberOfBytes,
ULONG Tag
);
Parameters:
PoolType: Specifies whether to allocate from Non-Paged Pool (`NonPagedPool`, `NonPagedPoolNx`) or Paged Pool (`PagedPool`, `PagedPoolSession`). The `Nx` variant indicates non-executable memory.NumberOfBytes: The number of bytes to allocate.Tag: A four-character string (e.g., 'MyT') used to identify the allocation. This is invaluable for debugging memory leaks.
Return Value: A pointer to the allocated memory, or NULL if the allocation fails.
ExFreePoolWithTag
This function frees memory previously allocated by ExAllocatePoolWithTag. It's crucial to free all allocated memory to prevent memory leaks.
VOID ExFreePoolWithTag(
PVOID P,
ULONG Tag
);
Parameters:
P: A pointer to the memory block to free.Tag: The same four-character tag used during allocation.
ExAllocatePoolWithTag or to free the same block of memory multiple times.
Specialized Allocators
For specific scenarios, the kernel offers more specialized allocation routines:
System Information Buffer Allocations
Functions like ZwQuerySystemInformation often require the caller to allocate a buffer of an appropriate size. The system provides standard structures and conventions for this.
Memory Descriptors (MDL)
Memory Descriptor Lists (MDLs) are used to describe physical memory pages. They are often used by I/O drivers to map buffers for DMA operations. Functions like IoAllocateMdl and MmBuildMdlForNonCached are relevant here.
Best Practices for Kernel Memory Allocation
- Always pair allocations with deallocations.
- Use descriptive tags for easy debugging.
- Allocate from Non-Paged Pool only when absolutely necessary. Paged Pool is generally preferred to conserve physical memory.
- Be mindful of allocation sizes to avoid fragmentation and excessive memory consumption.
- Handle allocation failures gracefully.