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.
This 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 any wrapping occurs, malloc() will allocate insufficient space for mBlock, which can lead to a subsequent buffer overflow.
#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; }
/* Fill in block header and return data portion */
return mBlock;
}
|
Some compilers will diagnose this condition.
In this compliant solution, the length operand is upcast to unsigned long long, ensuring that the addition takes place in this size:
#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; }
/* Fill 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 64-bit unsigned values, the result of the addition operation may not be representable as an unsigned long long value.
In this compliant solution, length is subtracted from SIZE_MAX, ensuring that wrapping cannot occur. See INT30-C. Ensure that unsigned integer operations do not wrap.
#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; }
/* Fill in block header and return data portion */
return mBlock;
}
|
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:
#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;
}
|
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 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 C, 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.
In this compliant solution, the cBlocks operand is upcast to unsigned long long, ensuring that the multiplication takes place in this size:
#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 4 bits larger than size_t.
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.
#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 */
}
} |
size_t)To ensure the comparison is properly performed, the return value of mbstowcs() should be compared against -1 cast to type size_t:
#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 */
}
} |
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 | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
INT18-C | high | likely | medium | P18 | L1 |
Tool | Version | Checker | Description |
|---|---|---|---|
| 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) |
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
| SEI CERT C++ Coding Standard | 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 | CWE-681, Incorrect conversion between numeric types CWE-190, Integer overflow (wrap or wraparound) |
| [Dowd 2006] | Chapter 6, "C Language Issues" |
| [Seacord 2013] | Chapter 5, "Integer Security" |