Windows XP Kernel Paged vs Non-Paged Pool Isolation
In Windows XP, the kernel enforced strict isolation between paged and non-paged memory by partitioning the system’s virtual address space, employing distinct memory descriptors, and maintaining strict hardware-level page table attributes. This architectural separation ensured that critical low-level operating system components could execute time-sensitive tasks without triggering fatal page faults, while less time-sensitive data could be offloaded to secondary storage to preserve physical RAM.
Dedicated Virtual Address Space Partitioning
Windows XP organized the upper portions of its 32-bit virtual address space (typically the upper 2 GB in a standard configuration) into dedicated, non-overlapping regions for different kernel resources. During system initialization, the Memory Manager mapped fixed boundaries for the Non-Paged Pool and the Paged Pool:
- Non-Paged Pool: Defined by system variables such as
MmNonPagedPoolStartandMmNonPagedPoolEnd. This region mapped memory that was guaranteed to reside in physical RAM at all times. - Paged Pool: Defined by boundaries like
MmPagedPoolStartandMmPagedPoolEnd. This distinct range accommodated allocations that could be dynamically written to and read from the paging file (pagefile.sys).
Because these ranges were mapped to separate virtual address blocks, the memory manager prevented address collisions and ensured that pointers assigned to one pool type could never resolve into the address space of the other.
Separate Pool Descriptors and Tracking
The Windows XP Executive layer managed these regions using distinct
POOL_DESCRIPTOR structures. When drivers or kernel
subsystems requested memory via ExAllocatePoolWithTag, they
specified a POOL_TYPE flag (such as PagedPool
or NonPagedPool).
The kernel routed the allocation request to the appropriate pool descriptor:
ExpPagedPoolDescriptortracked paged allocations, maintaining its own set of free lists and lookaside lists.ExpNonPagedPoolDescriptortracked non-paged allocations, operating on independent free lists mapped strictly to physical frames.
By maintaining independent tracking structures, the kernel allocator prevented internal fragmentation from bleeding between the two memory classes and prevented the coalescing of adjacent free blocks that belonged to different pool categories.
Page Table Entries (PTEs) and Hardware Protection
Isolation at the architectural level was enforced through Page Table Entries (PTEs) managed by the Memory Manager:
- Non-Paged Pool PTEs: When the operating system
initialized the non-paged pool, the corresponding PTEs were populated
with valid physical addresses and marked with flags indicating that they
were permanently locked into memory. The working set trimmer and the
modified page writer were barred from touching these entries, ensuring
the CPU's Memory Management Unit (MMU) would never trigger a page fault
exception (
#PF) when resolving these addresses. - Paged Pool PTEs: The PTEs assigned to the paged pool could be transitioned between valid, transition, and invalid (paged-out) states. When memory pressure occurred, the memory manager could invalidate these entries and offload the underlying pages to the pagefile.
Interrupt Request Level (IRQL) Enforcement
The physical and virtual isolation of these pools directly supported the Windows XP Interrupt Request Level (IRQL) model:
- High IRQL Constraints: Code executing at
DISPATCH_LEVEL(IRQL 2) or higher—such as Deferred Procedure Calls (DPCs) and Interrupt Service Routines (ISRs)—cannot process page faults because the page fault handler itself runs atAPC_LEVEL(IRQL 1) and requires the scheduler, which is disabled at higher IRQLs. - Fault Prevention: By isolating critical code, call
stacks, and driver buffers inside the Non-Paged Pool, Windows XP
prevented fatal memory access delays. Attempting to access an address
within the paged pool range while running at
DISPATCH_LEVELor above would trigger a page fault that the processor could not resolve, immediately resulting in a system crash (BugCheck 0x0A: IRQL_NOT_LESS_OR_EQUAL).