When code reads an object as a type it is not, the result ranges from a wrong value to memory corruption and code execution, a bug class usually called type confusion. D3-VTV Variable Type Validation is the MITRE D3FEND countermeasure for it. D3FEND defines it as "Ensuring that a variable has the correct type." It sits in the Harden tactic under Source Code Hardening (D3-SCH) and hardens the Pointer Dereferencing Function artefact. This card explains what the technique asks of developers, the weaknesses it removes, why D3FEND lists no ATT&CK relationship for it, where type checks are enforced (type systems, checked casts, compiler and sanitizer instrumentation, input validation), how to verify it and its limits. It is written for application security engineers, code reviewers and teams working in C, C++ and dynamically typed languages.
Key takeaways
- Two duties, per D3FEND: choose the right type for how a variable will be used, and check programmatically that a variable has the expected type before using it.
- Type confusion is a memory safety bug in native code. CWE-843 says an incompatible-type access to a memory buffer can read or modify memory, run unauthorised code or crash the program.
- Static typing does most of the work. The checks that matter are at the places the type system is bypassed: casts, unions, void pointers, serialisation and untrusted input.
- D3FEND lists no ATT&CK relationship for D3-VTV. Its value is in removing CWE-843 and CWE-704 defects before they become exploitable.
- Verify with checked casts, sanitizers and static analysis. Clang's CFI cast checks can run in release builds; its TypeSanitizer finds strict aliasing violations but is still experimental.
D3-VTV at a glance
| Field | D3-VTV Variable Type Validation |
|---|---|
| D3FEND ID | D3-VTV |
| Tactic | Harden |
| Parent technique | Source Code Hardening (D3-SCH) |
| Child techniques | None |
| Artefact hardened | Pointer Dereferencing Function (D3FEND relation: hardens); a subroutine that addresses a pointer and a memory block |
| Definition | "Ensuring that a variable has the correct type." |
| D3FEND reference | Princeton University: "Why type checking?" |
| Typical tools | Type systems and type checkers, checked casts, compiler warnings, Clang CFI cast checks, TypeSanitizer, static analysers, schema validation |
| Effort | Low in strongly typed code; high where legacy C relies on casts and unions |
| ATT&CK relationship in D3FEND | None listed, direct or inferred (checked November 2026) |
How it works
D3FEND's article has two parts: the developer should consider how a variable will be used throughout the program and choose the right type, and should check programmatically that the variable has the correct (expected) type before using it. It adds that the result of an operation on an unexpected type varies by language, and that the entry is not a definitive coding guideline. The reference D3FEND cites, Kim Bruce's notes on type checking, explains the trade-off: static checking catches errors earlier and lets run-time checks be removed, but static checkers are conservative, and languages such as C++ need unchecked casts, which is exactly where type errors get through.
In practice the technique applies differently by language family:
- C: the compiler trusts casts. CERT rule EXP39-C forbids accessing a variable through a pointer of an incompatible type, because it is undefined behaviour under the C aliasing rules (C standard 6.5, paragraph 7). Typical failures are a float written through an int pointer, or a structure read as a different structure after reallocation.
- C++: a static_cast from base to derived class, or a cast from void*, is not checked at run time. dynamic_cast checks the dynamic type, and tagged types such as std::variant replace unchecked unions (our view of common practice).
- Dynamically typed languages (JavaScript, Python, PHP): types are checked at the moment of use, so the defence is explicit checks at boundaries plus optional static type checkers. CWE-843 lists Python as an applicable language.
- Untrusted input in any language: CWE-1287 covers data that is meant to be a particular type but is never checked, such as an ID meant to be numeric that carried SQL injection (CVE-2008-2223).
Weaknesses it addresses
| CWE | Name | Typical consequence (per CWE) |
|---|---|---|
| CWE-843 | Access of Resource Using Incompatible Type ('Type Confusion') | Logic errors; with memory buffers, memory read or modification, unauthorised code execution, or crash. CanPrecede CWE-119 |
| CWE-704 | Incorrect Type Conversion or Cast | Parent of CWE-843; CERT EXP39-C is a subset of it |
| CWE-1287 | Improper Validation of Specified Type of Input | Input of the wrong type reaches logic that assumes the right one; child of CWE-20 |
CWE-843 rates automated static analysis as highly effective for detection. CERT rates EXP39-C as medium severity, unlikely likelihood and high remediation cost (P2, L3), and notes that GCC's -fstrict-aliasing, on at -O2, can change program output when the rule is broken, while -Wstrict-aliasing (in -Wall) warns about some violations but not all.
ATT&CK techniques D3FEND relates to D3-VTV
Relationship type: none listed. D3FEND shows neither direct nor inferred ATT&CK relationships for D3-VTV (checked November 2026), and this card does not invent one. Type confusion bugs are a common route to the memory corruption that exploits use, so the effect on attacks is indirect. For the adversary behaviours, see MITRE ATT&CK T1203 Exploitation for Client Execution and T1068 Exploitation for Privilege Escalation; these are context, not a D3FEND mapping.
Where it is implemented
| Layer | Mechanism | Strength | Limit |
|---|---|---|---|
| Language and type system | Static types, generics, tagged unions; in dynamic languages, optional type annotations with a checker in CI | Removes most type errors before run time | Escape hatches (casts, any, unsafe) still need review |
| Checked casts (C++) | dynamic_cast for downcasts where the dynamic type is uncertain | Fails safely on the wrong type | Run-time cost; needs RTTI |
| Coding standard | CERT EXP39-C; ban type punning through pointers; use memcpy or approved patterns | Clear rule for review | CERT commenters disagree on which alternatives are fully portable |
| Compiler warnings (GCC) | -Wstrict-aliasing; -fno-strict-aliasing as a workaround for legacy code | Cheap | Warns about some violations, not all |
| Release-build cast checks (Clang) | -fsanitize=cfi-derived-cast and cfi-unrelated-cast | Clang documents CFI schemes as optimised for release builds | Needs LTO (-flto or -flto=thin) and a -fvisibility flag |
| Test-build detection (Clang) | TypeSanitizer, -fsanitize=type | Finds strict aliasing violations in tests | Experimental; about 8x memory; cannot run with AddressSanitizer or ThreadSanitizer; unions not reliably covered |
| Static analysis | Cast and type confusion checkers in CI | High effectiveness for CWE-843 per CWE | False positives need triage |
| Input validation | Schema and type checks at API and parser boundaries ("accept known good", per CWE-1287) | Stops wrong-type input early | Does not cover internal casts |
Clang's cast checks are covered with its other schemes in the D3-CFI Control Flow Integrity card. For build flags across toolchains, see the Compiler and Toolchain Hardening Guide. For boundary checks, see the Input Validation and Output Encoding Guide.
Choosing an approach
- New code: pick a language and design where types are checked by the compiler, and keep unchecked casts to reviewed wrapper code; see the Memory-Safe Languages Guide.
- Existing C++: replace downcasts on untrusted paths with dynamic_cast or a type tag, and consider CFI cast checks in release builds where LTO is already used.
- Existing C: enforce EXP39-C in review, keep -Wstrict-aliasing on, and use -fno-strict-aliasing only as a documented stopgap for code that cannot be fixed yet.
- JavaScript, TypeScript and Python services: validate types at every external boundary and run a type checker in CI; see the TypeScript and JavaScript Secure Coding Guide.
Implementation steps
- Find the escape hatches. Search for casts from void* or base classes, unions used for type punning, unchecked deserialisation and untyped external input. Rank them by exposure to untrusted data.
- Set the rules. Add EXP39-C and the approved alternatives (checked casts, tagged types, memcpy for representation copies) to the coding standard; see the C and C++ Secure Coding Guide.
- Run analysis in report mode. Enable cast and type checkers, record a baseline and triage; see the SAST Triage Guide.
- Fix the highest-risk sites. Start with parsers and object factories that choose a type from input data.
- Gate new code. Fail pull requests that add new type findings or unchecked casts outside approved wrappers.
- Add instrumentation. Try TypeSanitizer in a separate test configuration (it cannot share a build with AddressSanitizer), and pilot CFI cast checks in a release-like build with LTO.
- Roll back safely. If a CFI check stops a legitimate path in a pilot, use Clang's ignorelist for that function while the code is fixed, rather than turning the scheme off.
How to verify it works
- Seeded defects: add a base-to-derived cast to the wrong class and a float accessed through an int pointer in a test branch. The CFI build should trap on the first, TypeSanitizer should report the second, and static analysis should flag both.
- Boundary tests: send wrong-type values (strings for numbers, arrays for objects) to each API and confirm rejection rather than a crash or a coerced value.
- Fuzzing: fuzz parsers with sanitizer builds and watch type confusion reports fall release by release; see the Fuzzing Guide.
| Metric (examples, not standards) | What it shows |
|---|---|
| Open CWE-843 and CWE-704 findings by component | Remaining backlog |
| Unchecked casts outside approved wrappers | Escape hatch growth |
| Share of release builds with cast checks enabled | Runtime enforcement coverage |
| Share of external endpoints with schema or type validation | Boundary coverage |
Limits, bypasses and failure patterns
- Checks only where the type is known: a void* carries no type, so a check needs a tag or RTTI the code may not have.
- Optimised-away evidence: Clang notes earlier optimisation passes can remove a violation before TypeSanitizer sees it, and higher optimisation levels can hide some.
- Unions and type punning: TypeSanitizer does not instrument unions reliably, and CERT records disagreement over whether union-based punning gives defined results.
- Coercion instead of rejection: in dynamic languages, silently converting "1" to 1 passes a type check and still lets malformed input through; CWE-1287 also calls for checking length, range and syntax, not only type.
- Disabling strict aliasing as a fix: -fno-strict-aliasing removes the symptom in one compiler, not the undefined behaviour.
- Suppressions that never expire: ignorelists and no_sanitize attributes need an owner and a review date.
Framework mapping
| Framework | Reference | Relationship to D3-VTV |
|---|---|---|
| SEI CERT C | EXP39-C | Rule against incompatible-type access |
| MITRE CWE | CWE-843, CWE-704, CWE-1287 | Weaknesses the technique prevents |
| NIST SP 800-53 Rev 5 | SA-11, SA-15 | Developer testing; development standards and tools |
| NIST SP 800-53 Rev 5 | SI-10 | Information input validation, for the CWE-1287 boundary case |
| MITRE ATT&CK | None in D3FEND | No relationship published for D3-VTV |
Related countermeasures
- Siblings under Source Code Hardening: D3-PV Pointer Validation, D3-VI Variable Initialization, D3-IRV Integer Range Validation and D3-DLV Domain Logic Validation. Pointer validation checks where a pointer points; type validation checks what it points at.
- Runtime counterpart: D3-CFI Control Flow Integrity, for virtual call and cast checks at run time.
- Parent: Source Code Hardening (D3-SCH); see the MITRE D3FEND Guide.
Practitioner checklist
- Inventory casts from void* and base classes, punning unions and untyped deserialisation, ranked by exposure.
- Adopt CERT EXP39-C and approved alternatives in the coding standard.
- Use checked casts or type tags wherever the type comes from data.
- Keep -Wstrict-aliasing on and treat -fno-strict-aliasing as a documented stopgap.
- Run type and cast checkers in CI, then gate new findings.
- Pilot Clang CFI cast checks in release builds that already use LTO.
- Validate type, length and range at every external boundary.
- Give every sanitizer suppression an owner and a review date.
Standards and references
- MITRE D3FEND: Variable Type Validation (D3-VTV)
- MITRE CWE: CWE-843 Access of Resource Using Incompatible Type
- MITRE CWE: CWE-1287 Improper Validation of Specified Type of Input
- SEI CERT C Coding Standard: EXP39-C Do not access a variable through a pointer of an incompatible type
- Princeton University: Why type checking?
- LLVM: Clang Control Flow Integrity
- LLVM: TypeSanitizer
- NIST: SP 800-53 Rev 5, Security and Privacy Controls
Related NHI Mgmt Group resources: Secure Coding Guide · C and C++ Secure Coding Guide · Input Validation and Output Encoding Guide · Compiler and Toolchain Hardening Guide · MITRE D3FEND Guide