If an integer expression involving an operation is compared to , or assigned to , a larger integer size, that integer expression should be evaluated in that larger size by explicitly casting one of the operands.
Noncompliant Code Example
This noncompliant code example is noncompliant on systems where size_t is an unsigned 32-bit value and long long is a 64-bit value. In this example, the programmer tests for wrapping by comparing SIZE_MAX to length + BLOCK_HEADER_SIZE. Because length is declared as size_t, the addition is performed as a 32-bit operation and can result in wrapping. The comparison with SIZE_MAX will always test false. If an any wrapping occurs, malloc() will allocate insufficient space for mBlock, which can lead to a subsequent buffer overflow.
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdlib.h> #include <stdint.h> /* For SIZE_MAX */ enum { BLOCK_HEADER_SIZE = 16 }; void *AllocateBlock(size_t length) { struct memBlock *mBlock; if (length + BLOCK_HEADER_SIZE > (unsigned long long)SIZE_MAX) return NULL; mBlock = (struct memBlock *)malloc( length + BLOCK_HEADER_SIZE ); if (!mBlock) { return NULL; } /* fillFill in block header and return data portion */ return mBlock; } |
Some compilers will diagnose this condition.
Compliant Solution (Upcast)
In this compliant solution, the length operand is upcast to unsigned long long, ensuring that the addition takes place in this size.:
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdlib.h> #include <stdint.h> enum { BLOCK_HEADER_SIZE = 16 }; void *AllocateBlock(size_t length) { struct memBlock *mBlock; if ((unsigned long long)length + BLOCK_HEADER_SIZE > SIZE_MAX) { return NULL; } mBlock = (struct memBlock *)malloc( length + BLOCK_HEADER_SIZE ); if (!mBlock) { return NULL; } /* fillFill in block header and return data portion */ return mBlock; } |
This test for wrapping is effective only when the sizeof(unsigned long long) > sizeof(size_t). If both size_t and unsigned long long types are represented as a 64-bit unsigned valuevalues, the result of the addition operation may not be representable as an unsigned long long value.
Compliant Solution (Rearrange Expression)
In this compliant solution, length is subtracted from SIZE_MAX, ensuring that wrapping cannot occur. See rule INT30-C. Ensure that unsigned integer operations do not wrap.
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdlib.h> #include <stdint.h> enum { BLOCK_HEADER_SIZE = 16 }; void *AllocateBlock(size_t length) { struct memBlock *mBlock; if (SIZE_MAX - length < BLOCK_HEADER_SIZE) return NULL; mBlock = (struct memBlock *)malloc( length + BLOCK_HEADER_SIZE ); if (!mBlock) { return NULL; } /* fillFill in block header and return data portion */ return mBlock; } |
Noncompliant Code Example
In this noncompliant code example, the programmer attempts to prevent wrapping by allocating an unsigned long long integer called alloc and assigning it the result from cBlocks * 16.:
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdlib.h> #include <limits.h> void *AllocBlocks(size_t cBlocks) { if (cBlocks == 0) { return NULL; } unsigned long long alloc = cBlocks * 16; return (alloc < UINT_MAX) ? malloc(cBlocks * 16) : NULL; } |
There are two separate problems with Two problems occur in this noncompliant code example. The first problem is that this code assumes an implementation where unsigned long long has at least four 4 more bits than size_t. The second problem, assuming an implementation where size_t is a 32-bit value and unsigned long long is represented by a 64-bit value, is that to be compliant with C99C, multiplying two 32-bit numbers in this context must yield a 32-bit result. Any wrapping resulting from this multiplication will remain undetected by this code, and the expression alloc < UINT_MAX will always be true.
Compliant Solution
In this compliant solution, the cBlocks operand is upcast to unsigned long long, ensuring that the multiplication takes place in this size.:
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdlib.h> #include <assert.h> #include <limits.h> static_assert( CHAR_BIT * sizeof(unsigned long long) >= CHAR_BIT * sizeof(size_t) + 4, "Unable to detect wrapping after multiplication" ); void *AllocBlocks(size_t cBlocks) { if (cBlocks == 0) return NULL; unsigned long long alloc = (unsigned long long)cBlocks * 16; return (alloc < UINT_MAX) ? malloc(cBlocks * 16) : NULL; } |
Note that this code does not prevent wrapping unless the unsigned long long type is at least four least 4 bits larger than size_t.
Noncompliant Code Example (size_t)
The mbstowcs() function converts a multibyte string to a wide character string, returning the number of characters converted. If an invalid multibyte character is encountered, mbstowcs() returns (size_t)(-1). Depending on how size_t is implemented, comparing the return value of mbstowcs() to signed integer literal -1 may not evaluate as expected.
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdlib.h>
void func(wchar_t *pwcs, const char *restrict s, size_t n) {
size_t count_modified = mbstowcs(pwcs, s, n);
if (count_modified == -1) {
/* Handle error */
}
} |
Compliant Solution (size_t)
To ensure the comparison is properly performed, the return value of mbstowcs() should be compared against -1 cast to type size_t:
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdlib.h>
void func(wchar_t *pwcs, const char *restrict s, size_t n) {
size_t count_modified = mbstowcs(pwcs, s, n);
if (count_modified == (size_t)-1) {
/* Handle error */
}
} |
Risk Assessment
Failure to cast integers before comparing or assigning them to a larger integer size can result in software vulnerabilities that can allow the execution of arbitrary code by an attacker with the permissions of the vulnerable process.
Rule | Severity | Likelihood | Detectable |
|---|
Repairable | Priority | Level |
|---|
INT18-C |
High | Likely |
No |
Yes | P18 | L1 |
Automated Detection
Tool | Version | Checker | Description |
|---|
| Section |
|---|
Fortify SCA |
| Section |
|---|
V. 5.0 |
| Section |
|---|
can detect violations of this rule with CERT C Rule Pack |
| Section |
|---|
Compass/ROSE |
| Astrée |
| Supported, but no explicit checker | |||||||
| CodeSonar |
| LANG.TYPE.AWID | Expression value widened by assignment | ||||||
| Compass/ROSE | Can |
detect violations of this rule. It should look for patterns of |
|
| |||||||||
| Coverity | 6.5 | OVERFLOW_BEFORE_WIDEN | Fully implemented | ||||||
| Helix QAC |
| C1890, C1891, C1892, C1893, C1894, C1895, C4490, C4491, C4492 | |||||||
| Klocwork |
| PORTING.CAST.SIZE | |||||||
| LDRA tool suite |
| 452 S | Partially implemented | ||||||
| Parasoft C/C++test |
| CERT_C-INT18-a | Avoid possible integer overflow in expressions in which the result is assigned to a variable of a wider integer type | ||||||
| Polyspace Bug Finder |
| Checks for integer overflow and unsigned integer overflow (rec. fully covered) |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
...
...
| INT35-CPP. Evaluate integer expressions in a larger size before comparing or assigning to that size | |
| ISO/IEC |
...
| TR 24772:2013 | Numeric Conversion Errors |
...
| [FLC] |
| MITRE CWE |
...
...
| Incorrect |
...
| conversion between numeric types CWE-190, |
...
| Integer |
...
| overflow ( |
...
| wrap or |
...
| wraparound) |
...
Bibliography
...
| [ |
...
...
...
| ] | Chapter 6, |
...
| "C |
...
| Language |
...
| Issues" |
...
...
| 2013] | Chapter 5, |
...
| "Integer |
...
| Security" |
...
INT34-C. Do not shift a negative number of bits or more bits than exist in the operand 04. Integers (INT) 05. Floating Point (FLP)