Not all exceptions can be caught. Quoting from , even with careful use of function try blocks. The C++ Standard, [except.handle], p13 of N3000, the current Working Draft of the C++ standardparagraph 13, states:
Exceptions thrown in destructors of objects with static storage duration or in constructors of namespace-scope namespacescope objects with static storage duration are not caught by a function-try-block on
main()
. Exceptions thrown Exceptions thrown in destructors of objects with thread storage duration or in constructors of namespace-scope objects with objects with thread storage duration are not caught by a function-try-block on the initial function of the thread.
When declaring an object with static or thread
...
storage duration, the type's constructor must be declared noexcept(true)
and comply with ERR37-CPP. Honor exception specifications.
For more information on exception specifications of destructors, see DCL40-CPP. Destructors and deallocation functions must be declared noexcept.
Noncompliant
...
Code Example
In the following non-compliant this noncompliant example, the constructor of global
may throw an exception during program startup . This exception is not caught by the function-try-block on main()
, resulting in a call to std::terminate()
. Similarly, the destructor of the local_static
object defined in function f(()
may throw an exception during program termination(the std::string
constructor accepting a const char *
and a default allocator object is not marked noexcept(true)
and thus allows all exceptions). This exception is also not caught by the function-try-block on main()
, again resulting in a call to std::terminate()
and abnormal program termination.
Code Block | ||
---|---|---|
| ||
#include <string> static const std::string global("..."); voidint fmain() { static const struct LocalClass try { // ... ~LocalClass() { if (error_detected) throw std::runtime_error("..."); } } local_static; } int main() try { f(); // other executable statements } catch(...) { // handle exceptions thrown during the call to // f() and any other statements in the try block // above // IMPORTANT: will not catch exceptions thrown // from the constructor of global or those from // the destructor of local_static defined in f() // and invoked during program termination . } |
Compliant Solution
Compliant code must prevent exceptions from escaping during program startup and termination. A solution is to avoid defining at namespace scope objects whose constructors may throw, in addition to preventing exceptions from being thrown from destructors as outlined in VOID ERR33-CPP. Destructors must not throw exceptions.This compliant solution avoids defining a std::string
at global namespace scope, and instead uses a static const char *
:
Code Block | ||
---|---|---|
| ||
static const char *global[] = "..."; voidint fmain() { static const struct LocalClass { // ... ~LocalClass() throw() { if (error_detected) { try { std::clog << "Runtime error: ...\n"; } catch(...) { // attempt to log error by other means } } } local_static; } int main() try { f(); } catch(...) { // handle exceptions thrown during the call to // f() and any other statements in the try block // above } |
References
...
}
|
Risk Assessment
Throwing an exception which cannot be caught results in abnormal program termination, and can lead to denial-of-service attacks.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
ERR41-CPP | Low | Likely | Low | P9 | L2 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
CERT C++ Coding Standard | ERR37-CPP. Honor exception specifications DCL40-CPP. Destructors and deallocation functions must be declared noexcept |
Bibliography
[ISO/IEC 14882-2014] | 15.4, "Exception Specifications" |
[Sutter 00] | Item 8, "Writing Exception-Safe Code—Part 1" |