Access of Resource Using Incompatible Type ('Type Confusion')
Description
Type Confusion occurs when a product allocates or initializes a resource using one type but later accesses that resource using an incompatible type. When the resource is accessed using the wrong type, it may lack expected properties, triggering logical errors. In memory-unsafe languages like C and C++, type confusion can enable out-of-bounds memory access because different types have different sizes and memory layouts. Attackers can exploit type confusion to read or write memory outside intended boundaries, potentially leading to code execution, information disclosure, or system crashes.
Risk
Type confusion vulnerabilities are particularly dangerous in low-level languages where type information is not enforced at runtime. Accessing memory through an incompatible type can cause reading beyond buffer boundaries (information disclosure), writing beyond buffer boundaries (memory corruption), interpreting data incorrectly (logic errors), and corrupting adjacent memory structures (code execution). In languages with looser type systems (PHP, JavaScript, Perl), type confusion can cause unexpected behavior in comparisons, mathematical operations, or data processing. Browser engines and document parsers are frequent targets for type confusion attacks.
Solution
Use type-safe programming practices and languages where possible. In C/C++, avoid unions for type punning unless absolutely necessary, use explicit type checks before casting, implement runtime type tracking for polymorphic objects, and use tagged unions or discriminated unions. In dynamic languages, validate types explicitly before operations and use strict comparison operators. Enable compiler warnings for type mismatches. Use static analysis tools to detect potential type confusion. Implement defensive programming with assertions to verify type expectations. Consider using memory-safe language features or memory-safe languages.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality, Integrity | Scope: Confidentiality, Integrity Read/Modify Memory - When memory is accessed using wrong type, it could read or write out of buffer bounds. |
| Availability | Scope: Availability DoS: Crash/Exit/Restart - Type confusion can cause crashes due to invalid memory access or assertion failures. |
| Integrity, Confidentiality, Availability | Scope: Integrity, Confidentiality, Availability Execute Unauthorized Code - Memory corruption from type confusion can lead to arbitrary code execution. |
Example Code
Vulnerable Code
// Vulnerable: Union-based type confusion
#include <stdio.h>
#include <string.h>
typedef union {
struct {
int type;
int value;
} integer_msg;
struct {
int type;
char *data; // Pointer - 8 bytes on 64-bit
} string_msg;
} Message;
void vulnerable_process_message(Message *msg) {
// Check type field
if (msg->integer_msg.type == 1) {
// Treat as integer message
printf("Integer value: %d\n", msg->integer_msg.value);
} else if (msg->integer_msg.type == 2) {
// Treat as string message
printf("String data: %s\n", msg->string_msg.data);
}
}
void exploit_type_confusion() {
Message msg;
// Set up as integer message
msg.integer_msg.type = 1;
msg.integer_msg.value = 0x41414141;
// Attacker modifies type field without updating data
msg.integer_msg.type = 2;
// Now value (0x41414141) is interpreted as pointer!
// Accessing this will read from arbitrary memory address
vulnerable_process_message(&msg); // Crash or info leak
}
// Vulnerable: C++ polymorphism type confusion
class Base {
public:
virtual void print() { std::cout << "Base\n"; }
};
class Derived : public Base {
public:
char buffer[100];
void print() override { std::cout << "Derived: " << buffer << "\n"; }
};
class Malicious {
public:
char exploit_buffer[200]; // Different size!
void print() { /* Different layout */ }
};
void vulnerable_process(Base* obj) {
// Vulnerable: Unsafe downcast without type check
Derived* d = (Derived*)obj; // C-style cast - no runtime check
// If obj is actually Malicious*, this accesses wrong memory layout
d->print();
strcpy(d->buffer, "data"); // May overflow if sizes differ
}
// Vulnerable: PHP type confusion in comparison
<?php
function vulnerable_auth($input_hash, $stored_hash) {
// Vulnerable: == operator allows type juggling
if ($input_hash == $stored_hash) {
return true; // Authenticated
}
return false;
}
// Attack: PHP type juggling
$stored_hash = "0e462097431906509019562988736854"; // Looks like scientific notation
$input_hash = "0"; // String "0" == "0e..." because both evaluate to 0
// "0" == "0e462097431906509019562988736854" is TRUE!
// Because both are treated as numbers: 0 == 0
$result = vulnerable_auth($input_hash, $stored_hash); // Returns true!
?>
# Vulnerable: Perl type confusion with array/scalar context
sub vulnerable_check_admin {
my ($username, $groups) = @_;
# Vulnerable: $groups could be array reference or scalar
# Developer expects array reference
if ($groups eq "admin") { # Vulnerable comparison
return 1;
}
# If $groups is passed as array ref ["admin", "users"]
# The eq comparison stringifies it to something like "ARRAY(0x...)"
# which doesn't match "admin"
# But if attacker passes string "admin" directly...
return 0;
}
# Expected call: check_admin("alice", ["admin", "users"])
# Attack call: check_admin("hacker", "admin") # Bypasses proper check
// Vulnerable: JavaScript type confusion
function vulnerableCompare(a, b) {
// Vulnerable: Loose equality allows type coercion
if (a == b) {
return true;
}
return false;
}
// Type confusion examples:
vulnerableCompare("0", 0); // true - string coerced to number
vulnerableCompare("", false); // true - both coerce to falsy
vulnerableCompare(null, undefined); // true - special case
vulnerableCompare([], false); // true - array coerces to 0
vulnerableCompare("1e1", 10); // true - scientific notation
// Security implications
function vulnerableAuthCheck(inputToken, storedToken) {
// Attacker sends array: {"token": [true]}
// [true] == "actual_token" could behave unexpectedly
if (inputToken == storedToken) {
return true; // May grant access incorrectly
}
return false;
}
Fixed Code
// Fixed: Tagged union with explicit type tracking
#include <stdio.h>
#include <string.h>
#include <assert.h>
typedef enum {
MSG_TYPE_INVALID = 0,
MSG_TYPE_INTEGER = 1,
MSG_TYPE_STRING = 2
} MessageType;
typedef struct {
MessageType type; // Explicit type tag
union {
int value;
char *data;
} payload;
} Message;
void fixed_process_message(Message *msg) {
// Fixed: Validate type before accessing
assert(msg != NULL);
switch (msg->type) {
case MSG_TYPE_INTEGER:
printf("Integer value: %d\n", msg->payload.value);
break;
case MSG_TYPE_STRING:
if (msg->payload.data != NULL) {
printf("String data: %s\n", msg->payload.data);
}
break;
default:
fprintf(stderr, "Invalid message type: %d\n", msg->type);
break;
}
}
// Fixed: Type-safe message creation functions
Message create_integer_message(int value) {
Message msg;
msg.type = MSG_TYPE_INTEGER;
msg.payload.value = value;
return msg;
}
Message create_string_message(char *data) {
Message msg;
msg.type = MSG_TYPE_STRING;
msg.payload.data = data;
return msg;
}
// Fixed: C++ with safe downcasting
#include <iostream>
#include <typeinfo>
class Base {
public:
virtual ~Base() = default;
virtual void print() const { std::cout << "Base\n"; }
// Fixed: Runtime type identification
virtual const char* getType() const { return "Base"; }
};
class Derived : public Base {
public:
char buffer[100] = {0};
void print() const override {
std::cout << "Derived: " << buffer << "\n";
}
const char* getType() const override { return "Derived"; }
};
void fixed_process(Base* obj) {
if (obj == nullptr) {
return;
}
// Fixed: Use dynamic_cast for safe downcasting
Derived* d = dynamic_cast<Derived*>(obj);
if (d != nullptr) {
// Safe: d is actually a Derived object
d->print();
strncpy(d->buffer, "data", sizeof(d->buffer) - 1);
} else {
// Not a Derived object - handle appropriately
std::cerr << "Object is not Derived type\n";
obj->print(); // Use base class interface
}
}
// Even better: Use std::variant for type-safe unions (C++17)
#include <variant>
#include <string>
using SafeMessage = std::variant<int, std::string>;
void process_safe_message(const SafeMessage& msg) {
std::visit([](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, int>) {
std::cout << "Integer: " << arg << "\n";
} else if constexpr (std::is_same_v<T, std::string>) {
std::cout << "String: " << arg << "\n";
}
}, msg);
}
// Fixed: PHP with strict comparison
<?php
function fixed_auth($input_hash, $stored_hash) {
// Fixed: Use === for strict type comparison
if (!is_string($input_hash) || !is_string($stored_hash)) {
return false; // Reject non-string inputs
}
// Fixed: Use hash_equals for timing-safe comparison
if (hash_equals($stored_hash, $input_hash)) {
return true;
}
return false;
}
// Better: Always use strict comparison
$stored_hash = "0e462097431906509019562988736854";
$input_hash = "0";
// "0" === "0e462097431906509019562988736854" is FALSE (string comparison)
$result = ($input_hash === $stored_hash); // Correctly returns false
// Even better: Explicit type validation
function validate_and_compare($input, $expected) {
if (!is_string($input)) {
throw new TypeError("Input must be string");
}
if (strlen($input) !== strlen($expected)) {
return false;
}
return hash_equals($expected, $input);
}
?>
# Fixed: Perl with explicit type checking
use strict;
use warnings;
use Scalar::Util qw(reftype);
sub fixed_check_admin {
my ($username, $groups) = @_;
# Fixed: Validate type explicitly
unless (ref($groups) eq 'ARRAY') {
die "groups must be an array reference";
}
# Fixed: Proper array membership check
foreach my $group (@{$groups}) {
if ($group eq "admin") {
return 1;
}
}
return 0;
}
# Usage with validation
sub safe_check_admin {
my ($username, $groups) = @_;
# Type validation
my $ref_type = reftype($groups);
if (!defined $ref_type || $ref_type ne 'ARRAY') {
warn "Invalid groups parameter type";
return 0;
}
return grep { $_ eq 'admin' } @{$groups} ? 1 : 0;
}
// Fixed: JavaScript with strict equality and type validation
function fixedCompare(a, b) {
// Fixed: Use strict equality (no type coercion)
if (a === b) {
return true;
}
return false;
}
// Fixed: Explicit type validation
function fixedAuthCheck(inputToken, storedToken) {
// Validate types explicitly
if (typeof inputToken !== 'string') {
console.warn('inputToken must be a string');
return false;
}
if (typeof storedToken !== 'string') {
console.warn('storedToken must be a string');
return false;
}
// Fixed: Strict equality comparison
if (inputToken === storedToken) {
return true;
}
return false;
}
// Even better: Use TypeScript for compile-time type safety
// function fixedAuthCheck(inputToken: string, storedToken: string): boolean {
// return inputToken === storedToken;
// }
// For objects, validate structure
function validateMessageType(msg) {
if (typeof msg !== 'object' || msg === null) {
throw new TypeError('Message must be an object');
}
if (typeof msg.type !== 'string') {
throw new TypeError('Message type must be a string');
}
const validTypes = ['integer', 'string', 'binary'];
if (!validTypes.includes(msg.type)) {
throw new TypeError(`Invalid message type: ${msg.type}`);
}
return true;
}
CVE Examples
- CVE-2025-32352: PHP authentication bypass via type confusion when comparing MD5 hashes.
- CVE-2010-4577: CSS parsing type confusion causing out-of-bounds read in browser.
- CVE-2011-0611: Size inconsistency between types enabled arbitrary code execution.
- CVE-2010-0258: File parsing type confusion caused object type misinterpretation.
Related CWEs
- CWE-704: Incorrect Type Conversion or Cast (parent)
- CWE-1287: Improper Validation of Specified Type of Input (related)
- CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer (can follow)
- CWE-136: Type Errors (category)
References
- MITRE Corporation. "CWE-843: Access of Resource Using Incompatible Type ('Type Confusion')." https://cwe.mitre.org/data/definitions/843.html
- Microsoft. "Understanding Type Confusion Vulnerabilities."
- Google Project Zero. Type Confusion vulnerability research.