...
Code Block |
---|
T *p1 = new T; // Throws std::bad_alloc if allocation fails T *p2 = new (std::nothrow) T; // Returns nullptr if allocation fails T *p3 = new T[1]; // Throws std::bad_alloc if the allocation fails T *p4 = new (std::nothrow) T[1]; // Returns nullptr if the allocation fails |
In additionFurthermore, operator new[]
, a subclass of std::bad_alloc
, can can throw an error of type std::bad_array_new_length
if , a subclass of std::bad_alloc
, if the size
argument passed to new
is negative or excessively large.
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 call std::terminate(), std::abort(), or std::_Exit()abruptly terminate the program.
Noncompliant Code Example
In this noncompliant code example, an array of int
is created using ::operator new[](std::size_t)
, but 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.
...
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; } |
...
Alternatively, you can use ::operator new[]
without std::nothrow
and instead catch a std::bad_alloc
exception if sufficient memory cannot be allocated:.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <cstring> #include <new> void f(const int *array, std::size_t size) noexcept { int *copy; try { copy = new int[size]; } catch(std::bad_alloc) { // Handle error return; } // At this point, copy has been initialized to allocated memory std::memcpy(copy, array, size * sizeof(*copy)); // ... delete [] copy; } |
...
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.
...
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); } |
...
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 |
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 calloc()
. Even though calloc()
returns NULL
, Flash does not attempt to read or write to the return value. Instead, 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 CERT website.
Related Guidelines
SEI CERT C Coding Standard | ERR33-C. Detect and handle standard library errors |
MITRE CWE | CWE 252, Unchecked Return Value |
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" |
...
...