A pointer that is NULL when code expects a valid object crashes the process at best and, in privileged code on some platforms, gives an attacker a write to memory they control. MITRE D3FEND names the countermeasure Null Pointer Checking (D3-NPC) and defines it simply: "Checking if a pointer is NULL." It sits under the Harden tactic as a child of Pointer Validation (D3-PV), within Source Code Hardening, and D3FEND lists "Nil Pointer Checking" as a synonym. This card explains where null checks belong, what compilers and languages do to help or hurt, how to verify coverage, and where the countermeasure stops. It is written for developers, product security engineers and code reviewers who already know the material in the C and C++ Secure Coding Guide.
Key takeaways
- It is a coding control, not a product. D3-NPC hardens two artefacts, Memory Free Function and Pointer Dereferencing Function, so it lives in source code, code review and the build, not in a runtime agent.
- The weakness is common and still ranked. CWE-476 NULL Pointer Dereference is number 13 in the 2025 CWE Top 25 (up from 21 in 2024, per MITRE's 2025 list).
- Order matters as much as presence. A check placed after the first dereference is undefined behaviour in C, and GCC may delete it; the SEI CERT rule EXP34-C shows this happening in a Linux kernel driver.
- Type systems do most of the work where they exist. Kotlin null safety, Rust Option and C# nullable reference types move the check to compile time; the remaining risk sits at interop boundaries and in explicit overrides.
- Verify with three tools, not one. Static analysis finds missing checks, sanitizers catch the ones that fire at run time, and fuzzing drives the error paths (allocation failure, missing input) where null pointers appear.
D3-NPC at a glance
| Attribute | Value |
|---|---|
| D3FEND ID | D3-NPC (d3f:NullPointerChecking) |
| Tactic | Harden |
| Parent technique | Pointer Validation (D3-PV), under Source Code Hardening (D3-SCH) |
| Sibling under Pointer Validation | Memory Block Start Validation (D3-MBSV) |
| Digital artefacts | Memory Free Function, Pointer Dereferencing Function (D3FEND relation: hardens) |
| Weakness addressed | CWE-476 NULL Pointer Dereference, which D3FEND models as a weakness of Pointer Dereferencing Function |
| D3FEND references | SEI CERT C EXP34-C, SEI CERT C MEM10-C, CWE-476 |
| Effort | Low per function; ongoing across a codebase, best enforced by compiler and analyser settings |
| Main limitation | Checks only that a pointer is not NULL; a dangling, freed or wild pointer passes the check |
How it works
D3FEND's knowledge base article describes the mechanism as programmatically checking whether a pointer is NULL before use, and adds two considerations: pointers should be checked prior to use after they have, or may have, been modified, and it may vary by circumstance whether the caller or the callee is responsible for the check. In practice that gives three rules.
- Check every value that can be NULL: results of allocation functions, lookups that return "not found" as NULL, optional parameters, and pointers read from structures another thread can change. CWE-476 adds that in multithreaded code the check needs proper locking around it, or a race (CWE-362) reopens the gap between check and use.
- Check before the first dereference: EXP34-C's Linux
tun_chr_poll()example readtun->skbefore testingtun. Because dereferencing NULL is undefined behaviour, the compiler could infer thattunwas non-null and remove the later test entirely. - Decide who owns the check: public APIs and functions that receive external input check their arguments (the callee). Internal functions with a documented non-null contract can rely on the caller, provided the contract is stated in an annotation the analyser understands rather than only in a comment.
The Memory Free Function artefact covers the other half. In standard C, free(NULL) does nothing, so a NULL check before free is not needed for safety; what matters is setting a freed pointer to NULL afterwards so a later check means something. That step is a separate D3FEND technique, Reference Nullification (D3-RN), covered in D3-RN Reference Nullification. D3FEND's MEM10-C reference goes one step further and recommends a single pointer validation function, so the checking logic is written once and can be tightened on platforms where more than NULL can be tested.
ATT&CK relationships
D3FEND lists no ATT&CK technique for D3-NPC. Its artefacts are code-level functions that ATT&CK techniques are not modelled as acting on, so D3FEND infers no relation (checked November 2026). The table below is an NHI Mgmt Group view, not a D3FEND mapping: these ATT&CK v20 techniques are where a missing null check typically ends up being used.
| ATT&CK ID | Technique | How a null dereference is used (our view) |
|---|---|---|
| T1499.004 | Application or System Exploitation | A crafted request or file triggers a NULL dereference that crashes a service; this is the usual outcome CWE-476 lists |
| T1068 | Exploitation for Privilege Escalation | A kernel NULL dereference where an unprivileged process has mapped page zero, the case mmap_min_addr exists to stop |
| T1203 | Exploitation for Client Execution | Rare: a client on a platform where address zero is mapped and writable, as in EXP34-C's libpng example on some ARM and XScale devices |
Where it is implemented
Null pointer checking shows up at four layers. Languages and compilers are listed alphabetically; the order is not a ranking.
| Layer | Mechanism | What it gives you |
|---|---|---|
| Source code | Explicit checks, a shared pointer validation function (MEM10-C), assertions for internal contracts | The control itself; works in any language |
| Language type system | C# nullable reference types (<Nullable>enable</Nullable>, warnings such as CS8602); Kotlin nullable and non-nullable types with ?. and ?:; Rust Option in place of null references | Missing checks become compile-time warnings or errors |
| Compiler and sanitizer | Clang UndefinedBehaviorSanitizer checks null, nonnull-attribute, returns-nonnull-attribute and the nullability group; GCC -fisolate-erroneous-paths-dereference (on by default at -O2) turns proven NULL dereference paths into traps | Run-time detection in testing; deterministic traps in some builds |
| Operating system | Linux vm.mmap_min_addr stops user processes mapping the lowest pages | Turns kernel NULL dereferences into crashes instead of attacker-controlled reads and writes |
Language notes
- C#: Microsoft's documentation states nullable reference types are entirely a compile-time feature; runtime behaviour is unchanged. Promote the warnings with
<WarningsAsErrors>nullable</WarningsAsErrors>so new code cannot add them, and treat the!null-forgiving operator as a review item. - Kotlin: the documentation lists the only ways to get a NullPointerException: an explicit throw, the
!!operator, initialisation inconsistencies, and Java interoperation through platform types. Those four are where review effort goes. - Rust: references and
Boxalways point to valid data and absence is expressed withOption;unwrap()andexpect()panic onNone, andunwrap_unchecked()onNoneis undefined behaviour. Raw pointers inunsafecode can still be null and need explicit checks. See the Memory-Safe Languages Guide.
Implementation steps
- Inventory the risky code. List C and C++ components,
unsafeRust blocks and interop layers (JNI, P/Invoke, Kotlin calling Java). These are where null checks are manual. - Turn on the analyser rules. Enable your static analyser's CWE-476 or EXP34-C rules and the language's own nullable analysis. Start in report-only mode on existing code and block only new findings, so the baseline does not stall delivery. See the SAST Triage Guide.
- Fix the patterns, not just the findings. Check allocation results, check before first use, add a validation function or annotation for non-null parameters, and set pointers to NULL after free.
- Add sanitizers to test builds. Build CI and fuzzing targets with Clang
-fsanitize=undefined(plus-fsanitize=nullabilitywhere_Nonnullannotations are used) or GCC's UBSan equivalent. - Harden the platform. On Linux hosts, confirm
vm.mmap_min_addris non-zero; the kernel documentation suggests a value such as 64k works for the vast majority of applications. - Roll back safely. If promoting nullable warnings to errors blocks a release, scope the setting back to new projects or files (C#
#nullabledirectives) rather than switching analysis off.
How to verify it works
- Static analysis trend: count of open CWE-476 findings per component, which should fall release on release. CWE rates automated static analysis as high effectiveness for this weakness.
- Sanitizer results: UBSan
nullreports in CI and fuzzing runs; any report is a defect, not noise. - Error-path testing: fuzz and fault-inject allocation failure and missing inputs. CWE-476 recommends triggering conditions such as low memory and insufficient permissions that rarely occur in normal use. See the Fuzzing Guide.
- Build configuration check: confirm warnings-as-errors for nullable diagnostics in each C# project file and that release builds do not carry
-fno-delete-null-pointer-checksas a workaround for code that checks too late. - Crash telemetry: segmentation faults at low addresses, NullReferenceException and NullPointerException counts in production, tracked per release.
Example metrics (examples, not standards): new CWE-476 findings merged per month (target zero); percentage of C# projects with nullable enabled and enforced; fuzzing hours per release on parsers; production null-related crashes per million sessions.
Limits and bypasses
- Non-null is not valid. A freed, uninitialised or out-of-bounds pointer passes a NULL check. CWE-476 notes the check does not handle improperly initialised variables (CWE-665). Pair it with D3-MBSV Memory Block Start Validation and reference nullification.
- Check after use is deleted. GCC documents that it assumes no code or data resides at address zero and is enabled by default on most targets (disabled on AVR and MSP430), so a late check can vanish. Embedded systems where address zero is valid need
-fno-delete-null-pointer-checksand careful review. - Time-of-check races. A pointer shared between threads can become NULL after the check without locking.
- Compile-time only. C# nullable analysis and Kotlin types do not change run-time behaviour at interop boundaries; data from Java or native code can still be null.
- Sanitizers are for testing. Clang's documentation says the full UBSan runtime needs careful security review before production use and recommends trap mode or the minimal runtime for security-sensitive applications.
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| CWE | CWE-476 NULL Pointer Dereference; related CWE-690, CWE-252 | The weakness D3-NPC prevents |
| SEI CERT C | EXP34-C (severity High, likelihood Likely, priority P18, level L1); MEM10-C | Coding rules D3FEND cites |
| NIST SP 800-53 Rev 5 | SA-11 Developer Testing and Evaluation; SA-15 Development Process, Standards, and Tools; SI-16 Memory Protection | Testing, coding standards and platform memory protection |
| NIST CSF 2.0 | PR.PS-06 secure software development practices | Programme level |
| MITRE ATT&CK mitigations | M1013 Application Developer Guidance; M1050 Exploit Protection | Our mapping; see the M1013 Application Developer Guidance page |
Practitioner checklist
- Enable CWE-476 and EXP34-C rules in static analysis and block new findings.
- Check every allocation and lookup result before use, and check before the first dereference, not after.
- Use a shared pointer validation function or non-null annotations so contracts are machine-readable.
- Set pointers to NULL after free so later checks are meaningful.
- Enable C# nullable reference types with warnings as errors; review every
!,!!,unwrap()and platform type use. - Run UBSan null checks in CI and fuzzing builds, and fuzz allocation failure paths.
- Confirm Linux
vm.mmap_min_addris non-zero on every host. - Track null-related crashes per release in production telemetry.
Standards and references
- MITRE D3FEND: Null Pointer Checking (D3-NPC)
- MITRE CWE: CWE-476 NULL Pointer Dereference
- MITRE CWE: 2025 CWE Top 25 Most Dangerous Software Weaknesses
- SEI CERT C Coding Standard: EXP34-C Do not dereference null pointers
- LLVM: Clang UndefinedBehaviorSanitizer
- GNU: GCC Options That Control Optimization
- Linux kernel: Documentation for /proc/sys/vm (mmap_min_addr)
- Microsoft Learn: Nullable reference types
Related NHI Mgmt Group resources: MITRE D3FEND Guide · D3-PAN Pointer Authentication · Secure Code Review Guide · MITRE ATT&CK T1499 Endpoint Denial of Service