CertC++-ERR58¶
Handle all exceptions thrown before main() begins executing
Required inputs: IR, StaticSemanticAnalysis
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 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 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 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 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, 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.
#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() {
// ...
}
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.
#include <exception>
#include <string>
namespace my {
struct string : std::string {
explicit string(const char *msg,
const std::string::allocator_type &alloc = std::string::allocator_type{}) noexcept
try : std::string(msg, alloc) {} catch(...) {
extern void log_message(const char *) noexcept;
log_message("std::string constructor threw an exception");
std::terminate();
}
// ...
};
}
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.
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.
#include <exception>
int f_helper() noexcept {
try {
extern int f() noexcept(false);
return f();
} catch (...) {
extern void log_message(const char *) noexcept;
log_message("f() threw an exception");
std::terminate();
}
// Unreachable.
}
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 |
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" |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
exception_escaping_initialization |
Uncaught exception raised in initialization or finalization |
None |
False |
exception_raised_in_initialization |
Exception raised in initialization or finalization |
None |
False |
Options¶
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions
exclude_exception_base_classes¶
exclude_exception_base_classes : set[bauhaus.analysis.config.QualifiedName] = set()
generate_violation_path¶
generate_violation_path : bool = True
report_at_call¶
report_at_call : bool = False
report_only_one_exception_per_function¶
report_only_one_exception_per_function : bool = False
report_only_uncaught¶
report_only_uncaught : bool = False