Versions Compared

Key

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

...

Code Block
bgColor#ccccff
langcpp
#include <new>

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

Noncompliant Code Example

In this noncompliant code example, the B * pointer value stored by a std::shared_ptr object is cast to the D * pointer type with dynamic_cast in an attempt to obtain a std::shared_ptr of the polymorphic derived type. However, this eventually results in undefined behavior as the same pointer is stored in two different std::shared_ptr objects. When g() exits, the pointer stored in Derived is freed by the default deleter. Any further use of Poly results in accessing freed memory. When f() exits, the same pointer stored in Poly is destroyed, resulting in a double-free vulnerability.

Code Block
bgColor#FFcccc
langcpp
#include <memory>

struct B {
  virtual ~B() = default; // Polymorphic object
  // ...
};
struct D : B {};

void g(std::shared_ptr<D> Derived);

void f() {
  std::shared_ptr<B> Poly(new D);
  // ...
  g(std::shared_ptr<D>(dynamic_cast<D *>(Poly.get())));
  // Any use of Poly will now result in accessing freed memory.
}

Compliant Solution

In this compliant solution, the dynamic_cast is replaced with a call to std::dynamic_pointer_cast(), which returns a std::shared_ptr of the polymorphic type with the valid shared pointer value. When g() exits, the reference count to the underlying pointer is decremented by the destruction of Derived, but because of the reference held by Poly (within f()) the stored pointer value is still valid after g() returns.

Code Block
bgColor#ccccff
langcpp
#include <memory>

struct B {
  virtual ~B() = default; // Polymorphic object
  // ...
};
struct D : B {};

void g(std::shared_ptr<D> Derived);

void f() {
  std::shared_ptr<B> Poly(new D);
  // ...
  g(std::dynamic_pointer_cast<D, B>(Poly));
  // Poly is still referring to a valid pointer value.
}

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.

...