Skip to content

CWE-476: NULL Pointer Dereference

Overview

A null pointer dereference happens when code reads, writes, or calls through a pointer, reference, or object handle that turns out to be null, nil, or None, causing a crash or, in unmanaged languages, undefined behavior. The usual cause is a value that can legitimately be null - a failed lookup, an uninitialized field, an optional return, an unchecked external response - reaching a use site where nobody confirmed it was present.

Relationship to Other CWEs

Record a null dereference against this page. Its MITRE parents - CWE-754 (Improper Check for Unusual or Exceptional Conditions) and CWE-710 (Improper Adherence to Coding Standards) - have no page here and both sit above the level a finding should carry, and CWE-476 has no children to drop to.

The pages around it differ by where the null came from, and by whether the pointer held a null at all:

  • CWE-476 (this page) - a value that can legitimately be absent reaches a dereference because nothing decided what to do about the absence
  • CWE-252 (Unchecked Return Value) - the call that produced the null reported its failure in a return value nobody inspected. MITRE records this page as one of the things CWE-252 can lead to, and the two are usually one defect seen from each end: the crash is at the dereference, the missing check is back at the call
  • CWE-824 (Access of Uninitialized Pointer) - the pointer was never assigned at all, rather than assigned a null. The fix overlaps, the failure mode does not: a null dereference faults predictably, while an uninitialized one holds whatever was left in that slot and may land on live memory without faulting, which is why CWE-824 carries the higher risk rating
  • CWE-362 (Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition')) - MITRE records this page as something a race condition can lead into: the value is non-null when checked and null by the time it is dereferenced. The guard in the secure pattern below is only sound while nothing else can write the value between the check and the use
  • CWE-398 (Indicator of Poor Code Quality) - the MITRE category this weakness is a member of, and PROHIBITED for mapping because categories are not weaknesses. A scanner finding filed as CWE-398 for a null dereference belongs here instead

OWASP Classification

A10:2025 - Mishandling of Exceptional Conditions

Risk

Medium: An unhandled null dereference crashes the thread, request, or process, which is a denial of service. In unmanaged languages it can also be a step toward memory corruption if the null (or near-null) address is combined with an offset and dereferenced as a write. The severity is higher when the affected code path is reachable without authentication, since a single crafted request can then take down a service.

Remediation Steps

Core Principle: Trace every null-capable value back to where it can become null and fix the contract or handle the absence explicitly at every point that can receive it - not just the one call site where the crash was observed.

Trace the Data Path

  • Source: Any lookup, external call, deserialization, optional field, or uninitialized declaration that can legitimately produce a null, nil, or None value
  • Sink: The dereference, member access, method call, or index operation performed on that value without a presence check
  • Missing Controls: No check for absence between the point where the value can become null and the point where it is used, or a check that exists at only one of several call sites that can receive the same null-producing value

Fix the Contract, Not Just the Crash Site (Primary Defense)

  • When a function can legitimately return or hold null, treat that as a contract problem: every caller needs to handle it, not just the one that crashed first
  • Prefer language and type-system features that make nullability explicit and checked at compile time, such as non-nullable reference types, Optional/Option-style wrappers, or mandatory nil checks, over ad hoc runtime guards scattered through the codebase
  • Initialize variables, fields, and collections to a valid state at declaration or construction rather than leaving them null until first use

Handle Absence Explicitly (Defense in Depth)

  • Add the check where the null-capable value is produced, and handle the absence explicitly: an early return, a meaningful error, or a documented safe default. A check that silently swallows the missing value lets execution continue in a partially-initialized state
  • Audit the other call sites of the same null-producing function or field, not only the one that crashed
  • Enable the language or platform's null-safety analysis (nullable reference type checking, Optional-returning API conventions, static analyzer null-dereference rules) to catch regressions before they ship

Test with Absent, Empty, and Present Values

  • Exercise the code path with the value absent, empty, and present, including any external or asynchronous source that can return null unexpectedly
  • Confirm the result is a controlled error or documented fallback, not a crash or a silently corrupted state
  • Re-run static analysis or the security scanner to confirm the finding is resolved

Common Vulnerable Patterns

// VULNERABLE - pseudo-code
user = lookupUser(id)          // can return null if id not found
print(user.name)               // dereferences without checking

// Attack: request an id that does not exist
// Result: unhandled dereference crashes the request/process

Why this is vulnerable: the null is not an error. It is lookupUser reporting truthfully that no such user exists. The defect is that the return type gives that answer the same shape as a real one, so the caller can use it without deciding what to do about it, and nothing at the call site shows that the decision was skipped.

What that costs depends on the runtime, and it is not always a crash. In a managed language it raises an exception, which is an availability problem if an attacker can trigger it on demand with a nonexistent identifier, and an information disclosure if the resulting trace reaches the caller (CWE-248). In an unmanaged one it is undefined behavior: usually a fault, but where the platform permits mapping the zero page, or where the dereference is of a field at a large offset from null, the access can land on memory an attacker arranged.

Adding a check here fixes this line but not the shape of the API. The fix that holds is a signature that cannot be dereferenced without unwrapping, such as an optional or result type, so every caller has to handle absence.

Secure Patterns

// SECURE - pseudo-code
user = lookupUser(id)          // can return null if id not found
if user is null:
    return notFoundResponse()
print(user.name)

Why this works: The absence case is handled explicitly at the point where the null-capable value is produced, before it can reach any operation that assumes presence. Combined with a non-nullable type or Optional-style wrapper enforced by the compiler, this makes the unchecked path a compile-time error rather than a runtime crash.

Common Pitfalls

  • Checking at the first call site only: Adding a null guard where the crash was reported, while other callers of the same function still dereference the value directly - the contract still allows null to leak into unguarded code.
  • Swallowing the null case silently: Catching the null and substituting a default value or empty object without logging or surfacing the condition, which can mask a real upstream bug and produce incorrect behavior instead of a clean, visible failure.
  • Relying on a framework's global exception handler: Letting an unhandled null dereference propagate to a top-level catch-all that returns a generic error - this avoids a crash but leaves the missing check in place, so the same code path remains a denial-of-service target under load.

Additional Resources