Join our Newsletter — 33% off our NHI Course
Home› Guides› D3-NPC Null Pointer Checking: Where Checks Belong, Compiler…
Guide Application Security

D3-NPC Null Pointer Checking: Where Checks Belong, Compiler Settings and Verification

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 12 November 2026 10 min read
On this page

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

AttributeValue
D3FEND IDD3-NPC (d3f:NullPointerChecking)
TacticHarden
Parent techniquePointer Validation (D3-PV), under Source Code Hardening (D3-SCH)
Sibling under Pointer ValidationMemory Block Start Validation (D3-MBSV)
Digital artefactsMemory Free Function, Pointer Dereferencing Function (D3FEND relation: hardens)
Weakness addressedCWE-476 NULL Pointer Dereference, which D3FEND models as a weakness of Pointer Dereferencing Function
D3FEND referencesSEI CERT C EXP34-C, SEI CERT C MEM10-C, CWE-476
EffortLow per function; ongoing across a codebase, best enforced by compiler and analyser settings
Main limitationChecks 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 read tun->sk before testing tun. Because dereferencing NULL is undefined behaviour, the compiler could infer that tun was 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 IDTechniqueHow a null dereference is used (our view)
T1499.004Application or System ExploitationA crafted request or file triggers a NULL dereference that crashes a service; this is the usual outcome CWE-476 lists
T1068Exploitation for Privilege EscalationA kernel NULL dereference where an unprivileged process has mapped page zero, the case mmap_min_addr exists to stop
T1203Exploitation for Client ExecutionRare: 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.

LayerMechanismWhat it gives you
Source codeExplicit checks, a shared pointer validation function (MEM10-C), assertions for internal contractsThe control itself; works in any language
Language type systemC# nullable reference types (<Nullable>enable</Nullable>, warnings such as CS8602); Kotlin nullable and non-nullable types with ?. and ?:; Rust Option in place of null referencesMissing checks become compile-time warnings or errors
Compiler and sanitizerClang 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 trapsRun-time detection in testing; deterministic traps in some builds
Operating systemLinux vm.mmap_min_addr stops user processes mapping the lowest pagesTurns 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 Box always point to valid data and absence is expressed with Option; unwrap() and expect() panic on None, and unwrap_unchecked() on None is undefined behaviour. Raw pointers in unsafe code can still be null and need explicit checks. See the Memory-Safe Languages Guide.

Implementation steps

  1. Inventory the risky code. List C and C++ components, unsafe Rust blocks and interop layers (JNI, P/Invoke, Kotlin calling Java). These are where null checks are manual.
  2. 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.
  3. 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.
  4. Add sanitizers to test builds. Build CI and fuzzing targets with Clang -fsanitize=undefined (plus -fsanitize=nullability where _Nonnull annotations are used) or GCC's UBSan equivalent.
  5. Harden the platform. On Linux hosts, confirm vm.mmap_min_addr is non-zero; the kernel documentation suggests a value such as 64k works for the vast majority of applications.
  6. Roll back safely. If promoting nullable warnings to errors blocks a release, scope the setting back to new projects or files (C# #nullable directives) 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 null reports 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-checks as 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-checks and 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

FrameworkReferenceRelevance
CWECWE-476 NULL Pointer Dereference; related CWE-690, CWE-252The weakness D3-NPC prevents
SEI CERT CEXP34-C (severity High, likelihood Likely, priority P18, level L1); MEM10-CCoding rules D3FEND cites
NIST SP 800-53 Rev 5SA-11 Developer Testing and Evaluation; SA-15 Development Process, Standards, and Tools; SI-16 Memory ProtectionTesting, coding standards and platform memory protection
NIST CSF 2.0PR.PS-06 secure software development practicesProgramme level
MITRE ATT&CK mitigationsM1013 Application Developer Guidance; M1050 Exploit ProtectionOur 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_addr is non-zero on every host.
  • Track null-related crashes per release in production telemetry.

Standards and references

Related NHI Mgmt Group resources: MITRE D3FEND Guide · D3-PAN Pointer Authentication · Secure Code Review Guide · MITRE ATT&CK T1499 Endpoint Denial of Service

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 12 November 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org