In a strongly-typed language, the compiler enforces that a Foo object is treated only as Foo. Type confusion happens when something breaks that promise: a bug in a JIT, a faulty cast, a missing tag check, an unchecked union.
The result is structural — the same bytes in memory are interpreted as different types by different parts of the program. Type confusion is the single most common vulnerability class in modern browser exploits, because JavaScript engines optimize aggressively and the optimizers are where the bugs live.
The shape
Two classes with the same memory size but different field layouts:
Both occupy 32 bytes. ImageBuffer.width is at +0x00; ScriptHandler.callback is also at +0x00. If the runtime allocates an ImageBuffer and the attacker sets width = 0x401234, then a buggy cast lets the code interpret the same memory as a ScriptHandler — and now callback is a function pointer to 0x401234. Calling handler->callback(...) jumps to attacker-controlled code.
Where it shows up
1. C / C++ unsafe casts (reinterpret_cast)
2. C unions without a tag
Unions share memory between members. If the code writes to one variant and reads from another without checking which is currently valid, it's type confusion by design.
3. C++ downcasting
Casting a base class to a derived class without verifying the actual runtime type. If the object is really a different sibling class, fields are at wrong offsets.
The correct C++ form — dynamic_cast<Dog*>(a) — returns nullptr if a isn't really a Dog. But dynamic_cast requires RTTI and is slower, so codebases often use the unchecked C-style cast.
4. JavaScript engine JITs
This is the home turf of modern type-confusion exploitation. V8, JavaScriptCore, SpiderMonkey all watch how variables are used and compile specialized fast paths assuming consistent types. If the engine's guard on the type is missing or wrong, the JIT's compiled code executes type-specialized operations on the wrong type. The exploit shape is:
- Train the JIT to assume
xis always an integer array. - Trigger the compiled fast path.
- During execution, secretly replace
xwith an object array (Type Confusion!). - The compiled code does integer arithmetic on the object pointers, treating object addresses as
intvalues. - Result: arbitrary read/write primitive, full RCE within the renderer process.
Almost every Pwn2Own browser win in the last decade has been a type confusion in the JIT. Both the incommensurable — the JIT is incredibly complex — and the incentive — speed matters — make this fertile ground for bugs.
Famous incidents
| Year | CVE | What it was |
|---|---|---|
| 2018 | CVE-2018-4878 | Adobe Flash Player. Type confusion exploited in the wild by North Korean state actors against South Korean targets. |
| 2021 | CVE-2021-21193 | Chrome V8 type confusion. Exploited in-the-wild zero day. |
| 2022 | CVE-2022-1096 | Chrome V8. Type confusion targeting JIT compilation. Used by NSO Group for targeted attacks. |
| 2023 | CVE-2023-4863 | libwebp. Started as integer overflow but the chain involved type confusion through structure overlap. Mass-patched at Apple, Google, Microsoft, Mozilla within a week. |
| 2024 | CVE-2024-0519 | V8 out-of-bounds read. Patched same day as disclosure; in-the-wild exploitation confirmed. |
Defenses
1. Use safe casts
In C++, prefer dynamic_cast with a null check over C-style casts. Pay the RTTI overhead. In codebases that have explicitly opted out of RTTI for size, use a custom tagged-type system: every object carries a type ID, every cast checks the ID before reinterpreting.
2. Tag your unions
Replace bare C unions with tagged structures: a discriminator field that says which variant is current. Standardized as std::variant<A,B,C> in C++17, which throws on invalid access. Rust's enums encode the tag in the type system — type confusion of this form is impossible.
3. Sanitize during testing
-fsanitize=cfi (Control Flow Integrity) catches some type-confusion-derived indirect calls at runtime. UBSan with -fsanitize=vptr catches polymorphic class confusion. Use in CI; fix every report.
4. Compile-time static analysis
Clang's -Wcast-align, -Wstrict-aliasing; tools like CodeQL and Semmle can find suspicious casts. Not a silver bullet but reduces the surface.
5. Memory-safe languages
Rust's type system makes the structural form of this bug impossible — every cast must be explicit and checked. Go has interface assertion (x.(*Foo)) that returns a second boolean. C#, Java, Swift all type-check at runtime.
Type confusion is what happens when the same bytes get interpreted as two different types. It's structural: an unsafe cast, a missing tag check, a JIT optimization that ran without its guard. The exploits aren't subtle — you have one type's field at the same offset as the other type's function pointer, and now you control execution.
Defense is type-system discipline: explicit checked casts, tagged unions, modern C++ std::variant, or jumping to a memory-safe language entirely. In codebases where that's not possible — JIT compilers, kernel code — rigorous testing, fuzzing, and sandboxing carry the load.
References
Formatted in APA 7.
- MITRE. (2024). CWE-843: Access of resource using incompatible type ('type confusion'). Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/843.html
- Google. (2024). V8 sandbox and the case for type-aware fuzzing. Google Project Zero. https://googleprojectzero.blogspot.com/
- Haller, I., Slowinska, A., Neugschwandtner, M., & Bos, H. (2013). Dowsing for overflows: A guided fuzzer to find buffer boundary violations. USENIX Security Symposium. https://www.usenix.org/system/files/conference/usenixsecurity13/sec13-paper_haller.pdf
- Stroustrup, B. (2013). The C++ programming language (4th ed.). Addison-Wesley.
- National Institute of Standards and Technology. (2022). CVE-2022-1096 detail (Chrome V8). National Vulnerability Database. https://nvd.nist.gov/vuln/detail/CVE-2022-1096