The std::abort() and std::_Exit() functions are used to terminate the program in an immediate fashion. They do so without calling exit handlers registered with std::atexit(), and without executing destructors for objects with automatic, thread, or static storage duration. It is implementation-defined as to whether open streams with unwritten buffered data are flushed, open streams are closed, or temporary files are removed [ISO/IEC 9899:1999]. Because these functions can leave external resources in an indeterminate state, they should only be called explicitly in direct response to a critical error in the application.

The std::terminate() function calls the current terminate_handler function, which defaults to calling std::abort().

The C++ Standard defines several ways in which std::terminate() may be called implicitly by an implementation [ISO/IEC 14882-2014]:

In many circumstances, the call stack will not be unwound in response to the implicit call to std::terminate(), and in a few cases, it is implementation-defined as to whether stack unwinding will occur or not. The C++ Standard, [except.terminate], paragraph 2 [ISO/IEC 14882-2014], states, in part:

In the situation where no matching handler is found, it is implementation-defined whether or not the stack is unwound before std::terminate() is called. In the situation where the search for a handler encounters the outermost block of a function with a noexcept-specification that does not allow the exception, it is implementation-defined whether the stack is unwound, unwound partially, or not unwound at all before std::terminate() is called. In all other situations, the stack shall not be unwound before std::terminate() is called.

Do not explicitly or implicitly call std::abort() or std::_Exit(). When the default terminate_handler is installed, or the current terminate_handler responds by calling std::abort() or std::_Exit(), do not explicitly or implicitly call std::terminate(). Abnormal process termination is the typical vector for denial-of-service attacks.

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.

#include <thread>

void throwing_func() noexcept(false);
 
void thread_start(void) {
  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:

#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();
}

Noncompliant Code Example

In this noncompliant code example, the call to f(), which was registered as an exit handler with std::at_exit(), may result in a call to std::terminate() because throwing_func() may throw an exception:

#include <cstdlib>
 
void throwing_func() noexcept(false);
 
void f() {
  throwing_func();
}
 
int main() {
  if (0 != std::atexit(f)) {
    // Handle error
  }
  // ...
}

Compliant Solution

In this compliant solution, f() handles all exceptions thrown by throwing_func(), and does not rethrow:

#include <cstdlib>

void throwing_func() noexcept(false);

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

int main() {
  if (0 != std::atexit(f)) {
    // Handle error
  }
  // ...
}

Exceptions

ERR30-CPP-EX1: It is acceptable to explicitly call std::abort()std::_Exit(), or std::terminate() in response to a critical program error for which no recovery is possible, after indicating the nature of the problem to the operator, as in this example:

#include <exception>

void report(const char *msg) noexcept;
[[noreturn]] void fast_fail(const char *msg) {
  // Report error message to operator.
  report(msg);
 
  // Terminate
  std::terminate();
}
 
void critical_function_that_fails() noexcept(false);
 
void f() {
  try {
    critical_function_that_fails();
  } catch (...) {
    fast_fail("Critical function failure");
  }
}

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

ERR30-CPP

Low

Probable

Medium

P4

L3

Automated Detection

Tool

Version

Checker

Description

PRQA QA-C++

4037, 4038, 4636, 4637

 

Related Vulnerabilities

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

Related Guidelines

CERT C++ Coding Standard

ERR37-CPP. Honor exception specifications
ERR41-CPP. Constructors of objects with static or thread storage duration must not throw exceptions
DCL40-CPP. Destructors and deallocation functions must be declared noexcept

MITRE CWECWE-754, Improper Check for Unusual or Exceptional Conditions

Bibliography

[ISO/IEC 14882-2014]

15.5.1, "The std::terminate() Function"
18.5, "Start and Termination" 

[ISO/IEC 9899:1999]7.20.4.1, "The abort Function"
7.20.4.4, "The _Exit 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"