Not all exceptions can be caught, even with careful use of function-try-blocks. The C++ Standard, [except.handle], paragraph 13 [ISO/IEC 14882-2014], states:
Exceptions thrown in destructors of objects with static storage duration or in constructors of namespace scope objects with static storage duration are not caught by a function-try-block on
main()
. Exceptions thrown in destructors of objects with thread storage duration or in constructors of namespace-scope 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, and that object is not declared within a function block scope, the type's constructor must be declared noexcept(true)
and must comply with ERR55-CPP. Honor exception specifications.
For more information on exception specifications of destructors, see DCL57-CPP. Do not let exceptions escape from destructors or deallocation functions.
Noncompliant Code Example
In this noncompliant example, the constructor for S may throw an exception that is not caught when globalS
is constructed during program startup.
struct S { S() noexcept(false); }; static S globalS;
Compliant Solution
This compliant solution makes globalS
into a local variable with static storage duration, allowing any exceptions thrown during object construction to be caught because the constructor for S
will be executed the first time the function globalS()
is called rather than at program startup. This solution does require the programmer to modify source code so that previous uses of globalS
are replaced by a function call to globalS()
.
struct S { S() noexcept(false); }; S &globalS() { try { static S s; return s; } catch (...) { // Handle error, perhaps by logging it and gracefully terminating the application. } // Unreachable. }
Noncompliant Code Example
In this noncompliant example, the constructor of global
may throw an exception during program startup (the std::string
constructor accepting a const char *
and a default allocator object is not marked noexcept(true)
and consequently allows all exceptions). This exception is not caught by the function-try-block on main()
, resulting in a call to std::terminate()
and abnormal program termination.
#include <string> static const std::string global("..."); int main() try { // ... } catch(...) { // IMPORTANT: Will not catch exceptions thrown // from the constructor of global }
Compliant Solution
Compliant code must prevent exceptions from escaping during program startup and termination. This compliant solution avoids defining a std::string
at global namespace scope and instead uses a static const char *
:
static const char *global = "..."; int main() { // ... }
Risk Assessment
Throwing an exception that cannot be caught results in abnormal program termination and can lead to denial-of-service attacks.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
ERR58-CPP | Low | Likely | Low | P9 | L2 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
Clang | 3.9 | cert-err58-cpp | Checked by clang-tidy |
Parasoft C/C++test | 9.5 | MISRA2008-15_3_1, EXCEPT-18 |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
Bibliography
[ISO/IEC 14882-2014] | Subclause 15.4, "Exception Specifications" |
[Sutter 00] | Item 8, "Writing Exception-Safe Code—Part 1" |