If you've worked with C++ on Windows for any length of time, you've seen
0xC0000005. It's STATUS_ACCESS_VIOLATION the NTSTATUS code
Windows raises when a program attempts to access a memory address it doesn't have
permission to read or write. It is by far the most common exception code in crash dumps.
What the code doesn't tell you is why the access was invalid, and that's where the real diagnostic work begins. An access violation can be a null pointer dereference, a use-after-free, a buffer overflow, a race condition, or a stack corruption. Each has different causes and different fixes. Telling them apart requires reading the crash data carefully rather than just noting the exception code.
Read vs. write: the first distinction
Every access violation has a type: read or write. This is shown in the Exception tab in CrashCatch and in WinDbg's output.
- A read violation means the program tried to read from an address it couldn't access. The most common cause is a null or dangling pointer being dereferenced.
- A write violation means the program tried to write to an address it couldn't access. This often indicates a buffer overflow, writing to freed memory, or writing to a read-only region.
Write violations to addresses in your own heap particularly to addresses that look
suspiciously like valid heap addresses are worth treating as potential heap corruption.
Read violations at 0x0 or small non-zero offsets are almost always null
pointer dereferences.
Reading the target address
The target address (the address that was being accessed when the fault occurred) is the single most useful piece of information for classifying an access violation. Here's how to read it:
Target address: 0x0000000000000000
Classic null pointer dereference. The code attempted to dereference a pointer that was null. Look at the faulting instruction and the frame that owns it you're looking for a pointer that wasn't initialized or wasn't checked before use.
Target address: small non-zero offset (0x8, 0x10, 0x18, 0x20...)
Almost certainly a null pointer dereference through a struct or class. The code is
accessing a member of an object, but the object pointer is null. The offset corresponds
to the byte offset of that member within the struct. For example, a target address of
0x18 on a class with a pointer at offset 0x18 means the
this-pointer was null when accessing that member.
Exception code: 0xC0000005 ACCESS_VIOLATION
Type: read
Fault address: 0x00007FF6A3C28E1
Target address: 0x0000000000000018
→ Accessing member at offset 0x18 on a null pointer.
→ Check the owning object before accessing this member.
Target address: looks like a valid heap address but isn't
This pattern a non-null address in the range of your heap that causes an access violation is the signature of a use-after-free or dangling pointer. The object was valid at some point, but the memory has since been freed and possibly reallocated. Accesses to it may succeed silently (if the memory was reallocated with compatible data) or crash with an access violation (if the page was decommitted).
Target address: debug heap sentinel values
In debug builds compiled with MSVC, the debug heap fills freed memory with known patterns. If your target address contains these values, the pointer came directly from freed memory:
0xDDDDDDDDDDDDDDDD— freed heap memory (debug CRT)0xFEEEFEEEFEEEFEEE— freed heap memory (HeapFree)0xCDCDCDCDCDCDCDCD— uninitialized heap memory (debug CRT)
Seeing these in a target address in a debug build is definitive: you have a dangling pointer. The fix is finding where the object is being freed while still being referenced.
Target address: large, random-looking value
A target address that looks like an arbitrary large number nothing recognizable — usually means a corrupted pointer. The pointer variable itself has been overwritten with garbage, either by a buffer overflow in a nearby variable, a race condition writing to the pointer location, or a stack corruption propagating from a previous function.
Using the register state
The CPU registers captured at crash time can help identify which variable or argument
was the bad pointer. On x64 Windows, function arguments are passed in specific registers:
RCX (first argument), RDX (second), R8 (third),
R9 (fourth). If the crash is in a function entry or early in a function body,
the argument registers may still hold their call-site values.
Look at RAX specifically on x64, RAX is the most commonly
used accumulator and is often the register that holds the pointer being dereferenced.
If RAX is 0x0 and the faulting instruction is
mov rcx, [rax+18h], that's a null pointer at rax. If
RAX is 0xDDDDDDDD, it's a freed pointer.
Stack trace patterns for each cause
The stack frame that owns the fault address is the most important frame it's where execution was at the moment of the crash. But the frames above it tell you how you got there, which is often equally important:
- Null pointer dereference the fault frame is typically deep inside a method on a class that should have been checked before use. Look one or two frames up from the fault for the call site that passed the bad pointer in.
- Use-after-free the fault frame may be in a destructor or in code that runs during cleanup. Look for a pattern where the same object is being accessed from multiple code paths, one of which frees it.
- Buffer overflow the fault frame is often in unrelated code that happens to be adjacent in memory. The actual corruption happened earlier. Look for array writes without bounds checks near the crash timeline in the log.
- Stack corruption the stack itself is damaged. Stack overflow (
0xC00000FD) is a separate exception code, but a corrupted stack can manifest as an access violation if the return address or a stack variable is overwritten. The stack trace may be truncated or nonsensical above a certain frame.
Intel Engine classification
CrashCatch's Intel Engine classifies 0xC0000005 automatically using the
target address, access type, register values, and stack shape. A null pointer at
0x0 with a read violation and a matching register state gets classified
as Null Pointer Dereference with high confidence. A randomized target address
combined with a write violation and suspicious register patterns gets classified as
Memory Corruption. You still need to do the debugging but having the
classification and the evidence list saves you the first ten minutes of every investigation.
Classify your access violations automatically.
Intel Engine runs on every crash dump no API key, no internet required.