Versions Compared

Key

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

Accessing previously dynamically allocated memory after it has been deallocated may corrupt the data structures used to manage the free store or other types of storage. References Evaluating a pointer—including dereferencing the pointer, using it as an operand of an arithmetic operation, type casting it, and using it as the right-hand side of an assignment—into memory that has been deallocated by a memory management function is undefined behavior. Pointers to memory that has been deallocated are referred to as called dangling pointers. Accessing a dangling pointer can result in exploitable exploitable vulnerabilities.

Reading a pointer to deallocated memory is undefined because the pointer value is indeterminate and may have a trap representation . In the latter case, doing so may cause a hardware trap.

When memory is deallocated, its contents may remain intact and accessible because it is at the memory manager's discretion when to reallocate or recycle the deallocated chunk. The data at the deallocated location may appear valid. However, this can change unexpectedly, leading to unintended program behavior. As a result, it is necessary to guarantee that memory is not It is at the memory manager's discretion when to reallocate or recycle the freed memory. When memory is freed, all pointers into it become invalid, and its contents might either be returned to the operating system, making the freed space inaccessible, or remain intact and accessible. As a result, the data at the freed location can appear to be valid but change unexpectedly. Consequently, memory must not be written to or read from once it is deallocatedfreed.anchor

Page properties

...

Noncompliant Code Example (free)

Wiki Markup
This example from Kernighan and Ritchie \[[Kernighan 88|AA. C++ References#Kernighan 88]\] shows both the incorrect and correct techniques for removing items from a linked list. The incorrect solution, clearly marked as wrong in the book, is bad because {{p}} is deallocated before the {{p->next}} is executed, so {{p->next}} reads memory that has already been deallocated.

Code Block
bgColor#FFCCCC

for (p = head; p != NULL; p = p->next)
    free(p);

...

hiddentrue

This rule could probably stand to cover memory which has yet to be allocated. For instance:

Code Block
void f(

...

Compliant Solution (free)

Kernighan and Ritchie also show the correct solution. To correct this error, a reference to p->next is stored in q before freeing p.

Code Block
bgColor#ccccff
for (p = head; p != NULL; p = q
) {
  
q
unsigned 
= p->next
char *ptr;
  
free(p); } head
*ptr = 
NULL
0;

...

}

This isn't really covered by EXP53-CPP. Do not read uninitialized memory because it has nothing to do with reading an uninitialized value.

...

Noncompliant Code Example (new

...

 and delete)

In this noncompliant code example, buff s is written to dereferenced after it has been deallocated. These vulnerabilities can be easily exploited to If this access results in a write-after-free, the vulnerability can be exploited to run arbitrary code with the permissions of the vulnerable process and are seldom this obvious. Typically Typically, dynamic memory allocations and deallocations are far removed, making it difficult to recognize and diagnose such problems.

Code Block
bgColor#FFCCCC
langcpp
#include <new>
 
struct S {
  void f();
};
 
void g() noexcept(false
int main(int argc, const char *argv[]) {
  charS *buff;

  buff s = new char[BUFSIZ]S;
  // ...
  delete[] buffs;
  // ...
  strncpy(buff, argv[1], BUFSIZ-1s->f();
}

...

The function g() is marked noexcept(false) to comply with MEM52-CPP. Detect and handle memory allocation errors.

...

Compliant Solution (new

...

 and delete)

In this compliant solution, the dynamically allocated memory isn't is not deallocated until it is no longer required.

Code Block
bgColor#ccccff
langcpp

int main(int argc, const char *argv[]#include <new>

struct S {
  void f();
};

void g() noexcept(false) {
  charS *buff;

  buffs = new char[BUFSIZ]S;
  // ...
  strncpy(buff, argv[1], BUFSIZ-1);
  // ...
  delete[] buff;
  buff = nullptr;
}

...

s->f();
  delete s;
}

Compliant Solution (Automatic Storage Duration)

When possible, use automatic storage duration instead of dynamic storage duration. Since s is not required to live beyond the scope of g(), this compliant solution uses automatic storage duration to limit the lifetime of s to the scope of g().

Code Block
bgColor#ccccff
langcpp
struct S {
  void f();
};

void g() {
  S s;
  // ...
  s.f();
}

...

Noncompliant Code Example (std::unique_ptr)

In the following noncompliant code example, the dynamically allocated memory managed by the buff object is accessed after it has been implicitly deallocated by the object's destructor.

Code Block
bgColor#ffcccc
langcpp
#include <iostream>
#include <memory>
#include <cstring>
 
int main(int argc, const char *argv[]) {
  const char *s = "";

  if (argc > 1 < argc)) {
    enum { BufferSize = 32 };
    try {
      std::unique_ptr<char[]> buff (new char [BUFSIZBufferSize]);
      std::memset(buff.get(), 0, BufferSize);
      // ...
      s = std::strncpy(buff.get(), argv[1], BufferSize BUFSIZ- 1);
    } catch (std::bad_alloc &) {
      // Handle error
    }
  }

  std::cout << s << '\n'std::endl;
}

Anchor cs_unique_ptr cs_unique_ptrThis code always creates a null-terminated byte string, despite its use of strncpy(), because it leaves the final char in the buffer set to 0.

Compliant Solution (std::unique_ptr)

In this compliant solution, the lifetime of the buff object extends past the point at which the memory managed by the object is accessed.

Code Block
bgColor#ccccff
langcpp
#include <iostream>
#include <memory>
#include <cstring>
 
int main(int argc, const char *argv[]) {
  std::unique_ptr<char[]> buff;
  const char *s = "";

  if (argc > 1 < argc)) {
    enum { BufferSize = 32 };
    try {
      buff.reset(new char [BUFSIZBufferSize]);
      std::memset(buff.get(), 0, BufferSize);
      // ...
      s = std::strncpy(buff.get(), argv[1], BufferSize BUFSIZ- 1);
    } catch (std::bad_alloc &) {
      // Handle error
    }
  }

  std::cout << s << std::endl;
}

Compliant Solution

In this compliant solution, a variable with automatic storage duration of type std::string is used in place of the std::unique_ptr<char[]>, which reduces the complexity and improves the security of the solution.

Code Block
bgColor#ccccff
langcpp
#include <iostream>
#include <string>
 
int main(int argc, const char *argv[]) {
  std::string str;

  if (argc > 1) << '\n';
}
{
    str = argv[1];
  }

  std::cout << str << std::endl;
}

Noncompliant Code Example (std::string::c_str())

In this noncompliant code example, std::string::c_str() is being called on a temporary std::string object. The resulting pointer will point to released memory once the std::string object is destroyed at the end of the assignment expression, resulting in undefined behavior when accessing elements of that pointer.

Code Block
bgColor#ffcccc
langcpp
#include <string>
 
std::string str_func();
void display_string(const char *);
 
void f() {
  const char *str = str_func().c_str();
  display_string(str);  /* Undefined behavior */
}

Compliant solution (std::string::c_str())

In this compliant solution, a local copy of the string returned by str_func() is made to ensure that string str will be valid when the call to display_string() is made.

Code Block
bgColor#ccccff
langcpp
#include <string>
 
std::string str_func();
void display_string(const char *s);

void f() {
  std::string str = str_func();
  const char *cstr = str.c_str();
  display_string(cstr);  /* ok */
}

Noncompliant Code Example

In this noncompliant code example, an attempt is made to allocate zero bytes of memory through a call to operator new(). If this request succeeds, operator new() is required to return a non-null pointer value. However, according to the C++ Standard, [basic.stc.dynamic.allocation], paragraph 2 [ISO/IEC 14882-2014], attempting to dereference memory through such a pointer results in undefined behavior.

Code Block
bgColor#FFcccc
langcpp
#include <new>

void f() noexcept(false) {
  unsigned char *ptr = static_cast<unsigned char *>(::operator new(0));
  *ptr = 0;
  // ...
  ::operator delete(ptr);
}

Compliant Solution

The compliant solution depends on programmer intent. If the programmer intends to allocate a single unsigned char object, the compliant solution is to use new instead of a direct call to operator new(), as this compliant solution demonstrates.

Code Block
bgColor#ccccff
langcpp
void f() noexcept(false) {
  unsigned char *ptr = new unsigned char;
  *ptr = 0;
  // ...
  delete ptr;
}

Compliant Solution

If the programmer intends to allocate zero bytes of memory (perhaps to obtain a unique pointer value that cannot be reused by any other pointer in the program until it is properly released), then instead of attempting to dereference the resulting pointer, the recommended solution is to declare ptr as a void *, which cannot be dereferenced by a conforming implementation.

Code Block
bgColor#ccccff
langcpp
#include <new>

void f() noexcept(false) {
  void *ptr = ::operator new(0);
  // ...
  ::operator delete(ptr);
}

Risk Assessment

Reading previously dynamically allocated memory after it has been deallocated can lead to abnormal program termination and denial-of-service attacks. Writing memory that has been deallocated can lead to the execution of arbitrary code with the permissions of the vulnerable process.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

MEM30

MEM50-CPP

high

High

likely

Likely

medium

Medium

P18

L1

Automated Detection

Tool

The LDRA tool suite Version 7.6.0 can detect violations of this rule.

Fortify SCA Version 5.0 can detect violations of this rule.

Splint Version 3.1.1 can detect violations of this rule.

Compass/ROSE can detect violations of the rule.

Klocwork Version 8.0.4.16 can detect violations of this rule with the UFM.DEREF.MIGHT, UFM.DEREF.MUST, UFM.FFM.MIGHT, UFM.FFM.MUST, UFM.PARAMPASS.MIGHT, UFM.PARAMPASS.MUST, UFM.RETURN.MIGHT, UFM.RETURN.MUST, UFM.USE.MIGHT, and UFM.USE.MUST checkers.

Related Vulnerabilities

Version

Checker

Description

Astrée

Include Page
Astrée_V
Astrée_V

dangling_pointer_use
Axivion Bauhaus Suite

Include Page
Axivion Bauhaus Suite_V
Axivion Bauhaus Suite_V

CertC++-MEM50
Clang
Include Page
Clang_V
Clang_V
clang-analyzer-cplusplus.NewDelete
clang-analyzer-alpha.security.ArrayBoundV2 
Checked by clang-tidy, but does not catch all violations of this rule.
CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

ALLOC.UAF

Use after free
Compass/ROSE




Coverity

Include Page
Coverity_V
Coverity_V

USE_AFTER_FREE

Can detect the specific instances where memory is deallocated more than once or read/written to the target of a freed pointer

Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C++4303, C++4304


Klocwork
Include Page
Klocwork_V
Klocwork_V
UFM.DEREF.MIGHT
UFM.DEREF.MUST
UFM.FFM.MIGHT
UFM.FFM.MUST
UFM.RETURN.MIGHT
UFM.RETURN.MUST
UFM.USE.MIGHT
UFM.USE.MUST 


LDRA tool suite
Include Page
LDRA_V
LDRA_V

483 S, 484 S

Partially implemented

Parasoft C/C++test
Include Page
Parasoft_V
Parasoft_V
CERT_CPP-MEM50-a

Do not use resources that have been freed

Parasoft Insure++

Runtime detection
Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C++: MEM50-CPP

Checks for:

  • Pointer access out of bounds
  • Deallocation of previously deallocated pointer
  • Use of previously freed pointer

Rule partially covered.

PVS-Studio

Include Page
PVS-Studio_V
PVS-Studio_V

V586, V774
Splint
Include Page
Splint_V
Splint_V



Related Vulnerabilities

VU#623332 describes a double-free vulnerability in the MIT Kerberos 5 function krb5_recvauth() [VU# 623332].

Search for other vulnerabilities resulting Search for vulnerabilities resulting from the violation of this rule on the the CERT website.

Other Languages

...

Related Guidelines

...

...

Wiki Markup
\[[Henricson 97|AA. C++ References#Henricson 97]\] Rule 8.3 Do not access a pointer or reference to a deleted object
\[[ISO/IEC 9899:1999|AA. C++ References#ISO/IEC 9899-1999]\] Section 7.20.3.2, "The {{free}} function"
\[[ISO/IEC PDTR 24772|AA. C++ References#ISO/IEC PDTR 24772]\] "DCM Dangling references to stack frames" and "XYK Dangling Reference to Heap"
\[[Kernighan 88|AA. C++ References#Kernighan 88]\] Section 7.8.5, "Storage Management"
\[[MISRA 04|AA. C++ References#MISRA 04]\] Rule 17.6
\[[MITRE 07|AA. C++ References#MITRE 07]\] [CWE ID 416|http://cwe.mitre.org/data/definitions/416.html], "Use After Free"
\[[OWASP Freed Memory|AA. C++ References#OWASP Freed Memory]\]
\[[Seacord 05a|AA. C++ References#Seacord 05]\] Chapter 4, "Dynamic Memory Management"
\[[Viega 05|AA. C++ References#Viega 05]\] Section 5.2.19, "Using freed memory"

CWE-415, Double Free
CWE-416, Use After Free

Bibliography

[ISO/IEC 14882-2014]Subclause 3.7.4.1, "Allocation Functions"
Subclause 3.7.4.2, "Deallocation Functions" 
[Seacord 2013b]Chapter 4, "Dynamic Memory Management"


...

Image Added Image Added Image AddedMEM11-CPP. Do not use volatile as a synchronization primitive      08. Memory Management (MEM)      MEM31-CPP. Free dynamically allocated memory exactly once