Unexpected Sign Extension

Description

Unexpected Sign Extension occurs when a signed integer value is converted to a larger integer type, and the sign bit is extended to fill the additional bits. When a negative signed value (with the high bit set) is extended to a larger type, it becomes a very large positive number if subsequently interpreted as unsigned, or remains a large negative number. This can produce unexpected values that bypass security checks, cause buffer overflows, or lead to other memory corruption issues when the extended value is used for sizes, indices, or loop counters.

Risk

Sign extension vulnerabilities are particularly dangerous because they can silently transform small negative values into enormous positive values. When a negative char (-1) is sign-extended to an int and then cast to size_t, it becomes a value near SIZE_MAX. If this value is used for buffer allocation or as a loop bound, catastrophic buffer overflows result. These vulnerabilities are subtle and easy to miss during code review. Real-world exploits have demonstrated that sign extension bugs in document parsers, protocol handlers, and media libraries can enable arbitrary code execution.

Solution

Avoid using signed integers for values that should never be negative, such as sizes, lengths, and indices. When converting between integer types of different sizes, explicitly check value ranges before conversion. Use explicit casts that make the intent clear. Validate that signed values are non-negative before using them in size calculations. Be particularly careful when char values (which may be signed by default) are used in array indexing. Use unsigned char or explicitly cast to unsigned when processing byte data. Enable compiler warnings for implicit conversions between signed and unsigned types.

Common Consequences

ImpactDetails
IntegrityScope: Memory Corruption

Sign-extended values used as sizes or indices cause massive buffer overflows.
Access ControlScope: Security Bypass

Sign extension can cause negative values to pass length checks when interpreted as large positive unsigned values.
AvailabilityScope: Denial of Service

Massive memory allocations from sign-extended sizes cause resource exhaustion.

Example Code + Solution Code

Vulnerable Code

#include <stdlib.h>
#include <string.h>

// VULNERABLE: Sign extension from char to int to size_t
void process_byte(char len_byte) {
    // If len_byte is -1 (0xFF), sign extension makes it
    // 0xFFFFFFFF (huge positive) when cast to size_t
    size_t length = len_byte;

    char *buffer = malloc(length);  // Enormous allocation
    // ...
}

// VULNERABLE: Sign extension bypasses length check
int copy_data(char *dest, char *src, short src_len) {
    // src_len could be negative
    if (src_len > MAX_SIZE) {  // Negative passes this check!
        return -1;
    }

    // Sign extended to size_t, becomes huge positive
    memcpy(dest, src, src_len);  // Massive overflow
    return 0;
}

// VULNERABLE: Signed char as array index
char lookup_table[256];

char get_lookup(char index) {
    // If index is negative (e.g., -50), this reads before array
    // When converted to int for indexing, becomes negative offset
    return lookup_table[index];  // Under-read!
}

// VULNERABLE: Pascal-style string length
void copy_pascal_string(char *dest, char *src) {
    // First byte is length (signed char)
    char len = *src;  // Could be negative

    // Sign extension: len = -1 becomes 0xFFFFFFFF
    memcpy(dest, src + 1, len);  // Massive overflow
}

Fixed Code

#include <stdlib.h>
#include <string.h>
#include <stdint.h>

// SAFE: Use unsigned type, validate range
void process_byte_safe(unsigned char len_byte) {
    // unsigned char: 0-255, no sign extension
    size_t length = len_byte;

    if (length == 0 || length > MAX_LENGTH) {
        return;
    }

    char *buffer = malloc(length);
    // ...
}

// SAFE: Validate signed value before use
int copy_data_safe(char *dest, size_t dest_size,
                   const char *src, int16_t src_len) {
    // Check for negative AND upper bound
    if (src_len < 0 || (size_t)src_len > MAX_SIZE) {
        return -1;
    }

    // After validation, cast is safe
    size_t copy_len = (size_t)src_len;

    if (copy_len > dest_size) {
        return -1;
    }

    memcpy(dest, src, copy_len);
    return 0;
}

// SAFE: Use unsigned char for byte indexing
char lookup_table[256];

char get_lookup_safe(unsigned char index) {
    // unsigned char: always 0-255, valid index
    return lookup_table[index];
}

// Alternative: explicit masking
char get_lookup_masked(char index) {
    // Mask to ensure positive index
    unsigned char safe_index = (unsigned char)index;
    return lookup_table[safe_index];
}

// SAFE: Validate Pascal string length
int copy_pascal_string_safe(char *dest, size_t dest_size,
                            const unsigned char *src, size_t src_size) {
    if (src_size == 0) return -1;

    // Read length as unsigned
    unsigned char len = src[0];

    // Validate length against both source and destination
    if (len > src_size - 1 || len > dest_size) {
        return -1;
    }

    memcpy(dest, src + 1, len);
    dest[len] = '\0';
    return 0;
}

Exploited in the Wild

SoftMaker Office TextMaker (SoftMaker, 2021)

CVE-2021-40411 is a sign extension vulnerability in SoftMaker Office 2021's TextMaker document parsing. A specially crafted document causes the parser to sign-extend a loop termination length, resulting in the loop index being used to write outside heap buffer bounds.

CODESYS Industrial Control (CODESYS, Multiple)

Multiple CODESYS products contained sign extension vulnerabilities where remote attackers could craft requests causing unexpected sign extension, resulting in denial of service or memory overwrite in industrial control systems.

Historical: BSD Network Stack (BSD, 1990s)

Classic sign extension vulnerabilities in BSD network stack implementations affected TCP/IP processing and socket operations, demonstrating the long history of this vulnerability class.


Tools to test/exploit

  • UBSan — detects implicit conversions that change value.

  • Coverity — static analysis identifying sign extension issues.

  • PVS-Studio — detects suspicious type conversions and sign extension.


CVE Examples

  • CVE-2021-40411 — SoftMaker Office sign extension leading to heap overflow.

  • CVE-2022-33260 — CODESYS sign extension causing memory corruption.

  • CVE-2017-7308 — Linux kernel packet socket sign extension.


References

  1. MITRE. "CWE-194: Unexpected Sign Extension." https://cwe.mitre.org/data/definitions/194.html

  2. 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