In our previous article, we looked at IRQL issues. Today, we dive into one of the most challenging areas of Windows debugging: Pool Corruption.
When a driver or kernel component writes past the end of its allocated memory buffer, it doesn’t always crash immediately. It might overwrite the “header” of the next memory block. The system only notices the damage later when it tries to allocate or free that next block, leading to a Bug Check 0x19: BAD_POOL_HEADER.
1. Understanding the Windows Kernel Pool
The Kernel Pool is the heap for kernel-mode drivers. It is divided into Pool Chunks. Each chunk starts with a Pool Header that contains metadata:
- Previous Size: Size of the preceding chunk.
- Pool Index: Used for tracking.
- Block Size: Size of the current chunk.
- Pool Tag: A 4-character “signature” (e.g.,
ExAl) identifying who allocated the memory.
If a driver performs an “off-by-one” error or a memcpy with an incorrect length, it overwrites the header of the adjacent chunk. When the Pool Manager later inspects that corrupted header, it triggers the BSOD to prevent further data loss.
2. Real Use Case: The “Ghost” Overwriter
Scenario: A server crashes randomly every few hours with 0x19. The stack trace usually points to nt!ExFreePoolWithTag, which is just the “victim” trying to clean up memory.
Step 1: Analyze the Parameters
In WinDbg, run !analyze -v. Look at the parameters for 0x19:
- Arg1:
0x20(The pool block header is corrupt) - Arg2: The address of the corrupted pool block.
- Arg3/4: Internal tracking data.
Step 2: Inspecting the Neighborhood
We use the !pool command to look at the memory surrounding the crash address:
Plaintext
kd> !pool fffff801`4a220000
fffff801`4a220000 size: 40 previous size: 0 (Free) ....
fffff801`4a220400 size: 60 previous size: 40 (Allocated) Leak
fffff801`4a220460 size: ?? previous size: ?? (Corrupt) Tag?
The debugger tells us the chunk at 0x460 is corrupt. This means the chunk immediately before it (at 0x400) is likely the one that overran its boundary.
Step 3: Identifying the Culprit via Pool Tags
Look at the tag for the chunk at 0x400. Let’s say it is Prot.
To find which driver owns that tag, use the strings command or search your source code:
findstr /s "Prot" *.c
If you don’t have the source, use:
!libpooltag Prot
This identifies the “Protocol” driver as the one that likely wrote too much data into its 0x60 byte allocation, destroying the header of the next block.
3. Advanced Technique: Special Pool
Sometimes the corruption is so subtle that !pool isn’t enough. This is where Driver Verifier and Special Pool come in.
By enabling Special Pool for a specific driver tag, Windows places each allocation on a separate memory page, right against a “guard page.”
- The Result: Instead of corrupting a neighbor and crashing later, the driver will trigger an immediate 0x50 (PAGE_FAULT_IN_NONPAGED_AREA) the very millisecond it tries to write one byte too far.
4. Best Practices for Blog Readers
- Always use Pool Tags: Never use
ExAllocatePool. UseExAllocatePoolWithTag. It’s your primary breadcrumb during a crash. - Validate Buffer Lengths: Before every
RtlCopyMemoryormemcpy, check your destination buffer size against the source length. - Use
!poolval: This WinDbg command can help validate the entire pool structure if you suspect widespread corruption.
Summary of Memory Bug Checks
| Bug Check | Name | Description |
| 0x19 | BAD_POOL_HEADER | A pool header was found to be invalid during a pool operation. |
| 0x50 | PAGE_FAULT_IN_NONPAGED_AREA | Invalid system memory was referenced (often due to bad pointers). |
| 0xC4 | DRIVER_VERIFIER_DETECTED_VIOLATION | Caught by Verifier—this is the “Gold Standard” for debugging. |
| 0xBE | ATTEMPTED_WRITE_TO_READONLY_MEMORY | A driver tried to write to a segment of memory marked as read-only. |
In the next article, we will tackle Deadlocks and Timeouts (0x133)—how to find out which thread is “hogging” the CPU and stalling the entire system.
