When the requested size is zero the behavior of the memory allocation functions malloc()
, calloc()
, and realloc()
is implementation-defined. According to C99, Section 7.2022.3 of the C standard [ISO/IEC 9899:19992011] states:
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.
In addition, the amount of storage allocated by a successful call to the allocation function when 0 bytes was requested is unspecified. See unspecified behavior 40 41 in section J.1 of the standard.
...
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 size
elements. However, if size
is 0, the call to malloc(size)
may return a reference to a block of memory of size 0 instead of a null
pointer. When (nonempty) data is copied to this location, a heap-buffer overflow occurs.
Code Block | ||||
---|---|---|---|---|
| ||||
size_t size;
/* initialize size, possibly by user-controlled input */
int *list = (int *)malloc(size);
if (list == NULL) {
/* Handle allocation error */
}
else {
/* Continue processing list */
}
|
...
Code Block | ||||
---|---|---|---|---|
| ||||
size_t size;
/* initialize size, possibly by user-controlled input */
if (size == 0) {
/* Handle error */
}
int *list = (int *)malloc(size);
if (list == NULL) {
/* Handle allocation error */
}
/* Continue processing list */
|
...
The realloc()
function deallocates the old object and returns a pointer to a new object of a specified 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 often recommended for reallocating memory:
Code Block | ||||
---|---|---|---|---|
| ||||
size_t nsize = /* some value, possibly user supplied */;
char *p2;
char *p = (char *)malloc(100);
if (p == NULL) {
/* Handle error */
}
/* ... */
if ((p2 = (char *)realloc(p, nsize)) == NULL) {
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 (for example, zero-length) object. In cases where the realloc()
function frees the memory but returns a null pointer, execution of the code results in a double-free vulnurability. If the realloc()
function returns a non-null value, but the size was 0, the returned memory will be of size 0, and a heap overflow will occur if nonempty data is copied there.
...
Code Block | ||||
---|---|---|---|---|
| ||||
size_t nsize;
/* initialize nsize */
char *p2;
char *p = (char *)malloc(100);
if (p == NULL) {
/* Handle error */
}
/* ... */
p2 = NULL;
if (nsize != 0) {
p2 = (char *)realloc(p, nsize);
}
if (p2 == NULL) {
free(p);
p = NULL;
return NULL;
}
p = p2;
|
...
Tool | Version | Checker | Description | ||
---|---|---|---|---|---|
Section | Compass/ROSE |
|
| Section | Can detect some violations of this rule. IsIn particular, it warns when when the argument to or that is known at compile time to be 0. |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
...
CERT C++ Secure Coding Standard: MEM04-CPP. Do not perform zero length allocations
ISO/IEC 9899:19992011 Section 7.2022.3, "Memory Management Functionsmanagement functions"
MITRE CWE: CWE-687, "Function Call With Incorrectly Specified Argument Valuecall with incorrectly specified argument value"
Bibliography
[Vanegue 2010] Julien Vanegue. Automated vulnerability analysis of zero sized heap allocations. April 2010.
[Seacord 2005] Chapter 4, "Dynamic Memory Management"
...