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 [ISO/IEC 14882-2014], states the following:
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 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.
Non-Compliant Code Example
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
and must comply with ERR55-CPP. Honor exception specifications. Additionally, the initializer for such a declaration, if any, must not throw an uncaught exception (including from any implicitly constructed objects that are created as a part of the initialization). If an uncaught exception is thrown before main()
is executed, or if an uncaught exception is thrown after main()
has finished executing, there are no further opportunities to handle the exception and it results in implementation-defined behavior. (See ERR50-CPP. Do not abruptly terminate the program for further details.)
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 startupIn the following non-compliant 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. This exception is also not caught by the function-try-block on main()
, again resulting in a call to std::terminate()
.
Code Block | ||||
---|---|---|---|---|
| ||||
struct S { static const std::string global("..." S() noexcept(false); void f() { static const struct LocalClass { // ... ~LocalClass() { if (error_detected) throw std::runtime_error("..."); } } local_static; } int main() try { f(); // other executable statements } catch}; 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()
.
Code Block | ||||
---|---|---|---|---|
| ||||
struct S { S() noexcept(false); }; S &globalS() { try { static S s; return s; } catch (...) { // handle exceptions thrown duringHandle error, perhaps by logging it and gracefully terminating the call toapplication. } // Unreachable. } |
Noncompliant Code Example
In this noncompliant example, the constructor of global
may throw an exception during program startup. (The std::string
constructor, which accepts a const char *
and a default allocator object, is not marked noexcept
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.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <string> static const std::string global("..."); int main() try {f() and any other statements in the try block // above ... } catch(...) { // IMPORTANT: willWill 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 ERR33-CPP. Destructors must not throw exceptionsThis 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() { // ... } |
Compliant Solution
This compliant solution introduces a class derived from std::string
with a constructor that catches all exceptions with a function try block and terminates the application in accordance with ERR50-CPP-EX1 in ERR50-CPP. Do not abruptly terminate the program in the event any exceptions are thrown. Because no exceptions can escape the constructor, it is marked noexcept
and the class type is permissible to use in the declaration or initialization of a static global variable.
For brevity, the full interface for such a type is not described.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <exception> #include <string> namespace my const struct LocalClass { struct string : // ...std::string { explicit ~LocalClassstring()const throw() {char *msg, if (error_detected) { try { const std::string::allocator_type &alloc = std::string::allocator_type{}) noexcept try : std::clog << "Runtime error: ...\n";string(msg, alloc) {} catch(...) { extern void log_message(const char }*) noexcept; log_message("std::string constructor threw catch(...) {an exception"); std::terminate(); } // attempt to log error by other means } } } local_static; } int main() try { f(); } catch... }; } static const my::string global("..."); int main() { // ... } |
Noncompliant Code Example
In this noncompliant example, an exception may be thrown by the initializer for the static global variable i
.
Code Block | ||||
---|---|---|---|---|
| ||||
extern int f() noexcept(false);
int i = f();
int main() {
// ...
} |
Compliant Solution
This compliant solution wraps the call to f()
with a helper function that catches all exceptions and terminates the program in conformance with ERR50-CPP-EX1 of ERR50-CPP. Do not abruptly terminate the program.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <exception> int f_helper() noexcept { try { extern int f() noexcept(false); return f(); } catch (...) { // handle exceptionsextern thrown during the call to // void log_message(const char *) noexcept; log_message("f() and any other statements in the try blockthrew an exception"); std::terminate(); } // aboveUnreachable. } |
References
int i = f_helper();
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 | ||||||
---|---|---|---|---|---|---|---|---|---|
Astrée |
| potentially-throwing-static-initialization | Partially checked | ||||||
Axivion Bauhaus Suite |
| CertC++-ERR58 | |||||||
Clang |
| cert-err58-cpp | Checked by clang-tidy | ||||||
CodeSonar |
| LANG.STRUCT.EXCP.THROW | Use of throw | ||||||
Helix QAC |
| C++4634, C++4636, C++4637, C++4639 | |||||||
Parasoft C/C++test |
| CERT_CPP-ERR58-a | Exceptions shall be raised only after start-up and before termination of the program | ||||||
Polyspace Bug Finder |
| CERT C++: ERR58-CPP | Checks for exceptions raised during program startup (rule fully covered) | ||||||
RuleChecker |
| potentially-throwing-static-initialization | Partially checked |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
This rule is a subset of ERR50-CPP. Do not abruptly terminate the program
SEI CERT C++ Coding Standard | DCL57-CPP. Do not let exceptions escape from destructors or deallocation functions ERR55-CPP. Honor exception specifications |
Bibliography
[ISO/IEC 14882-2014] | Subclause 15.4, "Exception Specifications" |
[Sutter 2000] | Item 8, "Writing Exception-Safe Code—Part 1" |
...
[Sutter 00] Sutter, Herb. Exceptional C++: 47 Engineering Puzzles, Programming Problems, and Solutions.