Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
bgColor#FFcccc
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;
}

GCC 3.4.4 produces a warning for this noncompliant code example.

Compliant Solution 1

In this compliant solution, the length operand is upcast to unsigned long long, ensuring that the addition takes place in this size.

Code Block
bgColor#ccccff
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 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.

Compliant Solution 2

In this compliant solution, the length operand is subtracted from SIZE_MAX, this ensures that no overflow can occur (see INT30-C. Ensure that unsigned integer operations do not wrap).

Code Block
bgColor#ccccff
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;
}

...

There are two separate problems with this noncompliant 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.

...