Versions Compared

Key

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

...

If no matching handler is found, the function std::terminate() is called; whether or not the stack is unwound before this call to std::terminate() is implementation-defined.

The default terminate handler called by std::terminate() calls std::abort(), which abnormally terminates the process. When std::abort() is called, or if the implementation does not unwind the stack prior to calling std::terminate(), destructors for objects may not be called and external resources can be left in an indeterminate state. Abnormal process termination is the typical vector for denial-of-service attacks. For more information on implicitly calling std::terminate(), see ERR50-CPP. Do not call std::terminate(), std::abort(), or std::_Exit().

All exceptions thrown by an application must be caught by a matching exception handler. Even if the exception cannot be gracefully recovered from, this ensures that the stack will be properly unwound, and provide an opportunity to gracefully manage external resources prior to terminating the process.

Noncompliant Code Example

In this noncompliant code example, the function f() does not catch exceptions thrown by throwing_func(). Since no matching handler can be found for the exception thrown, std::terminate() is called.

Code Block
bgColor#FFcccc
langcpp
void throwing_func() noexcept(false);
 
void f() {
  throwing_func();
}
 
int main() {
  f();
}

Compliant Solution

In this compliant solution, the main entrypoint handles all exceptions. This ensures that the stack is unwound up to the main() function, and allows for graceful management of external resources:

Code Block
bgColor#ccccff
langcpp
void throwing_func() noexcept(false);
 
void f() {
  throwing_func();
}
 
int main() {
  try {
    f();
  } catch (...) {
    // Handle error
  }
}

Noncompliant Code Example

In this noncompliant code example, the thread entrypoint function thread_start() does not catch exceptions thrown by throwing_func(). If the initial thread function exits due to an exception being thrown, std::terminate() is called.

Code Block
bgColor#FFcccc
langcpp
#include <thread>

void throwing_func() noexcept(false);
 
void thread_start() {
  throwing_func();
}
 
void f() {
  std::thread t(thread_start);
  t.join();
}

Compliant Solution

In this compliant solution, the thread_start() handles all exceptions and does not rethrow, allowing the thread to terminate normally:

Code Block
bgColor#ccccff
langcpp
#include <thread>

void throwing_func() noexcept(false);

void thread_start(void) {
  try {
    throwing_func();
  } catch (...) {
    // Handle error
  }
}

void f() {
  std::thread t(thread_start);
  t.join();
}

Risk Assessment

Allowing the application to abnormally terminate can lead to resources not being freed, closed, etc. It is frequently a vector for denial-of-service attacks.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

ERR31-CPP

Low

Probable

Medium

P4

L3

Automated Detection

Tool

Version

Checker

Description

 

 

 

 

Related Vulnerabilities

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

Related Guidelines

Bibliography

[ISO/IEC 14882-2014]

15.1, "Throwing an Exception"
15.3, "Handling an Exception"
15.5.1, "The std::terminate() Function"

[MISRA 08]Rule 15-3-2, "There should be at least one exception handler to catch all otherwise unhandled exceptions"
Rule 15-3-4, "Each exception explicitly thrown in the code shall have a handler of a compatible type in all call paths that could lead to that point"

...