Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

The default memory allocation 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 = new (std::nothrow) T; // Returns nullptr if allocation fails

T *p3 = new T[1]; // Throws

Wiki Markup
The return values for {{malloc()}} and other C 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|AA. References#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.

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():

Code Block

int* s = new int[-1]; // throws a std::bad_alloc exception
int* s if the allocation fails
T *p4 = new (std::nothrow) intT[-1]; // returns NULL
 Returns nullptr if the allocation fails

Furthermore, operator new[] can throw an error of type std::bad_array_new_length, a subclass of std::bad_alloc, if the size argument passed to new is negative or excessively large.

When using the nonthrowing formWhen using std::nothrow, it is imperative to check that the return value is not NULL before using it 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
bgColor#FFcccc
langcpp
#include <cstring>
 
void f(const int *array;
, std::size_t size) noexcept {
  int *copy = new int[size];
  std::memcpy(copy, array, size * sizeof(*copy));
/* initialize array and size */

  // ...
  delete [] copy;
}

Compliant Solution (std::nothrow)

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
bgColor#ccccff
langcpp
#include <cstring>
#include <new>
 
void f(const int *array, std::size_t size) noexcept {
  int *copy = (char *)malloc(size * sizeof(int));
memcpy( new (std::nothrow) int[size];
  if (!copy) {
    // Handle error
    return;
  }
  std::memcpy(copy, array, size * sizeof(int*copy));
  /*/ ... */
free(copy);
copy = NULL;

...


  delete [] copy;
}

Compliant Solution (std::

...

bad_alloc)

Alternatively, you can use ::operator new[] without std::nothrow and instead catch a std::bad_alloc exception if sufficient memory cannot be allocatedThis example remains noncompliant if we replace malloc() with new(std::nothrow).

Code Block
bgColor#ccccff
lang#FFcccccpp
#include <cstring>
#include <new>
 
void f(const int *array;
, std::size_t size;

/* initialize array and size */

int *) noexcept {
  int *copy;
  try {
    copy = new int[size];
  } catch(std::nothrow) int[size];
memcpy( bad_alloc) {
    // Handle error
    return;
  }
  // At this point, copy has been initialized to allocated memory
  std::memcpy(copy, array, size * sizeof(int*copy));
  //* ... */
free(copy);
copy = NULL;

  delete [] copy;
}

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 throwingWith 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.

Code Block
bgColor#ccccff
langcpp
#include <cstring>
 
void f(const int *array;
, std::size_t size;

/* initialize array and size */

) noexcept(false) {
  int *copy = new(std::nothrow) int[size];
if (copy == NULL) {
  /* Handle allocation error */
} else {
  memcpy(   // If the allocation fails, it will throw an exception which the caller
  // will have to handle.
  std::memcpy(copy, array, size * sizeof(int*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
bgColor#FFcccc
langcpp
struct A { /* ... */ };
struct  free(copyB { /* ... */ }; 
 
void g(A *, B *);
void  copy = NULL;
}

Compliant Solution (bad_alloc)

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 destroyedAlternatively, 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
bgColor#ccccff
langcpp
#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
bgColor#ccccff
langcpp
struct A { /* ... */ };
struct B { 
int *array;
size_t size;

/* initialize array and size */

int *copy = new int[size];
memcpy( copy, array, size * sizeof(int));
/* ... */
free(copy);
copy = NULL;
 }; 
 
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.

Wiki MarkupIf 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|http://www.kb.cert.org/vuls/id/159523]\].

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

MEM32

MEM52-CPP

high

High

likely

Likely

medium

Medium

P18

L1

Automated Detection

...

Tool

Version

Checker

Description

Compass/ROSE

...




Coverity7.5CHECKED_RETURNFinds inconsistencies in how function call return values are handled
Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C++3225, C++3226, C++3227, C++3228, C++3229, C++4632


Klocwork
Include Page
Klocwork_V
Klocwork_V
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
Include Page
LDRA_V
LDRA_V

45 D

Partially implemented

Parasoft C/C++test
Include Page
Parasoft_V
Parasoft_V

CERT_CPP-MEM52-a
CERT_CPP-MEM52-b

Check the return value of new
Do not allocate resources in function argument list because the order of evaluation of a function's parameters is undefined

Parasoft Insure++

Runtime detection
Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C++: MEM52-CPPChecks for unprotected dynamic memory allocation (rule partially covered)
PVS-Studio

Include Page
PVS-Studio_V
PVS-Studio_V

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

Related Vulnerabilities

Wiki MarkupThe vulnerability in Adobe Flash \[[VU#159523|AA. References#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 the CERT website.

Other Languages

...

Related Guidelines

...

...

standard library errors
MITRE CWE

CWE 252, Unchecked Return Value
CWE 391, Unchecked Error Condition
CWE 476, NULL Pointer Dereference
CWE 690, Unchecked Return Value to NULL Pointer Dereference
CWE 703, Improper Check or Handling of Exceptional Conditions
CWE 754, Improper Check for Unusual or Exceptional Conditions

Bibliography

[ISO/IEC 9899:2011]Subclause

References

...

7.20.3,

...

"Memory

...

Management Functions"
[ISO/IEC

...

14882-2014]

Subclause 18.6.1.1, "Single-Object Forms"
Subclause 18.6.1.2, "Array Forms"
Subclause 20.7.9.1, "Allocator Members"

[Meyers 1996]Item 7, "Be Prepared for Out-of-Memory Conditions"
[Seacord 2013]Chapter 4, "Dynamic Memory Management"


...

Image Added Image Added Image Added2003|AA. References#ISO/IEC 14882-2003]\] Section 5.3.4 \[[MITRE 07|AA. References#MITRE 07]\] [CWE ID 476|http://cwe.mitre.org/data/definitions/476.html], "NULL Pointer Dereference," and [CWE ID 252|http://cwe.mitre.org/data/definitions/252.html], "Unchecked Return Value" \[[Seacord 05|AA. References#Seacord 05]\] Chapter 4, "Dynamic Memory Management" \[[VU#159523|AA. References#VU#159523]\]MEM31-CPP. Free dynamically allocated memory exactly once      08. Memory Management (MEM)      MEM34-CPP. Only free memory allocated dynamically