Numeric Truncation Error

Description

Numeric Truncation Error occurs when a numeric value is converted to a smaller data type that cannot represent the full range of the original value, resulting in loss of significant bits and an incorrect value. For example, casting a 32-bit integer containing 0x12345678 to a 16-bit integer produces 0x5678, losing the upper bits. When truncated values are used for security-critical operations like buffer allocation, loop iteration, or access control decisions, the resulting smaller value can enable buffer overflows, infinite loops, or security bypasses.

Risk

Numeric truncation errors can have severe security implications. When a large size value is truncated to a smaller type, a small buffer is allocated while subsequent operations assume the original large size—causing buffer overflow. Truncation can bypass security checks that use the larger value while operations use the truncated value. Attackers craft inputs with specific bit patterns that produce dangerous truncated values. This vulnerability has been historically under-reported but affects critical software across all platforms. Recent research has discovered numerous truncation vulnerabilities in open-source projects.

Solution

Avoid implicit narrowing conversions between integer types. When conversion is necessary, verify the value is within the target type's range before converting. Use compiler warnings for implicit narrowing (-Wconversion, -Wnarrowing). Apply static analysis to identify truncation points. Use types large enough for the data being processed. In security-critical code, use explicit range validation before any narrowing cast. Consider using SafeInt or similar libraries that detect truncation.

Common Consequences

ImpactDetails
IntegrityScope: Memory Corruption

Truncated size values lead to undersized allocations and subsequent buffer overflows.
Access ControlScope: Security Bypass

Truncation can cause large values to pass checks that would reject them in their original form.
AvailabilityScope: Denial of Service

Truncation in loop counters can cause infinite loops; in allocations can cause resource exhaustion.

Example Code + Solution Code

Vulnerable Code

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

// VULNERABLE: 32-bit to 16-bit truncation
void allocate_buffer(uint32_t requested_size) {
    uint16_t size = requested_size;  // Truncates to 16 bits

    // If requested_size = 0x10100, size = 0x0100 (256)
    char *buffer = malloc(size);  // Small allocation

    // Operations use original size
    read_data(buffer, requested_size);  // Massive overflow!
}

// VULNERABLE: Truncation bypasses length check
void copy_with_limit(char *dest, char *src, size_t len) {
    unsigned short limit = len;  // Truncates large len values

    // len = 65537 becomes limit = 1
    if (limit > MAX_SIZE) {
        return;  // Small limit passes check
    }

    memcpy(dest, src, len);  // Uses original large len!
}

// VULNERABLE: Loop counter truncation
void process_elements(uint32_t count) {
    uint8_t i;

    // count = 260 truncates, i only goes 0-255 then wraps
    for (i = 0; i < count; i++) {
        process_element(i);  // Infinite loop if count > 255
    }
}

// VULNERABLE: Return value truncation
int16_t get_file_size(const char *filename) {
    struct stat st;
    stat(filename, &st);

    // st.st_size is 64-bit, truncates to 16-bit return
    return st.st_size;  // Large files return wrong size
}

Fixed Code

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

// SAFE: Validate before truncation
int allocate_buffer_safe(size_t requested_size) {
    // Validate size is reasonable
    if (requested_size > MAX_BUFFER_SIZE) {
        return -1;
    }

    // Use appropriate type (no truncation needed)
    char *buffer = malloc(requested_size);
    if (!buffer) return -1;

    read_data(buffer, requested_size);
    return 0;
}

// SAFE: Use consistent types
void copy_with_limit_safe(char *dest, size_t dest_size,
                          const char *src, size_t len) {
    // Use size_t throughout - no truncation
    if (len > MAX_SIZE || len > dest_size) {
        return;
    }

    memcpy(dest, src, len);
}

// SAFE: Validate range before narrowing
int process_elements_safe(uint32_t count) {
    // Check if count fits in loop variable type
    if (count > UINT8_MAX) {
        // Either reject or use appropriate type
        return -1;
    }

    uint8_t limit = (uint8_t)count;  // Safe after validation

    for (uint8_t i = 0; i < limit; i++) {
        process_element(i);
    }
    return 0;
}

// Alternative: Use larger loop variable
void process_elements_large(size_t count) {
    for (size_t i = 0; i < count; i++) {
        process_element(i);
    }
}

// SAFE: Return appropriate type
int64_t get_file_size_safe(const char *filename) {
    struct stat st;
    if (stat(filename, &st) != 0) {
        return -1;
    }

    // Return full size without truncation
    return (int64_t)st.st_size;
}

// Helper: Safe narrowing with validation
bool safe_narrow_to_u16(size_t value, uint16_t *result) {
    if (value > UINT16_MAX) {
        return false;  // Would truncate
    }
    *result = (uint16_t)value;
    return true;
}

Exploited in the Wild

Windows Shell Privilege Escalation (Microsoft, 2025)

CVE-2025-49679 is a numeric truncation error in the Windows Shell component allowing local attackers to escalate privileges. CVSS 7.8 with high impact on confidentiality, integrity, and availability.

Multiple Open Source Projects (OSS-Fuzz, 2023-2024)

Research using OSS-Sydr-Fuzz discovered 12 new numeric truncation errors across 5 open-source projects, demonstrating the prevalence of this under-studied vulnerability class.

Historical: Integer Truncation in Protocol Handlers (Various)

Multiple historical vulnerabilities in network protocol handlers (HTTP, DNS, SMB) stemmed from truncating 32-bit length fields to 16-bit types.


Tools to test/exploit

  • UBSan — detects implicit truncation with -fsanitize=implicit-conversion.

  • Coverity — static analysis identifying narrowing conversions.

  • Sydr — fuzzer with symbolic execution for finding truncation bugs.


CVE Examples

  • CVE-2025-49679 — Windows Shell numeric truncation privilege escalation.

  • CVE-2016-2324 — Git integer truncation in path handling.

  • CVE-2019-11477 — Linux kernel SACK integer overflow with truncation.


References

  1. MITRE. "CWE-197: Numeric Truncation Error." https://cwe.mitre.org/data/definitions/197.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

  3. OpenSSF. "Secure Coding Guide for Python: CWE-197." https://best.openssf.org/Secure-Coding-Guide-for-Python/CWE-664/CWE-197/