According to the C++ Standard, [except.spec] paragraph 8 [ISO/IEC 14882-2014]:
A function is said to allow an exception of type
Eif the constant-expression in its noexcept-specification evaluates tofalseor its dynamic-exception-specification contains a typeTfor which a handler of typeTwould be a match (15.3) for an exception of typeE.
If a function throws an exception other than one allowed by its exception-specification it can lead to an implementation-defined termination of the program ([except.spec] paragraph 9).
If a function declared with a dynamic-exception-specification throws an exception of a type that would not match the exception-specification, the function std::unexpected() is called. The behavior of this function can be overridden, but by default causes an exception of std::bad_exception to be thrown. Unless std::bad_exception is listed in the exception-specification, the function std::terminate() will be called.
Similarly, if a function declared with a noexcept-specification throws an exception of a type that would lead to the noexcept-specification to evaluate to false, the function std::terminate() will be called.
Calling std::terminate() leads to implementation-defined termination of the program. To prevent abnormal termination of the program, any function that declares an exception-specification should restrict itself, as well as any functions it calls, to throwing only allowed exceptions.
In this noncompliant code example, the second function claims to throw only exception1, but it may also throw exception2.
#include <exception>
class exception1 : public std::exception {};
class exception2 : public std::exception {};
void foo() {
throw exception2; // OK, since foo() promises nothing wrt exceptions
}
void bar() throw (exception1) {
foo(); // Bad, since foo() can throw exception2
}
|
This compliant solution catches the exceptions thrown by foo():
void bar() throw (exception1) {
try {
foo();
} catch (exception2 e) {
// handle error, without re-throwing it.
}
}
|
This compliant solution declares an exception-specification for bar() which covers all of the exceptions that can be thrown from it:
void bar() throw (exception1, exception2) {
foo();
} |
In this noncompliant code example, a function is declared as non-throwing, but it is possible for std::vector::resize() to throw an exception when the requested memory cannot be allocated:
#include <vector>
void f(std::vector<int> &v, size_t s) noexcept(true) {
v.resize(s); // May throw
}
|
In this compliant solution, the function's noexcept-specification is removed, signifying that the function allows all exceptions:
#include <vector>
void f(std::vector<int> &v, size_t s) {
v.resize(s); // May throw, but that is OK.
} |
Some vendors provide language extensions for specifying whether a function throws or not. For instance, Microsoft Visual Studio provides __declspec(nothrow)), and Clang supports __attribute__((nothrow)). Currently, the vendors do not document the behavior of specifying a nonthrowing function using these extensions. It is presumed that it is undefined behavior when throwing from a function declared with one of these language extensions.
Throwing unexpected exceptions disrupts control flow and can cause premature termination and denial of service.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
ERR55-CPP | Low | Likely | Low | P9 | L2 |
Tool | Version | Checker | Description |
|---|---|---|---|
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
| CERT C++ Coding Standard | ERR50-CPP. Do not call std::terminate(), std::abort(), or std::_Exit() |
| [ISO/IEC 14882-2014] | 15.4, "Exception Specifications" |
| [MSDN] | nothrow (C++) |
| [GNU] | Declaring Attributes of Functions |