Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Substantive changes

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 operator, ::operator new(std::size_t), 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():whether calling ::operator new(std::size_t) results in nullptr. The nonthrowing form, ::operator new(std::size_t, const std::nothrow_t &), will not throw an exception if the allocation fails, but will instead return 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
Code Block
int* s = new int[-1]; // throws a std::bad_alloc exception
int* sif allocation fails.
T *p2 = new (std::nothrow) int[- T; // Returns nullptr if allocation fails.

T *p3 = new T[1]; // returnsThrows NULL

...

std::

...

The following table shows the possible outcomes of the C++ Standard Library memory allocation functions.

Function

Successful Return

Failure

errno #1

malloc()

pointer to allocated space

NULL

ENOMEM

calloc()

pointer to allocated space

NULL

ENOMEM

realloc()

pointer to the new object

NULL

ENOMEM

operator new(size_t)

pointer to allocated space

bad_alloc

N/A

operator new(size_t, nothrow_t)

pointer to allocated space

NULL

N/A

...

bad_alloc if the allocation fails.
T *p4 = new (std::nothrow) T[1]; // Returns nullptr if the allocation fails.

In addition, operator new[] can  can throw an error of type type std::bad_array_new_length if the  if the size argument  argument passed to to new is  is negative or excessively large. This is a subclass of 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.

Code Block
bgColor#FFcccc
langcpp
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)

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 ERR30-CPP. Try to recover gracefully from unexpected errors.

Noncompliant Code Example

In this noncompliant code example, an array of int is created using ::operator new[](std::size_t), but the results of the allocation are not checked. Since the function is marked as noexcept, the caller assumes this function does not throw any exceptions. Because ::operator new[](std::size_t) can throw an exception if the allocation fails, this could lead to abnormal termination of the programThis example remains noncompliant if we replace malloc() with new(std::nothrow).

Code Block
bgColor#FFcccc
langcpp
#include <cstring>
 
void f(const int * array, std::size_t size) noexcept {
  int * copy = new(std::nothrow) int[size];
  std::memcpy(copy, array, size * sizeof *copy);
  // ...
  delete [] copy;
}

Compliant Solution (std::nothrow)

With 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 NULL before nullptr before referencing the pointer. Handle This compliant solution handles the error condition appropriately when the returned pointer is NULL nullptr.

Code Block
bgColor#ccccff
langcpp
int#include <cstring>
 
void f(const int * array, std::size_t size) noexcept {
  int * const copy = new (std::nothrow) int[size];
  if (!copy == NULL) {
    // IndicateHandle error to caller.
    return -1;
  }

  std::memcpy(copy, array, size * sizeof *copy);
  // ...
  delete [] copy;

  // Indicate successful completion.
  return 0;
}

Compliant Solution (std::bad_alloc)

Alternatively, one can use use ::operator new without std::nothrow. Unless [] without std::nothrow is provided, operator new never returns NULL; it will instead throw a , and instead catch a std::bad_alloc exception if it cannot allocate memorysufficient memory cannot be allocated.

Code Block
bgColor#ccccff
langcpp
int#include <cstring>
 
void f(const int * array, std::size_t size) noexcept {
  int * copy;
  try {
    copy = new int[size];
  }
  catch (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;
}

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. This is not strictly required, as any function without a noexcept specifier is presumed to allow throwing.

Code Block
bgColor#ccccff
langcpp
#include <cstring>
 
void f(const int *array, std::size_t size) noexcept(false) {
  int *copy = new int[size];
  // Indicate successful completionIf the allocation fails, it will throw an exception which the caller
  // will have to handle.
  std::memcpy(copy, array, size * sizeof *copy);
  // ...
  return 0delete [] copy;
}

Risk Assessment

Failing to detect allocation failures can lead to abnormal program termination and denial-of-service attacks.

...

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

MEM32-CPP

highHigh

likelyLikely

mediumMedium

P18

L1

Automated Detection

Tool

Version

Checker

Description

...

...

CHECKED_RETURN

...

Finds inconsistencies in how function call return values are handled.
Fortify SCA

...

5.0

...

  

...

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, 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

...

...

...

Bibliography

...

14882-2014]

18.6.1.1, "Single-object Forms"
18.6.1.2, "Array Forms"
20.7.9.1, "Allocator Members"

[ISO/IEC

...

9899:2011]Section 7.20.3, "Memory management functions"
[Meyers 95]Item 7. Be prepared for out-of-memory conditions.

...

[

...

Seacord 05]Chapter 4, "Dynamic Memory Management"

...

...

MEM31-CPP. Free dynamically allocated memory exactly once      08. Memory Management (MEM)      MEM33-CPP. Ensure that aborted constructors do not leak