CertC-INT30¶
Ensure that unsigned integer operations do not wrap
Required inputs: IR, StaticSemanticAnalysis
The C Standard, 6.2.5, paragraph 9 [ ISO/IEC 9899:2011], states
A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type.
This behavior is more informally called unsigned integer wrapping. Unsigned integer operations can wrap if the resulting value cannot be represented by the underlying representation of the integer. The following table indicates which operators can result in wrapping:
| Operator | Wrap | Operator | Wrap | Operator | Wrap | Operator | Wrap |
|---|---|---|---|---|---|---|---|
+ |
Yes |
-= |
Yes |
<< |
Yes | < |
No |
- |
Yes |
*= |
Yes | >> |
No | > |
No |
* |
Yes | /= |
No | & |
No | >= |
No |
/ |
No | %= |
No | | |
No | <= |
No |
% |
No |
<<= |
Yes | ^ |
No | == |
No |
++ |
Yes | >>= |
No | ~ |
No | != |
No |
-- |
Yes | &= |
No | ! |
No | && |
No |
= |
No | |= |
No | un + |
No | || |
No |
+= |
Yes | ^= |
No | un - |
Yes | ?: |
No |
The following sections examine specific operations that are susceptible to
unsigned integer wrap. When operating on integer types with less precision than
int, integer promotions are applied. The usual arithmetic
conversions may also be applied to (implicitly) convert operands to equivalent
types before arithmetic operations are performed. Programmers should understand
integer conversion rules before trying to implement secure arithmetic
operations. (See
INT02-C.
Understand integer conversion rules.)
Integer values must not be allowed to wrap, especially if they are used in any of the following ways:
- Integer operands of any pointer arithmetic, including array indexing
- The assignment expression for the declaration of a variable length array
- The postfix expression preceding square brackets
[]or the expression in square brackets[]of a subscripted designation of an element of an array object - Function arguments of type
size_torrsize_t(for example, an argument to a memory allocation function) - In security-critical code
The C Standard defines arithmetic on atomic integer types as read-modify-write operations with the same representation as regular integer types. As a result, wrapping of atomic unsigned integers is identical to regular unsigned integers and should also be prevented or detected.
Addition
Addition is between two operands of arithmetic type or between a pointer to an object type and an integer type. This rule applies only to addition between two operands of arithmetic type. (See ARR37-C. Do not add or subtract an integer to a pointer to a non-array object and ARR30-C. Do not form or use out-of-bounds pointers or array subscripts.)
Incrementing is equivalent to adding 1.
Noncompliant Code Example
This noncompliant code example can result in an unsigned integer wrap during
the addition of the unsigned operands
ui_a and
ui_b. If this behavior is
unexpected,
the resulting value may be used to allocate insufficient memory for a
subsequent operation or in some other manner that can lead to an exploitable
vulnerability.
void func(unsigned int ui_a, unsigned int ui_b) {
unsigned int usum = ui_a + ui_b;
/* ... */
}
Compliant Solution (Precondition Test)
This compliant solution performs a precondition test of the operands of the addition to guarantee there is no possibility of unsigned wrap:
#include <limits.h>
void func(unsigned int ui_a, unsigned int ui_b) {
unsigned int usum;
if (UINT_MAX - ui_a < ui_b) {
/* Handle error */
} else {
usum = ui_a + ui_b;
}
/* ... */
}
Compliant Solution (Postcondition Test)
This compliant solution performs a postcondition test to ensure that the result
of the unsigned addition operation
usum is not less than the first operand:
void func(unsigned int ui_a, unsigned int ui_b) {
unsigned int usum = ui_a + ui_b;
if (usum < ui_a) {
/* Handle error */
}
/* ... */
}
Subtraction
Subtraction is between two operands of arithmetic type, two pointers to qualified or unqualified versions of compatible object types, or a pointer to an object type and an integer type. This rule applies only to subtraction between two operands of arithmetic type. (See ARR36-C. Do not subtract or compare two pointers that do not refer to the same array, ARR37-C. Do not add or subtract an integer to a pointer to a non-array object, and ARR30-C. Do not form or use out-of-bounds pointers or array subscripts for information about pointer subtraction.)
Decrementing is equivalent to subtracting 1.
Noncompliant Code Example
This noncompliant code example can result in an unsigned integer wrap during
the subtraction of the unsigned operands
ui_a and
ui_b. If this behavior is unanticipated, it may lead to an
exploitable
vulnerability.
void func(unsigned int ui_a, unsigned int ui_b) {
unsigned int udiff = ui_a - ui_b;
/* ... */
}
Compliant Solution (Precondition Test)
This compliant solution performs a precondition test of the unsigned operands of the subtraction operation to guarantee there is no possibility of unsigned wrap:
void func(unsigned int ui_a, unsigned int ui_b) {
unsigned int udiff;
if (ui_a < ui_b){
/* Handle error */
} else {
udiff = ui_a - ui_b;
}
/* ... */
}
Compliant Solution (Postcondition Test)
This compliant solution performs a postcondition test that the result of the
unsigned subtraction operation
udiff is not greater than the minuend:
void func(unsigned int ui_a, unsigned int ui_b) {
unsigned int udiff = ui_a - ui_b;
if (udiff > ui_a) {
/* Handle error */
}
/* ... */
}
Multiplication
Multiplication is between two operands of arithmetic type.
Noncompliant Code Example
The Mozilla Foundation Security Advisory 2007-01 describes a heap buffer
overflow vulnerability in the Mozilla Scalable Vector Graphics (SVG)
viewer resulting from an unsigned integer wrap during the multiplication
of the
signed int value
pen->num_vertices and the
size_t value
sizeof(cairo_pen_vertex_t) [
VU#551436].
The
signed int operand is converted to
size_t prior to the multiplication operation so that the
multiplication takes place between two
size_t integers, which are unsigned. (See
INT02-C.
Understand integer conversion rules.)
pen->num_vertices = _cairo_pen_vertices_needed( gstate->tolerance, radius, &gstate->ctm ); pen->vertices = malloc( pen->num_vertices * sizeof(cairo_pen_vertex_t) );
The unsigned integer wrap can result in allocating memory of insufficient size.
Compliant Solution
This compliant solution tests the operands of the multiplication to guarantee that there is no unsigned integer wrap:
pen->num_vertices = _cairo_pen_vertices_needed(
gstate->tolerance, radius, &gstate->ctm
);
if (pen->num_vertices > SIZE_MAX / sizeof(cairo_pen_vertex_t)) {
/* Handle error */
}
pen->vertices = malloc(
pen->num_vertices * sizeof(cairo_pen_vertex_t)
);
Exceptions
INT30-C-EX1: Unsigned integers can exhibit modulo behavior (wrapping) when necessary for the proper execution of the program. It is recommended that the variable declaration be clearly commented as supporting modulo behavior and that each operation on that integer also be clearly commented as supporting modulo behavior.
INT30-C-EX2: Checks for wraparound can be omitted when it can be determined at compile time that wraparound will not occur. As such, the following operations on unsigned integers require no validation:
- Operations on two compile-time constants
- Operations on a variable and 0 (except division or remainder by 0)
- Subtracting any variable from its type's maximum; for example, any
unsigned intmay safely be subtracted fromUINT_MAX - Multiplying any variable by 1
- Division or remainder, as long as the divisor is nonzero
- Right-shifting any type maximum by any number no larger than the type
precision; for example,
UINT_MAX >> xis valid as long as0 <= x < 32(assuming that the precision ofunsigned intis 32 bits)
INT30-C-EX3. The left-shift operator takes two operands of
integer type. Unsigned left shift
<< can exhibit modulo behavior (wrapping). This
exception is provided because of common usage, because this behavior is usually
expected by the programmer, and because the behavior is well defined. For
examples of usage of the left-shift operator, see
INT34-C.
Do not shift an expression by a negative number of bits or by greater than or
equal to the number of bits that exist in the operand.
Risk Assessment
Integer wrap can lead to buffer overflows and the execution of arbitrary code by an attacker.
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| INT30-C | High | Likely | High | P9 | L2 |
Related Guidelines
| Taxonomy | Taxonomy item | Relationship |
|---|---|---|
| CERT C | INT02-C. Understand integer conversion rules | Prior to 2018-01-12: CERT: Unspecified Relationship |
| CERT C | ARR30-C. Do not form or use out-of-bounds pointers or array subscripts | Prior to 2018-01-12: CERT: Unspecified Relationship |
| CERT C | ARR36-C. Do not subtract or compare two pointers that do not refer to the same array | Prior to 2018-01-12: CERT: Unspecified Relationship |
| CERT C | ARR37-C. Do not add or subtract an integer to a pointer to a non-array object | Prior to 2018-01-12: CERT: Unspecified Relationship |
| CERT C | CON08-C. Do not assume that a group of calls to independently atomic methods is atomic | Prior to 2018-01-12: CERT: Unspecified Relationship |
| ISO/IEC TR 24772:2013 | Arithmetic Wrap-Around Error [FIF] | Prior to 2018-01-12: CERT: Unspecified Relationship |
| CWE 2.11 | CWE-190, Integer Overflow or Wraparound | 2016-12-02: CERT: Rule subset of CWE |
| CWE 2.11 | CWE-131 | 2017-05-16: CERT: Partial overlap |
| CWE 2.11 | CWE-191 | 2017-05-18: CERT: Partial overlap |
| CWE 2.11 | CWE-680 | 2017-05-18: CERT: Partial overlap |
Bibliography
| [ Bailey 2014] | Raising Lazarus - The 20 Year Old Bug that Went to Mars |
| [ Dowd 2006] | Chapter 6, "C Language Issues" ("Arithmetic Boundary Conditions," pp. 211-223) |
| [ ISO/IEC 9899:2011] | Subclause 6.2.5, "Types" |
| [ Seacord 2013b] | Chapter 5, "Integer Security" |
| [ Viega 2005] | Section 5.2.7, "Integer Overflow" |
| [ VU#551436] | |
| [ Warren 2002] | Chapter 2, "Basics" |
| [ Wojtczuk 2008] | |
| [ xorl 2009] | "CVE-2009-1385: Linux Kernel E1000 Integer Underflow" |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
unsigned_overflow |
Arithmetic computation may cause wrap-around |
None |
False |
unsigned_underflow |
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 = True
abstract_interpretation_overflow_unrolling_level¶
abstract_interpretation_overflow_unrolling_level : int = 0
check_signed¶
check_signed : bool = False
check_unsigned¶
check_unsigned : bool = True
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:
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.