Versions Compared

Key

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

The results of allocating zero bytes of memory are implementation dependent. According to C99 Section 7.20.3 ISO/IEC 9899-1999:

If the size of the space requested is zero, the behavior is implementation defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object.

This includes all three standard memory allocation functions: malloc(), calloc(), and realloc(). In cases where the memory allocation functions return a non-NULL pointer, using this pointer results in undefined behavior. Typically these pointer refer to a zero-length block of memory consisting entirely of control structures. Overwriting these control structures will damage the data structures used by the memory manager result of calling malloc(0) or calloc() - calloc(1,0), calloc(0,0), or calloc(0,1) - to allocate 0 bytes is implementation defined. From a practical standpoint, allocating 0 bytes with calloc() and malloc() can lead to programming errors with critical security implications, such as buffer overflows. This occurs because the result of allocating 0 bytes with calloc() and malloc() may not be considered an error, thus the pointer returned may not be NULL. Instead, the pointer may reference a block of memory on the heap of size zero. If memory is fetched from or stored in that location, a serious error could occur.

Non-Compliant Code Example

...

: malloc()

The result of calling malloc(0) to allocate 0 bytes is implementation defined. In this example, a dynamic array of integers is allocated to store s elements. However, if s is zero, the call to malloc(s) may return a reference to a block of memory of size 0 rather than NULL. When data is copied to this location, a heap-buffer overflow will occuroccurs.

Code Block
bgColor#FFcccc
...
list = malloc(sizeof(int) * s);
if (list == NULL) {
  /* Handle Allocation Error */
}
/* Continue Processing list */
...

Compliant Code Example

...

: malloc()

To ensure that zero is never passed as a size argument to malloc(), a check must be made on s to ensure it is not zero.

Code Block
bgColor#ccccff
...
if (s <= 0) {
  /* Handle Error */
}
list = malloc(sizeof(int) * s);
if (list == NULL) {
  /* Handle Allocation Error */
}
/* Continue Processing list */
...

Non-Compliant Code Example: realloc()

The realloc() function deallocates the old object pointed to by ptr and returns a pointer to a new object that has the size specified by size. If memory for the new object cannot be allocated, the realloc() function does not deallocate the old object and its value is unchanged. If the realloc() function returns NULL, failing to free the original memory will result in a memory leak. As a result, the following idiom is generally recommended for reallocating memory:

Code Block
bgColor#FFcccc

char *p2;
char *p = malloc(100);
...
if ((p2 = realloc(p, nsize)) == NULL) {
  if (p) free(p);
  p = NULL;
  return NULL;
}
p = p2;

However, this commonly recommended idiom has problems with zero length allocations. If the value of nsize in this example is 0, the standard allows the option of either returning a null pointer or returning a pointer to an invalid (e.g., zero-length) object. However, the standard does not dictate what the return value should be either case.

Both glibc and OpenBSD return a valid pointer to a zero-sized object (the same as malloc(0)). However, the realloc() function for Microsoft Visual Studio Version 7.1 returns a null pointer, resulting in a double free on the call to free() in this example.

Compliant Code Example: realloc()

Do not pass a size argument of zero to the realloc() function.

Code Block
bgColor#ccccff

char *p2;
char *p = malloc(100);
...
if ( (nsize == 0) || (p2 = realloc(p, nsize)) == NULL) {
  if (p) free(p);
  p = NULL;
  return NULL;
}
p = p2;

Risk Assessment

Assuming that allocating zero bytes results in an error can lead to buffer overflows when zero bytes are allocated. Buffer overflows can be exploited by an attacker to run arbitrary code with the permissions of the vulnerable process.

...