The return values for memory allocation routines indicate the failure or success of the allocation. According to C99, calloc()
, malloc()
, and realloc()
return null pointers if the requested memory allocation fails [ISO/IEC 9899:1999]. Failure to detect and properly handle memory management errors can lead to unpredictable and unintended program behavior. As a result, it is necessary to check the final status of memory management routines and handle errors appropriately and in accordance with ERR00-CPP. Adopt and implement a consistent and comprehensive error-handling policy.
By default operator new
will throw a std::bad_alloc
exception if the allocation fails. Therefore you need not check that the result of operator new
is NULL
. However, to ease conversion of code to C++, the C++ Standard ISO/IEC 14882-2003 provides a variant of operator new
that behaves like malloc()
:
int* s = new int[-1]; // throws a std::bad_alloc exception int* s = new (std::nothrow) int[-1]; // returns NULL
When using std::nothrow
, it is imperative to check that the return value is not NULL
before using it.
The following table shows the possible outcomes of the C++ Standard Library memory allocation functions.
Function | Successful Return | Failure |
|
---|---|---|---|
| pointer to allocated space |
|
|
| pointer to allocated space |
|
|
| pointer to the new object |
|
|
| pointer to allocated space |
| N/A |
| pointer to allocated space |
| N/A |
- Setting
errno
is a POSIX ® extension to C99.
In addition, operator new[]
can throw an error of type std::bad_array_new_length
if the size
argument passed to new
is negative or excessively large. This is a subclass of std::bad_alloc
.
Noncompliant Code Example (malloc()
)
In this example, array
is copied into dynamically allocated memory referenced by copy
. However, the result of malloc()
is not checked before copy
is referenced. Consequently, if malloc()
fails, the program abnormally terminates.
void f(const int *array, std::size_t size) { int* copy = (int*)std::malloc(size * sizeof *copy); std::memcpy(copy, array, size * sizeof *copy); // ... free(copy); }
Noncompliant Code Example (std::nothrow
)
This example remains noncompliant if we replace malloc()
with new(std::nothrow)
.
void f(const int* array, std::size_t size) { int* copy = new(std::nothrow) int[size]; std::memcpy(copy, array, size * sizeof *copy); // ... delete[] copy; }
Compliant Solution (std::nothrow
)
With std::nothrow
, the new operator returns either a null pointer or a pointer to the allocated space. Always test the returned pointer to ensure it is not NULL before referencing the pointer. Handle the error condition appropriately when the returned pointer is NULL.
int f(const int* array, std::size_t size) { int* const copy = new(std::nothrow) int[size]; if (copy == NULL) { // Indicate error to caller. return -1; } std::memcpy(copy, array, size * sizeof *copy); // ... delete[] copy; // Indicate successful completion. return 0; }
Compliant Solution (bad_alloc
)
Alternatively, one can use operator new
without std::nothrow
. Unless std::nothrow
is provided, operator new
never returns NULL
; it will instead throw a std::bad_alloc
exception if it cannot allocate memory.
int f(const int* array, std::size_t size) { int* copy; try { copy = new int[size]; } catch (std::bad_alloc&) { // Indicate error to caller. return -1; } std::memcpy(copy, array, size * sizeof *copy); // ... delete[] copy; // Indicate successful completion. return 0; }
Risk Assessment
Failing to detect allocation failures can lead to abnormal program termination and denial-of-service attacks.
If the vulnerable program references memory offset from the return value, an attacker can exploit the program to read or write arbitrary memory. This has been used to execute arbitrary code [VU#159523].
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
MEM32-CPP | high | likely | medium | P18 | L1 |
Automated Detection
Coverity Code Advisor version 7.5 can detect violations of this rule. The CHECKED_RETURN finds inconsistencies in how function call return values are handled.
Fortify SCA Version 5.0 can detect violations of this rule.
Compass/ROSE can detect violations of this rule. In particular, it ensures that variables are compared to NULL before being used, as in VOID EXP34-CPP. Ensure a null pointer is not dereferenced.
Related Vulnerabilities
The vulnerability in Adobe Flash [VU#159523] arises because Flash neglects to check the return value from calloc()
. Even though calloc()
returns NULL, Flash does not attempt to read or write to the return value, but rather attempts to write to an offset from the return value. Dereferencing NULL usually results in a program crash, but dereferencing an offset from NULL allows an exploit to succeed without crashing the program.
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Other Languages
This rule appears in the C Secure Coding Standard as void MEM32-C. Detect and handle memory allocation errors.
Bibliography
[ISO/IEC 9899:1999] Section 7.20.3, "Memory management functions"
[ISO/IEC 14882-2003] Section 5.3.4
[Meyers 95] Item 7. Be prepared for out-of-memory conditions.
[MITRE] CWE ID 252, "Unchecked Return Value"
[MITRE] CWE ID 391, "Unchecked Error Condition"
[MITRE] CWE ID 476, "NULL Pointer Dereference"
[MITRE] CWE ID 690, "Unchecked Return Value to NULL Pointer Dereference"
[MITRE] CWE ID 703, "Failure to Handle Exceptional Conditions"
[MITRE] CWE ID 754, "Improper Check for Unusual or Exceptional Conditions"
[Seacord 05] Chapter 4, "Dynamic Memory Management"
[VU#159523]
MEM31-CPP. Free dynamically allocated memory exactly once 08. Memory Management (MEM) MEM33-CPP. Ensure that aborted constructors do not leak