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|AA. C References#ISO/IEC 9899-1999]\] Sections 6.3.1, "Arithmetic operands"
\[[Seacord 05a|AA. C References#Seacord 05a]\] Seacord, R. _Secure Coding in C and C+\+_. Boston, MA: Addison-Wesley, 2005.  Chapter 5, "Integer Security."