Signed to Unsigned Conversion Error
Description
Signed to Unsigned Conversion Error occurs when a signed integer value is converted to an unsigned integer type. When the signed value is negative, the conversion produces a very large positive unsigned value (typically near the maximum value for that unsigned type). For example, converting -1 (signed int) to an unsigned 32-bit integer produces 4294967295. This is particularly dangerous when the converted value is used for memory allocation sizes, buffer lengths, or loop counters, as the unexpectedly large value can cause buffer overflows or resource exhaustion.
Risk
Signed to unsigned conversion errors are extremely dangerous because they can transform small negative values (often error codes like -1) into enormous positive values. Functions returning -1 on error are common in C/C++ APIs. If these error return values are not checked before being used as sizes in malloc(), memcpy(), or array accesses, the result is catastrophic buffer overflow or memory exhaustion. Attackers specifically craft inputs to trigger error conditions that produce negative values, knowing the conversion will create exploitable overflow conditions.
Solution
Always check return values from functions that may return negative error codes before using them as unsigned sizes. Use size_t or unsigned types for variables that should never be negative. Add explicit range validation before type conversions. Enable compiler warnings for implicit signed/unsigned conversions (-Wsign-conversion in GCC/Clang). Consider using separate error handling mechanisms rather than sentinel values. Apply static analysis to identify dangerous conversion patterns.
Common Consequences
| Impact | Details |
|---|---|
| Integrity | Scope: Memory Corruption Negative values converted to large unsigned values cause massive buffer overflows when used as sizes. |
| Availability | Scope: Resource Exhaustion Large converted values can cause memory exhaustion through enormous allocations. |
| Access Control | Scope: Code Execution Buffer overflows resulting from conversion errors enable arbitrary code execution. |
Example Code + Solution Code
Vulnerable Code
#include <stdlib.h>
#include <string.h>
// VULNERABLE: Function returns -1 on error
int get_data_size(int fd) {
int size = read_header(fd);
if (size < 0) return -1; // Error return
return size;
}
void process_file(int fd) {
int size = get_data_size(fd);
// Missing error check!
// size (-1) converted to size_t becomes huge positive
char *buffer = malloc(size); // Allocates 4GB or fails
read(fd, buffer, size); // Overflow!
}
// VULNERABLE: memcpy with signed size
void copy_data(char *dest, char *src, int count) {
// If count is negative from upstream error...
memcpy(dest, src, count); // count converted to size_t, huge value
}
// VULNERABLE: Array index with error value
#define ERROR_VALUE -1
int data_array[1000];
void store_result(int index, int value) {
// index might be ERROR_VALUE from failed operation
data_array[index] = value; // Underflow: accesses before array
}
Fixed Code
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
// SAFE: Check error return before use
int get_data_size(int fd) {
int size = read_header(fd);
if (size < 0) return -1;
return size;
}
bool process_file_safe(int fd) {
int size = get_data_size(fd);
// Check for error return!
if (size < 0) {
return false; // Handle error
}
// Additional sanity check
if (size > MAX_BUFFER_SIZE) {
return false;
}
// Now safe to use as size
char *buffer = malloc((size_t)size);
if (!buffer) return false;
ssize_t bytes_read = read(fd, buffer, (size_t)size);
free(buffer);
return (bytes_read == size);
}
// SAFE: Validate signed value before conversion
bool copy_data_safe(char *dest, size_t dest_size,
const char *src, int count) {
// Reject negative count
if (count < 0) {
return false;
}
// Check against destination size
if ((size_t)count > dest_size) {
return false;
}
memcpy(dest, src, (size_t)count);
return true;
}
// SAFE: Use separate error handling
typedef struct {
bool success;
size_t value;
} SizeResult;
SizeResult get_size_safe(int fd) {
SizeResult result = {false, 0};
int size = read_header(fd);
if (size >= 0) {
result.success = true;
result.value = (size_t)size;
}
return result;
}
Exploited in the Wild
Okio GzipSource Denial of Service (Okio, 2023)
CVE-2023-3635 in Okio library where GzipSource does not properly handle exceptions when parsing malformed gzip buffers, leading to signed-to-unsigned conversion errors that cause denial of service.
Juniper Layer 2 Control Protocol (Juniper, 2025)
CVE-2025-30646 is a signed to unsigned conversion error in Juniper's Layer 2 Control Protocol (L2CP) implementation affecting network infrastructure.
Multiple System Libraries (Various, Historical)
Many historical vulnerabilities in libc implementations, OpenSSL, and other system libraries stemmed from unchecked conversions of error return values to unsigned sizes.
Tools to test/exploit
-
UBSan — detects implicit type conversions that change value.
-
Coverity — static analysis identifying dangerous signed/unsigned conversions.
-
PVS-Studio — detects suspicious type conversions.
CVE Examples
-
CVE-2023-3635 — Okio GzipSource signed to unsigned conversion DoS.
-
CVE-2025-30646 — Juniper L2CP signed to unsigned conversion error.
-
CVE-2021-21220 — Chrome V8 type confusion involving integer conversion.
References
-
MITRE. "CWE-195: Signed to Unsigned Conversion Error." https://cwe.mitre.org/data/definitions/195.html
-
CERT. "INT31-C. Ensure that integer conversions do not result in lost or misinterpreted data." https://wiki.sei.cmu.edu/confluence/display/c/INT31-C