If an integer expression is compared to, or assigned to, a larger integer size, then that integer expression should be evaluated in that larger size by explicitly casting one of the operands.
Non-Compliant Code Example
This code example is non-compliant 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 integer overflow by assigning the value UINT_MAX to max and testing if length + BLOCK_HEADER_SIZE > max. Because length is declared as size_t, however, the addition is performed as a 32-bit operation and can result in an integer overflow. The comparison with max in this example will always test false. If an overflow occurs, malloc() will allocate insufficient space for mBlock which could lead to a subsequent buffer overflow.
enum { BLOCK_HEADER_SIZE = 16 };
unsigned long long max = UINT_MAX;
void *AllocateBlock(size_t length) {
struct memBlock *mBlock;
if (length + BLOCK_HEADER_SIZE > max) return NULL;
mBlock = malloc(length + BLOCK_HEADER_SIZE);
if (!mBlock) return NULL;
/* fill in block header and return data portion */
return mBlock;
}
Compliant Solution
In this compliant solution, the length operand is upcast to unsigned long long ensuring that the addition takes place in this size.
enum { BLOCK_HEADER_SIZE = 16 };
unsigned long long max = UINT_MAX;
void *AllocateBlock(size_t length) {
struct memBlock *mBlock;
if ((unsigned long long)length + BLOCK_HEADER_SIZE > max) return NULL;
mBlock = malloc(length + BLOCK_HEADER_SIZE);
if (!mBlock) return NULL;
/* fill in block header and return data portion */
return mBlock;
}
This test for integer overflow is only effective 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 value, the result of the addition operation may not be representable as an unsigned long long value.
Non-Compliant Code Example
In this non-compliant code example, the programmer attempts to prevent integer overflow by allocating an unsigned long long integer called alloc and assigning it the result from cBlocks * 16.
unsigned long long max = UINT_MAX;
...
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 this non-compliant code example. The first problem is that this code assumes an implementation where unsigned long long has a least four 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 C99, multiplying two 32-bit numbers in this context must yield a 32-bit result. Any integer overflow 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.
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 overflows unless the unsigned long long type is at least four bits larger than size_t.
Risk Assessment
Failure to cast integers before comparing or assigning 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 |
|---|---|---|---|---|---|
INT35-C |
3 (high) |
3 (likely) |
2 (medium) |
P18 |
L1 |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
[[Dowd 06]] Chapter 6, "C Language Issues"
[[ISO/IEC 9899-1999]] Section 6.3.1, "Arithmetic operands"
[[Seacord 05a]] Chapter 5, "Integer Security"
INT33-C. Ensure that division and modulo operations do not result in divide-by-zero errors 04. Integers (INT) INT36-C. Do not shift a negative number of bits or more bits than exist in the operand