CWE-190¶
Integer Overflow or Wraparound. [Numeric-Errors, Incorrect-Calculation, Top25-2024-23]
Required inputs: IR, StaticSemanticAnalysis
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
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¶
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions
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
abstract_interpretation_overflow_unrolling_level¶
abstract_interpretation_overflow_unrolling_level : int = 0
check_signed¶
check_signed : bool = True
check_unsigned¶
check_unsigned : bool = True
relevant_expressions¶
relevant_expressions : RelevantExpressions = 'const_expressions_only'
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.