Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

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, states:

Exceptions thrown in destructors of objects with static storage duration or in constructors of namespace-scope namespacescope 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.

When declaring an object with static or thread

...

storage duration, the type's constructor must be declared noexcept(true) and comply with ERR37-CPP. Honor exception specifications.

For more information on exception specifications of destructors, see DCL40-CPP. Destructors and deallocation functions must be declared noexcept.

Noncompliant

...

Code Example

In the following non-compliant this noncompliant 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(the std::string constructor accepting a const char * and a default allocator object is not marked noexcept(true) and thus allows all exceptions). This exception is also not caught by the function-try-block on main(), again resulting in a call to std::terminate() and abnormal program termination.

Code Block
bgColor#FFcccc
#include <string>
 
static const std::string global("...");

voidint fmain() {
  static const struct LocalClass try {
    // ...
    ~LocalClass() { if (error_detected) throw std::runtime_error("..."); }
  } local_static;
}

int main()
try {
  f();
  // other executable statements
}
catch(...) {
  // handle exceptions thrown during the call to
  // f() and any other statements in the try block
  // above

  // IMPORTANT: will 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 VOID ERR33-CPP. Destructors must not throw exceptions.This compliant solution avoids defining a std::string at global namespace scope, and instead uses a static const char *:

Code Block
bgColor#ccccff
static const char *global[] = "...";

voidint fmain() {
  static const struct LocalClass {
    // ...
    ~LocalClass() throw() {
      if (error_detected) {
        try {
          std::clog << "Runtime error: ...\n";
        }
        catch(...) {
          // attempt to log error by other means
        }
    }
  } local_static;
}

int main()
try {
  f();
}
catch(...) {
  // handle exceptions thrown during the call to
  // f() and any other statements in the try block
  // above
}

References

...

}

Risk Assessment

Throwing an exception which cannot be caught results in abnormal program termination, and can lead to denial-of-service attacks.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

ERR41-CPP

Low

Likely

Low

P9

L2

Automated Detection

Tool

Version

Checker

Description

    

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Related Guidelines

 

Bibliography

[ISO/IEC 14882-2014]15.4, "Exception Specifications"
[Sutter 00]Item 8, "Writing Exception-Safe Code—Part 1"