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 Coding Example
In this non-compliant code exapmle, the programmer attempts to prevent against integer by ensuring that length + BLOCK_HEADER_SIZE > max. Because length is declared as size_t, the addition is performed as a 32 bit operation
unsigned long long max = MAX_BLOCK;
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;
}
Non-Compliant Coding Example
In this non-compliant code exapmle, the programmer attempts to prevent against integer overflow by allocating an unsigned long long integer called alloc and assigning it the result from cBlocks * 16.
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 a couple of problems with this code. The first problem is that this code assumes an implementation where unsigned long long has a least twice the number of bits as 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 the 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 will not prevent overflows unless the unsigned long long type is at least twice the length of 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 (probable) |
2 (medium) |
P18 |
L1 |
References
[[ISO/IEC 9899-1999]] Sections 6.3.1, "Arithmetic operands"
[[Seacord 05a]] Seacord, R. Secure Coding in C and C++. Boston, MA: Addison-Wesley, 2005. Chapter 5, "Integer Security."