Incorrect Type Conversion or Cast
Description
Incorrect Type Conversion or Cast is a vulnerability where software does not correctly convert an object, resource, or structure from one type to another. This includes improper casts between numeric types (signed/unsigned, different sizes), incorrect pointer casts, union type confusion, and relying on implicit type coercion that produces unexpected results. When type conversions are incorrect, the resulting value may be truncated, sign-extended incorrectly, or completely misinterpreted, leading to security vulnerabilities like buffer overflows, memory corruption, or logic errors.
Risk
Incorrect type conversions create serious security vulnerabilities. Converting signed negative values to unsigned types produces large positive values, potentially causing buffer overflows when used as sizes. Truncating 64-bit values to 32-bit can result in heap corruption when the truncated value is used for memory operations. Union type confusion allows modifying memory through one type interpretation while reading through another, enabling out-of-bounds access. Loose type comparisons in dynamic languages can bypass security checks by exploiting type coercion rules. The risk is amplified because these issues may only manifest with specific input values that trigger the incorrect conversion.
Solution
Use explicit type conversions and validate values before converting. Check that values fit within the target type's range before casting. Avoid mixing signed and unsigned types in comparisons and arithmetic. Use fixed-width integer types (int32_t, uint64_t) to ensure consistent behavior across platforms. In dynamically typed languages, use strict equality operators and explicit type checking. Be cautious with union types—document and enforce which member is valid. Enable compiler warnings for type conversion issues and treat them as errors. Use static analysis tools that detect dangerous type conversions.
Common Consequences
| Impact | Details |
|---|---|
| Integrity | Scope: Integrity Modify Memory - Incorrect type conversions can lead to writing beyond buffer bounds or corrupting memory. |
| Availability | Scope: Availability DoS: Crash, Exit, or Restart - Memory corruption from bad casts often causes crashes. |
| Other | Scope: Other Varies by Context - Impacts depend on how the incorrectly converted value is used. |
Example Code
Vulnerable Code
// Vulnerable: Signed to unsigned conversion of return value
unsigned int vulnerable_read_data() {
int amount = 0;
// accessmainframe() returns -1 on error
amount = accessmainframe();
// Vulnerable: Returning int as unsigned int
// If amount is -1, returns 4,294,967,295 (UINT_MAX) on 32-bit
return amount;
}
void use_vulnerable() {
unsigned int size = vulnerable_read_data();
if (size > 0 && size < 1000000) { // This passes for UINT_MAX!
// Actually, UINT_MAX > 1000000, so this would fail
// But other checks might pass unexpectedly
}
char* buffer = malloc(size); // Huge allocation or overflow
}
// Vulnerable: 64-bit to 32-bit truncation (CVE-2021-43537 pattern)
void vulnerable_truncation(uint64_t large_value) {
// Vulnerable: Forced cast truncates upper 32 bits
uint32_t truncated = (uint32_t)large_value;
// If large_value = 0x100000010 (4GB + 16)
// truncated = 16
char* buffer = malloc(truncated); // Allocates only 16 bytes
// Later code uses large_value to access buffer - overflow!
fill_buffer(buffer, large_value);
}
// Vulnerable: Union type confusion
struct MessageBuffer {
int msgType;
union {
char *name; // Pointer (4 or 8 bytes)
int nameID; // Integer (4 bytes)
};
};
void vulnerable_union_access(struct MessageBuffer *buf) {
// First, name is set to a valid pointer
buf->name = "ValidString";
// Then, attacker modifies nameID
buf->nameID = 0x41414141; // Overwrites pointer value!
// Vulnerable: Now using name as pointer, but it points to 0x41414141
printf("Name: %s\n", buf->name); // Crash or arbitrary read
// Also vulnerable: reading past union boundaries
char* ptr = buf->name;
ptr[100] = 'X'; // Out-of-bounds write if pointer was corrupted
}
<?php
// Vulnerable: Loose comparison type coercion (CVE-2022-3979 pattern)
function vulnerable_validate_hash($user_hash, $stored_hash) {
// Vulnerable: Loose comparison with !=
if ($user_hash != $stored_hash) {
return false;
}
return true;
}
// Attack: If stored_hash = "0e12345678" (scientific notation)
// and user provides "0e99999999" (different number but both equal 0)
// "0e12345678" == "0e99999999" evaluates to TRUE!
// Both are treated as 0 in scientific notation
// Vulnerable: Type juggling in authentication
$password = $_GET['password'];
$correct = 0; // Integer zero
// Vulnerable: Comparing string to integer with ==
if ($password == $correct) {
// "0" == 0 is TRUE
// "" == 0 is TRUE
// "abc" == 0 is TRUE (non-numeric string equals 0)
grant_access();
}
?>
// Vulnerable: Incorrect cast in Java
public class VulnerableCast {
public void vulnerableDowncast(Object obj) {
// Vulnerable: Unchecked cast can throw ClassCastException
String str = (String) obj; // Crashes if obj isn't String
process(str);
}
public void vulnerableNumericCast(long bigValue) {
// Vulnerable: Silent truncation
int smallValue = (int) bigValue;
// If bigValue = 2147483648L, smallValue = -2147483648
// Sign bit interpretation changes completely!
if (smallValue > 0) { // This fails for large positive longs
allocate(smallValue);
}
}
}
Fixed Code
// Fixed: Validate before unsigned conversion
int secure_read_data(unsigned int *result) {
int amount = accessmainframe();
// Fixed: Check for error before conversion
if (amount < 0) {
return -1; // Error indicator
}
*result = (unsigned int)amount;
return 0; // Success
}
void use_secure() {
unsigned int size;
if (secure_read_data(&size) < 0) {
// Handle error
return;
}
if (size == 0 || size > MAX_SIZE) {
return;
}
char* buffer = malloc(size);
}
// Fixed: Validate before truncation
int secure_truncation(uint64_t large_value, uint32_t *result) {
// Fixed: Check if value fits in 32 bits
if (large_value > UINT32_MAX) {
return -1; // Error: value too large
}
*result = (uint32_t)large_value;
return 0;
}
void use_secure_truncation(uint64_t value) {
uint32_t truncated;
if (secure_truncation(value, &truncated) < 0) {
// Handle error - value doesn't fit
return;
}
char* buffer = malloc(truncated);
fill_buffer(buffer, truncated); // Use same variable consistently
}
// Fixed: Safe union handling with type tag
typedef enum { MSG_BY_NAME, MSG_BY_ID } MessageType;
struct SafeMessageBuffer {
MessageType msgType;
union {
char *name;
int nameID;
} data;
};
void secure_union_access(struct SafeMessageBuffer *buf) {
// Fixed: Check type tag before accessing union member
switch (buf->msgType) {
case MSG_BY_NAME:
if (buf->data.name != NULL) {
printf("Name: %s\n", buf->data.name);
}
break;
case MSG_BY_ID:
printf("ID: %d\n", buf->data.nameID);
break;
default:
// Invalid type - handle error
break;
}
}
// Fixed: Encapsulate union access
void set_message_name(struct SafeMessageBuffer *buf, char *name) {
buf->msgType = MSG_BY_NAME;
buf->data.name = name;
}
void set_message_id(struct SafeMessageBuffer *buf, int id) {
buf->msgType = MSG_BY_ID;
buf->data.nameID = id;
}
<?php
// Fixed: Strict comparison
function secure_validate_hash($user_hash, $stored_hash) {
// Fixed: Use strict comparison ===
if ($user_hash !== $stored_hash) {
return false;
}
return true;
}
// Even better: Use constant-time comparison
function secure_validate_hash_timing_safe($user_hash, $stored_hash) {
return hash_equals($stored_hash, $user_hash);
}
// Fixed: Type-safe password check
$password = $_GET['password'];
$correct_hash = '$2y$10$...'; // Stored password hash
// Fixed: Use password_verify for secure comparison
if (password_verify($password, $correct_hash)) {
grant_access();
}
// Fixed: Explicit type validation
function secure_process($value) {
// Fixed: Validate type explicitly
if (!is_string($value)) {
throw new InvalidArgumentException("String expected");
}
// Now safe to use as string
return trim($value);
}
?>
// Fixed: Safe casting with instanceof check
public class SecureCast {
public void secureDowncast(Object obj) {
// Fixed: Check type before casting
if (obj instanceof String) {
String str = (String) obj;
process(str);
} else {
handleInvalidType(obj);
}
}
public int secureNumericCast(long bigValue) throws ArithmeticException {
// Fixed: Use Math.toIntExact which throws on overflow
return Math.toIntExact(bigValue);
}
// Alternative: Manual range check
public int secureNumericCastManual(long bigValue) {
if (bigValue < Integer.MIN_VALUE || bigValue > Integer.MAX_VALUE) {
throw new ArithmeticException("Value out of int range: " + bigValue);
}
return (int) bigValue;
}
}
CVE Examples
- CVE-2021-43537: Unsigned 64-bit to 32-bit cast caused integer overflow and heap memory corruption in browser.
- CVE-2022-3979: PHP loose comparison (!=) instead of strict (!==) allowed hash validation bypass.
- CVE-2009-0231: Integer truncation causing heap buffer overflow.
References
- MITRE Corporation. "CWE-704: Incorrect Type Conversion or Cast." https://cwe.mitre.org/data/definitions/704.html
- CERT C Coding Standard. "INT31-C. Ensure that integer conversions do not result in lost or misinterpreted data."
- OWASP. "Type Juggling Vulnerabilities."