In our previous articles, we covered timing and resource deadlocks. Now, we return to the most frequent “bread and butter” of Windows debugging: The Access Violation.
When a driver tries to read or write to a memory address that doesn’t exist, is protected, or has already been freed, the CPU throws an exception. If the kernel doesn’t have a specific handler for that exception, it triggers Bug Check 0x50: PAGE_FAULT_IN_NONPAGED_AREA or 0x7E: SYSTEM_THREAD_EXCEPTION_NOT_HANDLED.
1. The Architecture of a Memory Fault
Windows uses a virtual memory system. Every address a driver sees is “Virtual.” The Hardware Management Unit (MMU) translates this into a “Physical” address using Page Tables.
A crash happens when:
- NULL Pointer: The driver tries to access address
0x00000000(the most common bug). - Use-After-Free: The driver accesses memory it already released back to the Pool.
- Instruction Corruption: The CPU tries to execute data as if it were code.
2. Real Use Case: The Race Condition Pointer
Scenario: A high-speed imaging driver crashes randomly during device initialization. The BSOD shows 0x50.
Step 1: Analyze the Faulting Address
Run !analyze -v.
- Arg1:
ffffa00012345678(The memory address referenced) - Arg2:
0x00(Read operation) or0x01(Write operation)
Step 2: Context is King
With a 0x7E or 0x50, the stack trace might look “garbage” if the instruction pointer (RIP) itself is corrupted. Use the Trap Frame to restore the registers to the exact state at the crash:
Plaintext
kd> .trap ffff8001`5521a000
Step 3: Disassembling the Failure
Now, look at the exact instruction that failed using u . (unassemble at current IP):
Plaintext
MyCameraDriver!StartCapture+0x7b:
fffff801`4a22047b 488b01 mov rax,qword ptr [rcx]
The Insight: The CPU tried to move data from the address held in the RCX register into RAX.
If we check the registers:
kd> r rcx
rcx=0000000000000020
The Diagnosis: 0x20 is a “near-NULL” pointer. It usually means the driver has a structure pointer that is NULL, and it tried to access a member at offset 0x20.
3. Debugging “Use-After-Free” with PageHeap
Standard memory pools are recycled quickly. If Driver A frees memory and Driver B immediately allocates it, Driver A might still have a “dangling pointer” and overwrite Driver B’s data without a crash.
To catch this, use Verifier /flags 0x1 (Special Pool).
- Windows will place the allocation at the very end of a page.
- When the driver frees it, the page is marked “Invalid” immediately.
- Any subsequent access by the dangling pointer triggers an immediate 0x50, catching the bug at the source rather than miles down the road.
4. Pro-Tips for the Blog
- Initialize Your Pointers: Always set pointers to
NULLafter callingExFreePool. - Check for NULL: Never assume
ExAllocatePoolsucceeded. Alwaysif (Pointer == NULL) return STATUS_INSUFFICIENT_RESOURCES;. - Understand the “Non-Paged” Requirement: If you are at
DISPATCH_LEVEL, your code and data must be in non-paged memory. If the OS has to “page in” your code from the disk while you are at a high IRQL, you get a0x50.
Summary Table: Access & Exception Bug Checks
| Code | Name | Description |
| 0x50 | PAGE_FAULT_IN_NONPAGED_AREA | Accessing invalid memory at an IRQL that forbids page faults. |
| 0x7E | SYSTEM_THREAD_EXCEPTION_NOT_HANDLED | A general “catch-all” for kernel-mode exceptions (like divide-by-zero). |
| 0xD1 | DRIVER_IRQL_NOT_LESS_OR_EQUAL | (Recall Article 1) Specifically a page fault at high IRQL. |
| 0x3B | SYSTEM_SERVICE_EXCEPTION | An exception happened while transitioning from User mode to Kernel mode. |
With this, you have the foundational toolkit for 90% of the BSODs you will encounter in your career. Which of these scenarios have you seen most often in your labs?
