Reclaiming resources when exceptions are thrown is important. An exception being thrown may result in cleanup code being bypassed or an object being left in a partially initialized state. Such a partially - initialized object would violate basic exception safety, as described in ERR56-CPP. Guarantee exception safety. It is preferable that resources be reclaimed automatically, using the RAII design pattern [Stroustrup 2001], when objects go out of scope. This technique avoids the need to write complex cleanup code when allocating resources.
However, constructors do not offer the same protection. Because a constructor is involved in allocating resources, it does not automatically free any resources it allocates if it terminates prematurely. The C++ Standard, [except.ctor], paragraph 2 [ISO/IEC 14882-2014], states the following:
An object of any storage duration whose initialization or destruction is terminated by an exception will have destructors executed for all of its fully constructed subobjects (excluding the variant members of a union-like class), that is, for subobjects for which the principal constructor (12.6.2) has completed execution and the destructor has not yet begun execution. Similarly, if the non-delegating constructor for an object has completed execution and a delegating constructor for that object exits with an exception, the object’s destructor will be invoked. If the object was allocated in a new-expression, the matching deallocation function (3.7.4.2, 5.3.4, 12.5), if any, is called to free the storage occupied by the object.
...
Resources must not be leaked as a result of throwing an exception, including during the construction of an object.
This rule is a subset of MEM51-CPP. Properly deallocate dynamically allocated resources, as all failures to deallocate resources violate that rule.
Noncompliant Code Example
In this noncompliant code example, pst
is not properly released when process_item
throws an exception, causing a resource leak:.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <new> struct SomeType { SomeType() noexcept; // Performs nontrivial initialization. ~SomeType(); // Performs nontrivial finalization. void process_item() noexcept(false); }; void f() { SomeType *pst = new (std::nothrow) SomeType(); if (!pst) { // Handle error return; } try { pst->process_item(); } catch (...) { // HandleProcess error, but do not recover from it; rethrow. throw; } delete pst; } |
Compliant Solution (delete
)
In this compliant solution, the exception handler frees pst
by calling delete
:.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <new> struct SomeType { SomeType() noexcept; // Performs nontrivial initialization. ~SomeType(); // Performs nontrivial finalization. void process_item() noexcept(false); }; void f() { SomeType *pst = new (std::nothrow) SomeType(); if (!pst) { // Handle error return; } try { pst->process_item(); } catch (...) { // HandleProcess error, but do not recover from it; rethrow. delete pst; throw; } delete pst; } |
...
Code Block | ||||
---|---|---|---|---|
| ||||
struct SomeType { SomeType() noexcept; // Performs nontrivial initialization. ~SomeType(); // Performs nontrivial finalization. void process_item() noexcept(false); }; void f() { SomeType st; try { st.process_item(); } catch (...) { // HandleProcess error, but do not recover from it; rethrow. throw; } // After re-throwing the exception, the destructor is run for st. } // If f() exits without throwing an exception, the destructor is run for st. |
...
This compliant solution mitigates the potential failures by releasing a
and b
if an exception is thrown during their allocation or during init()
:.
Code Block | ||||
---|---|---|---|---|
| ||||
struct A {/* ... */}; struct B {/* ... */}; class C { A *a; B *b; protected: void init() noexcept(false); public: C() : a(nullptr), b(nullptr) { try { a = new A(); b = new B(); init(); } catch (...) { delete a; delete b; throw; } } }; |
...
Memory and other resource leaks will eventually cause a program to crash. If an attacker can provoke repeated resource leaks by forcing an exception to be thrown through the submission of suitably crafted data, then the attacker can mount a denial-of-service attack.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
ERR57-CPP | Low | Probable | High | P2 | L3 |
Automated Detection
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
CodeSonar |
| ALLOC.LEAK | Leak | ||||||
Helix QAC |
| DF4756, DF4757, DF4758 | |||||||
Klocwork |
| CL.MLK | |||||||
LDRA tool suite |
| 50 D | Partially implemented | ||||||
Parasoft C/C++test |
| CERT_CPP-ERR57-a | Ensure resources are freed | |||||||
Polyspace Bug Finder |
| CERT C++: ERR57-CPP | Checks for:
|
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
SEI CERT C++ Coding Standard | MEM51-CPP. Properly deallocate dynamically allocated resources |
Bibliography
[Cline 2009] | Question 17.2, I'm still not convinced: A 4-line code snippet shows that return-codes aren't any worse than exceptions; |
[ISO/IEC 14882-2014] | Subclause 15.2, "Constructors and Destructors" |
[Meyers |
1996] | Item 9, "Use Destructors to Prevent Resource Leaks" |
[Stroustrup 2001] | "Exception-Safe Implementation Techniques" |
...
...