The results of allocating zero bytes of memory are implementation dependent. According to C99 Section 7.20.3 ISO/IEC 9899-1999When the requested size is 0, the behavior of the memory allocation functions malloc()
, calloc()
, and realloc()
is implementation-defined. Subclause 7.22.3 of the C Standard [ISO/IEC 9899:2011] 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 41 in subclause J.1 of the C Standard.
This includes all three standard memory allocation functions: malloc()
, calloc()
, and realloc()
. In cases where the memory allocation functions return a non-NULL null pointer, using this pointer reading from or writing to the allocated memory area results in undefined behavior. Typically these , the pointer refer refers to a zero-length block of memory consisting entirely of control structures. Overwriting these control structures will damage damages the data structures used by the memory manager.
...
Noncompliant 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
size
elements. However, if s
size
is zero0, the call to malloc(ssize)
may return a reference to a block of memory of size 0 rather than NULL
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 = malloc(sizeof(int) * s)malloc(size); if (list == NULL) { /* Handle Allocationallocation Errorerror */ } else { /* Continue Processingprocessing list */ ...} |
Compliant
...
Solution (malloc()
)
To ensure that zero 0 is never passed as a size argument to malloc()
, a check must be made on s
to ensure it is not zero. size
is checked to confirm it has a positive value:
Code Block | ||||
---|---|---|---|---|
| ||||
...size_t size; /* Initialize size, possibly by user-controlled input */ if (ssize <== 0) { /* Handle Errorerror */ } int *list = malloc(sizeof(int) * s)malloc(size); if (list == NULL) { /* Handle Allocationallocation Errorerror */ } /* Continue Processingprocessing list */ ... |
...
Noncompliant 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 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 generally 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) { 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.for example, zero-length) object. However, the standard does not dictate what the return value should be either case. In cases where the realloc()
function frees the memory but returns a null pointer, execution of this the code will result results in a double-free when free()
is calledvulnerability. 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.
Implementation Details
OpenBSD returns a valid If this noncompliant code is compiled with GCC 3.4.6 and linked with libc 2.3.4, invoking realloc(p, 0)
returns a non-null pointer to a zero-sized object (the same as malloc(0)
). However, the realloc()
function for both if the same code is compiled with either Microsoft Visual Studio Version 7.1 and gcc version or GCC 4.1.0 return 0 , realloc(p, 0)
returns a null pointer, resulting in a double free on the call to free()
in this example-free vulnerability.
Compliant
...
Solution (realloc()
)
Do This compliant solution does not pass a size argument of zero to the realloc()
function.:
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) { if (p) free(p); p = NULL; return NULL; } p = p2; |
Risk Assessment
Assuming that allocating zero bytes results in an error Allocating 0 bytes 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. abnormal program termination.
Recommendation |
---|
Severity | Likelihood | Remediation Cost | Priority | Level |
---|
MEM36-C
3 (high)
2 (probable)
2 (medium)
P12
L1
References
MEM04-C | Low | Likely | Medium | P6 | L2 |
Automated Detection
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
Astrée |
| Supported, but no explicit checker | |||||||
CodeSonar |
| (customization) | Users can add a custom check for allocator calls with size argument 0 (this includes literal 0, underconstrained tainted values, and computed values). | ||||||
Compass/ROSE | Can detect some violations of this rule. In particular, it warns when the argument to | ||||||||
Parasoft C/C++test |
| CERT_C-MEM04-a | The validity of values passed to library functions shall be checked | ||||||
Polyspace Bug Finder |
| Checks for:
Rec. fully covered. |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
SEI CERT C++ Coding Standard | VOID MEM04-CPP. Do not perform zero-length allocations |
MITRE CWE | CWE-687, Function call with incorrectly specified argument value |
Bibliography
[ISO/IEC 9899:2011] | Section 7.22 |
...
.3, "Memory Management Functions" |
[Seacord |
...
2013] | Chapter 4, "Dynamic Memory Management" |
[Vanegue 2010] | "Automated Vulnerability Analysis of Zero-Sized Heap Allocations" |
...