You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 20 Next »

Evaluating pointers into memory that have been deallocated by a memory management function, including dereferencing, acting as an operand of an arithmetic operation, type casting, or using the pointer as the right-hand side of an assignment, is undefined behavior. Pointers to memory that have been deallocated are referred to as dangling pointers. Accessing a dangling pointer can result in exploitable vulnerabilities.

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

Noncompliant Code Example (new and delete)

In this noncompliant code example, s is dereferenced after it has been deallocated. 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, dynamic memory allocations and deallocations are far removed, making it difficult to recognize and diagnose such problems.

#include <new>
 
struct S {
  void f();
};
 
void f() noexcept(false) {
  S *s = new S;
  // ...
  delete s;
  // ...
  s->f();
}

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

Compliant Solution (new and delete)

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

#include <new>

struct S {
  void f();
};

void f() noexcept(false) {
  S *s = new S;
  // ...
  s->f();
  delete s;
}

Compliant Solution (Automatic Storage Duration)

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

struct S {
  void f();
};

void f() {
  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:

#include <iostream>
#include <memory>
#include <cstring>
 
int main(int argc, const char *argv[]) {
  const char *s = "";
  if (argc > 1) {
    enum { BUFFER_SIZE = 32 };
    try {
      std::unique_ptr<char[]> buff(new char[BUFFER_SIZE]);
      // ...
      s = std::strncpy(buff.get(), argv[1], BUFFER_SIZE - 1);
    } catch (std::bad_alloc &) {
      // Handle error
    }
  }

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

Compliant Solution (std::unique_ptr)

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

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

  if (argc > 1) {
    enum { BUFFER_SIZE = 32 };
    try {
      buff.reset(new char[BUFFER_SIZE]);
      // ...
      s = std::strncpy(buff.get(), argv[1], BUFFER_SIZE - 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 increases the security of the solution:

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

  if (argc > 1) {
    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.

#include <string>
 
std::string someStringReturningFunction();
void displayString(const char *);
 
void f() {
  const char *str = someStringReturningFunction().c_str();
  displayString(str);  /* Undefined behavior */
}

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

In this compliant solution, a local copy of the string returned by someStringReturningFunction() is made that ensures the string str will be valid when the call to displayString is made:

#include <string>
 
std::string someStringReturningFunction();
void displayString(const char *s);

void f() {
  std::string str = someStringReturningFunction();
  const char *str = str.c_str();
  displayString(str);  /* ok */
}

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

High

Likely

Medium

P18

L1

Automated Detection

Tool

Version

Checker

Description

Compass/ROSE

 

 

 

Coverity

v7.5.0

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

Fortify SCA

5.0

Double Free

 

Klocwork

2024.3

UFM.DEREF.MIGHT
UFM.DEREF.MUST
UFM.PARAMPASS.MIGHT
UFM.PARAMPASS.MUST

UFM.RETURN.MIGHT
UFM.RETURN.MUST
UFM.USE.MIGHT
UFM.USE.MUST

 

LDRA tool suite

9.7.1

 

51 D

Fully implemented

Splint

5.0

 

 

 

Related Vulnerabilities

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

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

Related Guidelines

Bibliography

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

 


  

  • No labels