Weakness reference
CWE-197

Numeric Truncation Error

Numeric truncation occurs when a program converts a number from one data type to another and loses precision or magnitude in the process. This happens most…

01Summary

Numeric truncation occurs when a program converts a number from one data type to another and loses precision or magnitude in the process. This happens most commonly when a larger numeric type (like a 64-bit integer) is cast to a smaller one (like a 32-bit integer) without checking whether the value fits. The truncated value may be incorrect enough to bypass security checks, cause buffer overflows, or trigger unexpected behavior.

02How It Happens

When a programming language converts a numeric value to a smaller data type, it discards the bits that don't fit. For example, converting a large integer to a smaller integer type silently drops the high-order bits. If the original value is larger than the target type can represent, the result is a wrapped or truncated value that bears no logical relationship to the original. This is especially dangerous in security-critical contexts: a size check that passes on a large value may fail silently when that value is truncated and used in a memory allocation, buffer operation, or access control decision.

03Real-World Impact

Truncation errors can lead to buffer overflows, integer underflows, or bypassed security checks. For instance, if a file size is read as a 64-bit value, checked against a limit, and then truncated to a 32-bit integer for allocation, an attacker could craft a file size that passes the check but allocates far less memory than needed, causing a heap overflow. Similarly, truncation in access control logic (e.g., user IDs or permission flags) can result in unauthorized access or privilege escalation.

04Vulnerable & Fixed Patterns

Vulnerable pattern
import struct

def process_file_size(size_bytes):
    # size_bytes is a 64-bit integer from untrusted input
    # Truncate to 32-bit for legacy API
    size_32bit = size_bytes & 0xFFFFFFFF
    
    # Allocate buffer based on truncated size
    buffer = bytearray(size_32bit)
    return buffer

# Attacker sends size = 0x100000001 (4GB + 1)
# Truncated to 0x00000001 (1 byte)
# Buffer allocated is 1 byte, but code expects 4GB

Why it's vulnerable:
The truncation silently converts a large value to a small one without validation. Code downstream assumes the buffer is large enough for the original size, causing an overflow.

Fixed pattern
import struct

def process_file_size(size_bytes, max_size=1000000):
    # Validate size before any conversion
    if size_bytes > max_size or size_bytes < 0:
        raise ValueError("File size out of acceptable range")
    
    # Now safe to use the value
    buffer = bytearray(size_bytes)
    return buffer
Vulnerable pattern
<?php
function allocate_buffer($user_size) {
    // Cast large integer to int (32-bit on 32-bit systems)
    $buffer_size = (int)$user_size;
    
    // Allocate based on truncated value
    $buffer = str_repeat("\x00", $buffer_size);
    return $buffer;
}

// If $user_size = 9999999999, it may truncate to a small value
// Buffer is allocated too small
?>

Why it's vulnerable:
Casting to int truncates the value on systems where int is smaller than the input. No validation ensures the cast is safe.

Fixed pattern
<?php
function allocate_buffer($user_size, $max_size = 1000000) {
    // Validate before any type conversion
    if (!is_numeric($user_size) || $user_size < 0 || $user_size > $max_size) {
        throw new Exception("Invalid buffer size");
    }
    
    // Safe to use
    $buffer = str_repeat("\x00", (int)$user_size);
    return $buffer;
}
?>

05Prevention Checklist

Validate numeric ranges before type conversion.
Check that a value fits within the target type's limits before casting.
Use explicit range checks in security-critical code.
For sizes, IDs, permissions, and access control decisions, validate against known safe bounds.
Prefer larger data types when possible.
Use 64-bit integers for sizes and offsets to reduce truncation risk.
Test with boundary values.
Include test cases with maximum and near-maximum values for each numeric type used.
Use compiler warnings.
Enable warnings for implicit narrowing conversions (e.g., -Wconversion in C/C++, or linters in Python/PHP).
Document numeric type assumptions.
Clearly specify the expected range and type for each numeric input, especially across API boundaries.

06Signs You May Already Be Affected

Look for unexpected crashes or memory errors when processing large files, IDs, or counts. Check logs for buffer overflow exceptions, segmentation faults, or out-of-bounds access errors that correlate with unusually large numeric inputs. Review code for explicit casts from larger to smaller integer types without preceding validation.

07Related Recent Vulnerabilities