The C++ Standard, [except.spec], paragraph 8 [ISO/IEC 14882-2014], states the following:
A function is said to allow an exception of type
E
if the constant-expression in its noexcept-specification evaluates tofalse
or its dynamic-exception-specification contains a typeT
for which a handler of typeT
would 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 cause 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.
Noncompliant Code Example
In this noncompliant code example, a function is declared as nonthrowing, but it is possible for std::vector::resize()
to throw an exception when the requested memory cannot be allocated.
#include <cstddef> #include <vector> void f(std::vector<int> &v, size_t s) noexcept(true) { v.resize(s); // May throw }
Compliant Solution
In this compliant solution, the function's noexcept-specification is removed, signifying that the function allows all exceptions.
#include <cstddef> #include <vector> void f(std::vector<int> &v, size_t s) { v.resize(s); // May throw, but that is okay }
Noncompliant Code Example
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{}; // Okay because foo() promises nothing about exceptions } void bar() throw (Exception1) { foo(); // Bad because foo() can throw Exception2 }
Compliant Solution
This compliant solution catches the exceptions thrown by foo().
#include <exception> class Exception1 : public std::exception {}; class Exception2 : public std::exception {}; void foo() { throw Exception2{}; // Okay because foo() promises nothing about exceptions } void bar() throw (Exception1) { try { foo(); } catch (Exception2 e) { // Handle error without rethrowing it } }
Compliant Solution
This compliant solution declares a dynamic exception-specification for bar()
, which covers all of the exceptions that can be thrown from it.
#include <exception> class Exception1 : public std::exception {}; class Exception2 : public std::exception {}; void foo() { throw Exception2{}; // Okay because foo() promises nothing about exceptions } void bar() throw (Exception1, Exception2) { foo(); }
Implementation Details
Some vendors provide language extensions for specifying whether or not a function throws. 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. Throwing from a function declared with one of these language extensions is presumed to be undefined behavior.
Risk Assessment
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 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
Astrée | 22.10 | unhandled-throw-noexcept | Partially checked |
Axivion Bauhaus Suite | 7.2.0 | CertC++-ERR55 | |
CodeSonar | 8.1p0 | LANG.STRUCT.EXCP.THROW | Use of throw |
Helix QAC | 2024.3 | C++4035, C++4036, C++4632 | |
LDRA tool suite | 9.7.1
| 56 D | Partially implemented |
Parasoft C/C++Test | 2023.1 | CERT_CPP-ERR55-a | Where a function's declaration includes an exception-specification, the function shall only be capable of throwing exceptions of the indicated type(s) |
Polyspace Bug Finder | R2024a | CERT C++: ERR55-CPP | Checks for noexcept functions exiting with exception (rule fully covered) |
RuleChecker | 22.10 | unhandled-throw-noexcept | Partially checked |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
Bibliography
[GNU 2016] | "Declaring Attributes of Functions" |
[ISO/IEC 14882-2014] | Subclause 15.4, "Exception Specifications" |
[MSDN 2016] | "nothrow (C++)" |
7 Comments
David Svoboda
Suppose I write a library function that involves a callback. That is, it is expected to invoke code outside the library, perhaps by another library, perhaps by the calling application. This callback could be a virtual function in an object subclass, or a lambda, or a functor, or... Finally, suppose that I have no idea what exceptions the callback might produce, but I want to pass them transparently back to the caller (I do not care to catch or process them).
Would that constitute an...er, exception to this rule? Or should I declare my function to say throw(std::exception), or noexcept(false).
An alternative would be to create my own exception wrapper class, catch all exceptions, wrap them, and throw my exception wrapper. Perhaps the 1st CS could wrap Exception2 inside Exception1?
Aaron Ballman
Let me see if I'm following along properly. You want to write a library function that calls back into the user's code, so something like:
This function honors its exception specification –
some_function()
does not promise anything regarding exceptions, so there's nothing there to honor. If you want to mark the library function such that it has the same exception contract as the function, you can do that with anoexcept
expression as part of thenoexcept
specification (aka, "this function is noexcept if that function is noexcept"), but only in C++17.For C++14, there's no way to mark
some_function()
to have an exception spec based onFunc
, but that doesn't matter for conformance to this rule since you can simply leave the exception specification off to denote that it may or may not throw, which is honoring the exception specification.I should note that if
fn(someParam)
throws, that exception will propagate back to the caller without intervention, but you may run afoul of ERR59-CPP. Do not throw an exception across execution boundaries.Joseph C. Sible
Now that the
throw(...)
exception specifications have been completely removed from C++, should we rewrite this to just be aboutnoexcept()
?Aaron Ballman
People still need to support legacy codebases that cannot yet move to C++11, so I'm on the fence about removing that content. However, it's probably not a bad idea to rearrange the text to talk about
noexcept
first rather than dynamic exception specifications.Joseph C. Sible
Good point: we should keep
throw()
for that reason, since it was useful in C++03 for the same reasonnoexcept(true)
is today. What aboutthrow
(Exception1)
though? Was that ever useful? Even if you are still on C++03, can't you just remove that without replacement in all cases?Aaron Ballman
Dynamic exception specifications were used and useful (and their failure modes differ from
noexcept
exception specifications). Removing them can (theoretically) change program behavior and degrade static analysis quality. I think we should continue to mention them since that kind of code does exist in the wild (e.g., https://codesearch.isocpp.org/actcd19/main/a/asc/asc_2.6.1.0-5/source/libs/paragui/include/ychar.h)David Svoboda
Agreed on all points. I moved the noexcept NCCE to before the throw() NCCE, since that is what we should prioritize now.