...
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 any wrapping occurs, malloc() will allocate insufficient space for mBlock, which can lead to a subsequent buffer overflow.
| Code Block | ||||
|---|---|---|---|---|
| ||||
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;
}
|
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
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.
...
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.
| Code Block | ||||
|---|---|---|---|---|
| ||||
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;
}
|
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
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 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 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.
...
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.
...
Tool | Version | Checker | Description | ||||||
|---|---|---|---|---|---|---|---|---|---|
|
| Can detect violations of this rule. It should look for patterns of
| |||||||
| Coverity | 6.5 | OVERFLOW_BEFORE_WIDEN | Fully Implemented | ||||||
5.0 |
| Can detect violations of this rule with CERT C Rule Pack | |||||||
| PRQA QA-C |
| 1890 | Partially implemented |
...