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 MmNonPagedPoolStart and MmNonPagedPoolEnd. This region mapped memory that was guaranteed to reside in physical RAM at all times.
  • Paged Pool: Defined by boundaries like MmPagedPoolStart and MmPagedPoolEnd. 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:

  • ExpPagedPoolDescriptor tracked paged allocations, maintaining its own set of free lists and lookaside lists.
  • ExpNonPagedPoolDescriptor tracked 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:

  1. 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.
  2. 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 at APC_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_LEVEL or 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).