Referring to objects of incomplete class type, also known as forward declarations, is a common practice. One such common usage is with the "pimpl idiom" [Sutter 00] whereby an opaque pointer is used to hide implementation details from a public-facing API. However, attempting to delete a pointer to an object of incomplete class type can lead to undefined behavior. The C++ Standard, [expr.delete], paragraph 5 [ISO/IEC 14882-2014], states the following:
If the object being deleted has incomplete class type at the point of deletion and the complete class has a non-trivial destructor or a deallocation function, the behavior is undefined.
...
Pointer downcasting to a pointer of incomplete class type has similar caveats. Pointer upcasting (casting from a more derived type to a less derived type) is a standard implicit conversion operation. C++ allows static_cast
to perform the inverse operation, pointer downcasting, via [expr.static.cast], paragraph 7. However, when the pointed-to type is incomplete, the compiler is unable to make any class offset adjustments that may be required in the presence of multiple inheritancesinheritance, resulting in a pointer that cannot be validly dereferenced.
The reinterpret_cast
of a pointer type is defined by [expr.reinterpret.cast], paragraph 7, as being static_cast<cv T *>(static_cast<cv void *>(PtrValue))
, meaning that reinterpret_cast
is simply a sequence of static_cast
operations. C-style casts of a pointer to incomplete an incomplete object type are defined as using either static_cast
or reinterpret_cast
(it is unspecified which is picked is unspecified) in [expr.cast], paragraph 5.
...
In this noncompliant code example, a class attempts to implement the pimpl idiom but deletes a pointer to an incomplete class type, resulting in undefined behavior if Body
has a nontrivial destructor:.
Code Block | ||||
---|---|---|---|---|
| ||||
class Handle { class Body *impl; // Declaration of a pointer to an incomplete class public: ~Handle() { delete impl; } // Deletion of pointer to an incomplete class // ... }; |
...
Implementation Details
When compiled with ClangBB. Definitions#clang3.8 and the function f()
is executed, the noncompliant code example prints the following.
...
Casting pointers or references to incomplete classes can result in bad addresses. Deleting a pointer to an incomplete class results in undefined behavior if the class has a nontrivial destructor. Doing so can cause program termination, a runtime signal, or resource leaks.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
EXP57-CPP | Medium | Unlikely | Medium | P4 | L3 |
Automated Detection
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
Astrée |
| delete-with-incomplete-type | |||||||
Coverity | 6.5 | DELETE_VOID | Fully implemented | ||||||
Clang |
| -Wdelete-incomplete |
CodeSonar |
| LANG.CAST.PC.INC | Conversion: pointer to incomplete | ||||||
Helix QAC |
| C++3112 | |||||||
Klocwork |
| CERT.EXPR.DELETE_PTR.INCOMPLETE_TYPE | |||||||
LDRA tool suite |
| 169 S, 554 S | Enhanced Enforcement | ||||||
Parasoft C/C++test |
| CERT_CPP-EXP57-a | Do not delete objects with incomplete class at the point of deletion |
Parasoft Insure++ |
Runtime detection | |||||||||
Polyspace Bug Finder |
| CERT C++: EXP57-CPP | Checks for conversion or deletion of incomplete class pointer | ||||||
RuleChecker |
| delete-with-incomplete-type |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Bibliography
[Dewhurst |
2002] | Gotcha #39, "Casting Incomplete Types" |
[ISO/IEC 14882-2014] | Subclause 4.10, "Pointer Conversions" |
[Sutter |
2000] | "Compiler Firewalls and the Pimpl Idiom" |
...
...