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" |