CWE-190

Integer Overflow or Wraparound. [Numeric-Errors, Incorrect-Calculation, Top25-2024-23]

Required inputs: IR, StaticSemanticAnalysis

The product performs a calculation that can produce an integer overflow or wraparound, when the logic assumes that the resulting value will always be larger than the original value. This can introduce other weaknesses when the calculation is used for resource management or execution control. An integer overflow or wraparound occurs when an integer value is incremented to a value that is too large to store in the associated representation. When this occurs, the value may wrap to become a very small or negative number. While this may be intended behavior in circumstances that rely on wrapping, it can have security consequences if the wrap is unexpected. This is especially the case if the integer overflow can be triggered using user-supplied inputs. This becomes security-critical when the result is used to control looping, make a security decision, or determine the offset or size in behaviors such as memory allocation, copying, concatenation, etc.
Demonstrative Examples Functional Areas
Example 1

The following image processing code allocates a table for images.

Example Language:C
    img_t table_ptr; /*struct containing img data, 10kB each*/
    int num_imgs;
    ...
    num_imgs = get_num_imgs();
    table_ptr = (img_t*)malloc(sizeof(img_t)*num_imgs);
    ...

This code intends to allocate a table of size num_imgs, however as num_imgs grows large, the calculation determining the size of the list will eventually overflow (CWE-190). This will result in a very small list to be allocated instead. If the subsequent code operates on the list as if it were num_imgs long, it may result in many types of out-of-bounds problems (CWE-119).

Example 2

The following code excerpt from OpenSSH 3.3 demonstrates a classic case of integer overflow:

Example Language:C
    nresp = packet_get_int();
    if (nresp > 0) {
        response = xmalloc(nresp*sizeof(char*));
        for (i = 0; i < nresp; i++) response[i] = packet_get_string(NULL);
    }

If nresp has the value 1073741824 and sizeof(char*) has its typical value of 4, then the result of the operation nresp*sizeof(char*) overflows, and the argument to xmalloc() will be 0. Most malloc() implementations will happily allocate a 0-byte buffer, causing the subsequent loop iterations to overflow the heap buffer response.

Example 3

Integer overflows can be complicated and difficult to detect. The following example is an attempt to show how an integer overflow may lead to undefined looping behavior:

Example Language:C
    short int bytesRec = 0;
    char buf[SOMEBIGNUM];

    while(bytesRec < MAXGET) {
        bytesRec += getFromInput(buf+bytesRec);
    }

In the above case, it is entirely possible that bytesRec may overflow, continuously creating a lower number than MAXGET and also overwriting the first MAXGET-1 bytes of buf.

Example 4

In this example the method determineFirstQuarterRevenue is used to determine the first quarter revenue for an accounting/business application. The method retrieves the monthly sales totals for the first three months of the year, calculates the first quarter sales totals from the monthly sales totals, calculates the first quarter revenue based on the first quarter sales, and finally saves the first quarter revenue results to the database.

Example Language:C
    #define JAN 1
    #define FEB 2
    #define MAR 3

    short getMonthlySales(int month) {...}

    float calculateRevenueForQuarter(short quarterSold) {...}

    int determineFirstQuarterRevenue() {
        // Variable for sales revenue for the quarter
        float quarterRevenue = 0.0f;

        short JanSold = getMonthlySales(JAN); /* Get sales in January */
        short FebSold = getMonthlySales(FEB); /* Get sales in February */
        short MarSold = getMonthlySales(MAR); /* Get sales in March */

        // Calculate quarterly total
        short quarterSold = JanSold + FebSold + MarSold;

        // Calculate the total revenue for the quarter
        quarterRevenue = calculateRevenueForQuarter(quarterSold);

        saveFirstQuarterRevenue(quarterRevenue);

        return 0;
    }

However, in this example the primitive type short int is used for both the monthly and the quarterly sales variables. In C the short int primitive type has a maximum value of 32768. This creates a potential integer overflow if the value for the three monthly sales adds up to more than the maximum value for the short int primitive type. An integer overflow can lead to data corruption, unexpected behavior, infinite loops and system crashes. To correct the situation the appropriate primitive type should be used, as in the example below, and/or provide some validation mechanism to ensure that the maximum value for the primitive type is not exceeded.

Example Language:C
    ...
    float calculateRevenueForQuarter(long quarterSold) {...}

    int determineFirstQuarterRevenue() {
        ...
        // Calculate quarterly total
        long quarterSold = JanSold + FebSold + MarSold;

        // Calculate the total revenue for the quarter
        quarterRevenue = calculateRevenueForQuarter(quarterSold);

        ...
    }

Note that an integer overflow could also occur if the quarterSold variable has a primitive type long but the method calculateRevenueForQuarter has a parameter of type short.

Demonstrative Examples Functional Areas
  • Number Processing
  • Memory Management
  • Counters
Excerpts from CWE [https://cwe.mitre.org], Copyright (C) 2006-2026, the MITRE Corporation. See section 9.4. "3rd-Party Licenses" in the documentation for full details.

Possible Messages

Key

Text

Severity

Disabled

cast_overflow

Cast on result of arithmetic computation may cause overflow

None

False

certain_shift_amount_negative

Shift by a negative bit count (undefined behavior)

None

False

certain_shift_amount_too_large

Shift by the integer width or more (undefined behavior)

None

False

certain_shift_right_negative

Right shift with negative left-hand-side

None

False

overflow

Arithmetic computation may cause overflow

None

False

shift_amount_negative

Possible shift by a negative bit count (undefined behavior)

None

False

shift_amount_too_large

Possible shift by the integer width or more (undefined behavior)

None

False

shift_right_negative

Possible right shift with negative left-hand-side

None

False

static_cast_overflow

Cast on result of arithmetic computation may cause overflow

None

False

static_cast_underflow

Cast on result of arithmetic computation may cause underflow

None

False

static_cast_underflow_minus_1

Casting -1 to an unsigned type causes underflow

None

False

static_overflow

Arithmetic computation may cause overflow

None

False

static_underflow

Arithmetic computation may cause underflow

None

False

unsigned_cast_overflow

Cast on result of arithmetic computation may cause wrap-around

None

False

unsigned_overflow

Arithmetic computation may cause wrap-around

None

False

Options

abstract_interpretation_maximal_tracked_array_index

abstract_interpretation_maximal_tracked_array_index : int = 10

The number of explicit indices in array expressions per routine tracked by the "symbolic expression analysis". For example, consider the following program.

extern signed char a[6];
int main()
{
    if (a[2] < 0)
    {
        a[2]++;
    }
    if (a[3] < 0)
    {
        a[3]++;
    }
    if (a[4] < 0)
    {
        a[4]++;
    }
    return 0;
}

If the value of this option is set to 2, the first two array index expressions encountered in the routine are tracked. Hence, the analysis can use the facts a[2] < 0 and a[3] < 0 to infer that a[2]++ and a[3]++ do not overflow, but it will not track the third array access in this routine.

A higher value of the option can cause more consumption of memory and time for the analysis.

 

abstract_interpretation_overflow

abstract_interpretation_overflow : bool = False

Use abstract-interpretation-based "symbolic expression analysis" as additional postprocessing step.
 

abstract_interpretation_overflow_unrolling_level

abstract_interpretation_overflow_unrolling_level : int = 0

How many levels of conditions are traversed to compute additional constraints for the "symbolic expression analysis".
 

check_signed

check_signed : bool = True

Whether issues for signed integer operations should be reported. For casts including implicit conversions, the target type of the cast is used.
 

check_unsigned

check_unsigned : bool = True

Whether wrap-around for unsigned integer operations should be reported. For casts including implicit conversions, the target type of the cast is used.
 

relevant_expressions

relevant_expressions : RelevantExpressions = 'const_expressions_only'

Which (const / constant) expressions should be considered.

Note: this is only relevant for the purely static parts of the analysis. The StaticSemanticAnalysis-based checks for runtime errors will be performed independently.

 

suppress_well_defined_findings

suppress_well_defined_findings : SuppressionMode = 'NONE'

Some overflows have well-defined semantics in all C/C++ standard versions. The typical example is UINT_MAX+1 which is well-defined as 0 via wraparound. This differs from INT_MAX+1 which is either undefined or implementation-defined depending on the considered standard version. Most CPUs will compute INT_MIN but this wraparound is not guaranteed by any C/C++ standard.

Both cases are overflows and are reported by this rule. However, one might want to suppress messages for the well-defined cases. To suppress these activate this option.

Different C and C++ standard versions differ in what is well-defined, implementation-defined, or undefined. Luckily, if we only consider well-defined and do not discern between implementation-defined and undefined, we end up with only two groups: pre-C++20 and since-C++20.

 

Option Types

These types are used by options listed above:

RelevantExpressions

An enumeration.
 

none

No (additional) checks for overflows in const-expressions or compile time constant expressions.

const_expressions_only

Whether the analysis should statically check const-expressions (i.e., const variables and literals) that might have been reduced to a literal during compilation.

const_and_compile_time_constant

Whether the analysis should statically check const-expressions (i.e., const variables and literals) as well as compile-time constant expressions (i.e., preprocessor defines, constexprs or literals) that might have been reduced to a literal during compilation.

SuppressionMode

An enumeration.
 

NONE

Suppress nothing.

PRE_CPP2020

Suppress findings that are well-defined before C++20. These are:

  • Over- and underflows of unsigned integers during addition, subtraction, and multiplication
  • Conversions from unsigned to unsigned integers
  • Wrap-around caused by left-shifting of unsigned integer

CPP2020

Suppress findings that are well-defined since C++20. These are:

  • Over- and underflows of unsigned integers during addition, subtraction, and multiplication
  • Conversions between signed and unsigned integers
  • Wrap-around caused by left-shifting
  • Shifting negative integers

Surprising mechanics of C++20 signed narrow integers

Since C++20, casts between signed and unsigned are defined as two-complement wrap-around. Overflows of signed integers are still undefined behavior and are reported by this rule. But, due to integer promotion rules, certain expressions are computed using wider integer types, which can lead to the false impression that this is no longer the case, because no overflow findings are reported there.

Suppose, that the code is compiled on a platform where short is smaller than or equal to half the size of an int. Very commonly the sizes are 2 and 4. This assumption is thus true for many platforms.

In this case, narrow signed integer types such as short or signed char are first implicitly promoted to int before the arithmetic operation is executed. Because of this promotion, the actual operation does not overflow and is thus well-defined. After the operation, an implicit cast is performed to the narrower type. This cast is well-defined in C++20 as wrapping around.

Consider the following snippet:

    static_assert(sizeof(short) == 2);
    static_assert(sizeof(int) == 4);
    short a = 0x1000;
    short b = 0x1001;
    short c = a*b;
C++20 defines c as 0x1000. The reason is that a*b is implicitly promoted to static_cast<int> (a)*static_cast<int>(b). After the promotion, the multiplication does not overflow and yields a well-defined 0x1001000. This number is then implicitly cast to 0x1000 which is also a well-defined operation.

An analogous effect can be observed for signed short addition and multiplication. Another effect is that it is well-defined to shift by up to as many bits as int has even if the shifted integer has fewer bits.

DERIVE_FROM_IR

Derive the language version from the IR compilation flags and suppress findings accordingly.