...
Do not create an unrelated smart pointer object with a pointer value is that owned is owned by another smart pointer object. This includes resetting a smart pointer's managed pointer to an already-owned pointer value, such as by calling reset()
.
...
In this noncompliant code example, the B *
poly
pointer value owned 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 thereby stored in two different std::shared_ptr
objects. When g()
exits, the pointer stored in Derived
derived
is freed by the default deleter. Any further use of Poly
poly
results in accessing freed memory. When f()
exits, the same pointer stored in Poly
poly
is destroyed, resulting in a double-free vulnerability.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <memory> struct B { virtual ~B() = default; // Polymorphic object // ... }; struct D : B {}; void g(std::shared_ptr<D> Derivedderived); void f() { std::shared_ptr<B> Polypoly(new D); // ... g(std::shared_ptr<D>(dynamic_cast<D *>(Polypoly.get()))); // Any use of Polypoly will now result in accessing freed memory. } |
...
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
derived
, but because of the reference held by Poly
poly
(within f()
), the stored pointer value is still valid after g()
returns.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <memory> struct B { virtual ~B() = default; // Polymorphic object // ... }; struct D : B {}; void g(std::shared_ptr<D> Derivedderived); void f() { std::shared_ptr<B> Polypoly(new D); // ... g(std::dynamic_pointer_cast<D, B>(Polypoly)); // Polypoly is still referring to a valid pointer value. } |
...
Passing a pointer value to a deallocation function that was not previously obtained by the matching allocation function results in undefined behavior, which can lead to exploitable vulnerabilities.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
MEM56-CPP | High | Likely | Medium | P18 | L1 |
Automated Detection
Tool | Version | Checker | Description |
---|
Astrée |
| dangling_pointer_use | |||||||
Axivion Bauhaus Suite |
| CertC++-MEM56 | |||||||
Helix QAC |
| DF4721, DF4722, DF4723 | |||||||
Parasoft C/C++test |
| CERT_CPP-MEM56-a | Do not store an already-owned pointer value in an unrelated smart pointer | ||||||
Polyspace Bug Finder |
| CERT C++: MEM56-CPP | Checks for use of already-owned pointers (rule fully covered) | ||||||
|
Related Vulnerabilities
Search for other vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
SEI CERT C++ Coding Standard | MEM50-CPP. Do not access freed memory MEM51-CPP. Properly deallocate dynamically allocated resources |
MITRE CWE | CWE-415, Double Free |
Bibliography
[ISO/IEC 14882-2014] | Subclause 20.8, "Smart Pointers" |
...
...