...
| Code Block | ||||
|---|---|---|---|---|
| ||||
#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.
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
#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.
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
#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;
}
| ||||
Noncompliant Code Example
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
#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 BB. Definitions#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.
...
| Code Block | ||||||
|---|---|---|---|---|---|---|
| #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.
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
#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 */
}
} | ||||
Compliant Solution (size_t)
...
| Code Block | ||||||
|---|---|---|---|---|---|---|
| #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 */
}
}||||||
Risk Assessment
Failure to cast integers before comparing or assigning them to a larger integer size can result in software vulnerabilities BB. Definitions#vulnerability that can allow the execution of arbitrary code by an attacker with the permissions of the vulnerable process.
...
Tool | Version | Checker | Description | ||||||
|---|---|---|---|---|---|---|---|---|---|
| CodeSonar |
| ALLOC.SIZE.ADDOFLOW ALLOC.SIZE.IOFLOW ALLOC.SIZE.MULOFLOW ALLOC.SIZE.SUBUFLOW ALLOC.SIZE.TRUNC MISC.MEM.SIZE.ADDOFLOW MISC.MEM.SIZE.BAD MISC.MEM.SIZE.MULOFLOW MISC.MEM.SIZE.SUBUFLOW MISC.MEM.SIZE.TRUNC | Addition Overflow of Allocation Size Integer Overflow of Allocation Size Multiplication Overflow of Allocation Size Subtraction Underflow of Allocation Size Truncation of Allocation Size Addition Overflow of Size Unreasonable Size Argument Multiplication Overflow of Size Subtraction Underflow of Size Truncation of Size | ||||||
|
| 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 | |||||||
| CERT C Rules implemented in the LDRA tool suite |
| 452 S | Partially implemented | ||||||
| PRQA QA-C |
| 1890 4491 4492 | Partially implemented |
...
| SEI CERT C++ Coding Standard | INT35INT18-CPP. Evaluate integer expressions in a larger size before comparing or assigning to that sizeINT18-CPP. Evaluate integer expressions in a larger size before comparing or assigning to that size |
| ISO/AA. Bibliography#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) |
Bibliography
| [Dowd 2006AA. Bibliography#Dowd 06] | Chapter 6, "C Language Issues" |
| [Seacord AA. Bibliography#Seacord 2013] | Chapter 5, "Integer Security" |
...