The return values for default 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()
:
operator, ::operator new(std::size_t)
, throws a std::bad_alloc
exception if the allocation fails. Therefore, you need not check whether calling ::operator new(std::size_t)
results in nullptr. The nonthrowing form, ::operator new(std::size_t, const std::nothrow_t &)
, does not throw an exception if the allocation fails but instead returns nullptr
. The same behaviors apply for the operator new[]
versions of both allocation functions. Additionally, the default allocator object (std::allocator
) uses ::operator new(std::size_t)
to perform allocations and should be treated similarly.
Code Block |
---|
T *p1 = new T; // Throws std::bad_alloc if allocation fails
T *p2 |
Code Block |
int* s = new int[-1]; // throws a std::bad_alloc exception int* s = new (std::nothrow) int[- T; // Returns nullptr if allocation fails T *p3 = new T[1]; // returnsThrows NULL |
...
std:: |
...
bad_alloc if the allocation fails
T *p4 = new (std::nothrow) T[1]; // Returns nullptr if the allocation fails |
Furthermore, operator new[]
can throw an error of type
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 |
...
In addition, operator new[]
can throw an error of type std::bad_array_new_length
if the size
argument passed to new
is , a subclass of std::bad_alloc
, if the size
argument passed to new
is negative or excessively large. This is a subclass of std::bad_alloc
When using the nonthrowing form, it is imperative to check that the return value is not nullptr
before accessing the resulting pointer. When using either form, be sure to comply with ERR50-CPP. Do not abruptly terminate the program.
Noncompliant Code Example
...
In this noncompliant code example, an 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 terminatesof int
is created using ::operator new[](std::size_t)
and the results of the allocation are not checked. The function is marked as noexcept
, so the caller assumes this function does not throw any exceptions. Because ::operator new[](std::size_t)
can throw an exception if the allocation fails, it could lead to abnormal termination of the program.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <cstring> void f(const int *array, std::size_t size) noexcept { int * copy = (int*)std::malloc(size * sizeof *copy)new int[size]; std::memcpy(copy, array, size * sizeof (*copy)); // ... free(copy)delete [] copy; } |
...
Compliant Solution (std::nothrow
)
This example remains noncompliant if we replace malloc()
with new(When using 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 nullptr
before referencing the pointer. This compliant solution handles the error condition appropriately when the returned pointer is nullptr.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <cstring> #include <new> void f(const int * array, std::size_t size) noexcept { int * copy = new (std::nothrow) int[size]; if (!copy) { // Handle error return; } std::memcpy(copy, array, size * sizeof (*copy)); // ... delete [] copy; } |
Compliant Solution (std::
...
bad_alloc
)
With Alternatively, you can use ::operator new[]
without 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. and instead catch a std::bad_alloc
exception if sufficient memory cannot be allocated.
Code Block | ||||
---|---|---|---|---|
| ||||
int#include <cstring> #include <new> void f(const int * array, std::size_t size) noexcept { int * constcopy; try { copy = new(std::nothrow) int[size]; if} (copy == NULLcatch(std::bad_alloc) { // IndicateHandle error to caller. return -1; } // At this point, copy has been initialized to allocated memory std::memcpy(copy, array, size * sizeof (*copy)); // ... delete [] copy; // Indicate successful completion. return 0; } |
Compliant Solution (
...
noexcept(false))
If the design of the function is such that the caller is expected to handle exceptional situations, it is permissible to mark the function explicitly as one that may throw, as in this compliant solution. Marking the function is not strictly required, as any function without a noexcept
specifier is presumed to allow throwingAlternatively, 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.
Code Block | ||||
---|---|---|---|---|
| ||||
int#include <cstring> void f(const int * array, std::size_t size) noexcept(false) { int * copycopy = new int[size]; try {// If the allocation fails, it will throw an exception which the caller // will have to handle. std::memcpy(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; } , array, size * sizeof(*copy)); // ... delete [] copy; } |
Noncompliant Code Example
In this noncompliant code example, two memory allocations are performed within the same expression. Because the memory allocations are passed as arguments to a function call, an exception thrown as a result of one of the calls to new
could result in a memory leak.
Code Block | ||||
---|---|---|---|---|
| ||||
struct A { /* ... */ };
struct B { /* ... */ };
void g(A *, B *);
void f() {
g(new A, new B);
} |
Consider the situation in which A
is allocated and constructed first, and then B
is allocated and throws an exception. Wrapping the call to g()
in a try
/catch
block is insufficient because it would be impossible to free the memory allocated for A
.
This noncompliant code example also violates EXP50-CPP. Do not depend on the order of evaluation for side effects, because the order in which the arguments to g()
are evaluated is unspecified.
Compliant Solution (std::unique_ptr
)
In this compliant solution, a std::unique_ptr
is used to manage the resources for the A
and B
objects with RAII. In the situation described by the noncompliant code example, B
throwing an exception would still result in the destruction and deallocation of the A
object when then std::unique_ptr<A>
was destroyed.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <memory>
struct A { /* ... */ };
struct B { /* ... */ };
void g(std::unique_ptr<A> a, std::unique_ptr<B> b);
void f() {
g(std::make_unique<A>(), std::make_unique<B>());
} |
Compliant Solution (References)
When possible, the more resilient compliant solution is to remove the memory allocation entirely and pass the objects by reference instead.
Code Block | ||||
---|---|---|---|---|
| ||||
struct A { /* ... */ };
struct B { /* ... */ };
void g(A &a, B &b);
void f() {
A a;
B b;
g(a, b);
} |
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 vulnerability has been used to execute arbitrary code [VU#159523].
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|
MEM52-CPP |
High |
Likely |
Medium | P18 | L1 |
Automated Detection
...
Tool | Version | Checker | Description |
---|---|---|---|
Compass/ROSE | |||
Coverity | 7.5 | CHECKED_RETURN |
...
Finds inconsistencies in how function call return values are handled |
...
Fortify SCA Version 5.0 can detect violations of this rule.
...
Helix QAC |
| C++3225, C++3226, C++3227, C++3228, C++3229, C++4632 | |||||||
Klocwork |
| NPD.CHECK.CALL.MIGHT NPD.CHECK.CALL.MUST NPD.CHECK.MIGHT NPD.CHECK.MUST NPD.CONST.CALL NPD.CONST.DEREF NPD.FUNC.CALL.MIGHT NPD.FUNC.CALL.MUST NPD.FUNC.MIGHT NPD.FUNC.MUST NPD.GEN.CALL.MIGHT NPD.GEN.CALL.MUST NPD.GEN.MIGHT NPD.GEN.MUST RNPD.CALL RNPD.DEREF | |||||||
LDRA tool suite |
| 45 D | Partially implemented | ||||||
Parasoft C/C++test |
| CERT_CPP-MEM52-a | Check the return value of new | ||||||
Parasoft Insure++ | Runtime detection | ||||||||
Polyspace Bug Finder |
| CERT C++: MEM52-CPP | Checks for unprotected dynamic memory allocation (rule partially covered) | ||||||
PVS-Studio |
| V522, V668 |
Related Vulnerabilities
The vulnerability in Adobe Flash [VU#159523] arises because Flash neglects to check the return value from from calloc()
. Even though though calloc()
returns returns NULL
, Flash does not attempt to read or write to the return value. Instead, but rather it 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 the CERT website.
Other Languages
...
Related Guidelines
...
...
...
Bibliography
...
MITRE CWE | CWE 252, Unchecked Return Value |
...
...
...
703, |
...
Improper Check or Handling of Exceptional Conditions |
...
...
...
754, |
...
Improper Check for Unusual or Exceptional Conditions |
...
Bibliography
[ISO/IEC 9899:2011] | Subclause 7.20.3, "Memory Management Functions" |
[ISO/IEC 14882-2014] | Subclause 18.6.1.1, "Single-Object Forms" |
[Meyers 1996] | Item 7, "Be Prepared for Out-of-Memory Conditions" |
[Seacord 2013] | Chapter 4, "Dynamic Memory Management" |
...
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