Versions Compared

Key

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

The std::abort(), std::quick_exit(), 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. How a system manages open streams when a program ends is implementation-defined [ISO/IEC 9899:1999]. Open streams with unwritten buffered data may or may not be flushed, open streams may or may not be closed, and temporary files may or may not be removed. Because these functions can leave external resources, such as files and network communications, in an indeterminate state, they should be called explicitly only in direct response to a critical error in the application. (See ERR50-CPP-EX1 for more information.)

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 Thrown exceptions that are not explicitly caught subject a running program to implementation-defined behavior culminating in abnormal termination. According to section 15.3 "Handling an Exception" of C++2003 [ISO/IEC 14882-20032014]:

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

The effects of std::terminate() are to call the terminate_handler function in effect immediately after evaluating the throw-expression. The default terminate_handler calls std::abort(), which has the effect of causing abnormal process termination to occur. Abnormal process termination is the typical vector for denial of service attacks. A user-defined terminate_handler may be set by calling std::set_terminate(). In either case, std::terminate() must not return. Attempting to return from a user-defined terminate_handler or from a SIGABRT handler invoked as a result of calling std::abort() leads to undefined behavior.

Consequently, programs should take steps to prevent std::terminate() from being invoked for at least two reasons:

  1. If the stack is not unwound then destructors of local objects are not invoked, acquired system-wide or application-wide resources may not be released, file buffers are not flushed, database transactions are not committed or rolled back, etc.
  2. Since failing to catch an exception involves implementation-defined behavior, to comply MSC14-CPP. Do not introduce unnecessary platform dependencies.

Rather, programs should catch all exceptions and attempt to recover at the earliest opportunity. Under the rare circumstances when recovery is not feasible (for example, when a logic error is detected), programs should gracefully terminate after indicating the nature of the problem to the operator. See also ERR04-CPP. Choose an appropriate termination strategy.

Non-Compliant Code Example (main())

In this example, main() does several useful work but does not catch any exceptions. Consequently, any exceptions thrown will call std::terminate(), and might not destroy any objects owned by the program.

Code Block
bgColor#FFcccc

int main(int argc, char** argv) {
  Object object; // might not get destroyed if exception thrown
  // do useful work
  return 0;
}

Compliant Solution (main())

In this code example, all exceptions are caught, allowing normal termination, even in the face of unexpected errors (albeit with an exit status indicating that an error occurred).

  1. When the exception handling mechanism, after completing the initialization of the exception object but before activation of a handler for the exception, calls a function that exits via an exception ([except.throw], paragraph 7)
  2. When a throw-expression with no operand attempts to rethrow an exception and no exception is being handled ([except.throw], paragraph 9)
  3. When the exception handling mechanism cannot find a handler for a thrown exception ([except.handle], paragraph 9)
  4. When the search for a handler encounters the outermost block of a function with a noexcept-specification that does not allow the exception ([except.spec], paragraph 9)
  5. When the destruction of an object during stack unwinding terminates by throwing an exception ([except.ctor], paragraph 3)
  6. When initialization of a nonlocal variable with static or thread storage duration exits via an exception ([basic.start.init], paragraph 6)
  7. When destruction of an object with static or thread storage duration exits via an exception ([basic.start.term], paragraph 1)
  8. When execution of a function registered with std::atexit()or std::at_quick_exit() exits via an exception ([support.start.term], paragraphs 8 and 12)
  9. When the implementation’s default unexpected exception handler is called ([except.unexpected], paragraph 2)
    Note that std::unexpected() is currently deprecated.
  10. When std::unexpected() throws an exception that is not allowed by the previously violated dynamic-exception-specification, and std::bad_exception() is not included in that dynamic-exception-specification ([except.unexpected], paragraph 3)
  11. When the function std::nested_exception::rethrow_nested() is called for an object that has captured no exception ([except.nested], paragraph 4)
  12. When execution of the initial function of a thread exits via an exception ([thread.thread.constr], paragraph 5)
  13. When the destructor is invoked on an object of type std::thread that refers to a joinable thread ([thread.thread.destr], paragraph 1)
  14. When the copy assignment operator is invoked on an object of type std::thread that refers to a joinable thread ([thread.thread.assign], paragraph 1)
  15. When calling condition_variable::wait()condition_variable::wait_until(), or condition_variable::wait_for() results in a failure to meet the postcondition: lock.owns_lock() == true or lock.mutex() is not locked by the calling thread ([thread.condition.condvar], paragraphs 11, 16, 21, 28, 33, and 40)
  16. When calling condition_variable_any::wait()condition_variable_any::wait_until(), or condition_variable_any::wait_for() results in a failure to meet the postcondition: lock is not locked by the calling thread ([thread.condition.condvarany], paragraphs 11, 16, and 22)

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

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::quick_exit(),  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.

The std::exit() function is more complex. The C++ Standard, [basic.start.main], paragraph 4, states:

Terminating the program without leaving the current block (e.g., by calling the function std::exit(int) (17.5)) does not destroy any objects with automatic storage duration (11.4.6). If std::exit is called to end a program during the destruction of an object with static or thread storage duration, the program has undefined behavior.

You may call std::exit() only in a program that has not yet initialized any objects with automatic storage duration.

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.

Code Block
bgColor#FFcccc
langcpp
#include <cstdlib>
 
void throwing_func() noexcept(false);
 
void f() { // Not invoked by the program except as an exit handler.
  throwing_func();
}
 
int main() {
  if (0 != std::atexit(f)) 
Code Block
bgColor#ccccff

int main(int argc, char** argv) {
  Object object;
  int exit_status = EXIT_SUCCESS;

  try {
    // doHandle useful workerror
  } catch (...) {
    exit_status = EXIT_FAILURE;
  }

  return exit_status; // object gets destroyed here...
}

Compliant Solution

...

In this compliant solution, f() handles all exceptions thrown by throwing_func() and does not rethrowAn alternative is to wrap all of main()'s functionality inside a try-catch block and catch and handle exceptions by exiting with a status indicating an error to the invoking process.

Code Block
bgColor#ccccff
langcpp
#include <cstdlib>

void throwing_func() noexcept(false);

void f() { // Not invoked by the program except as an exit handler.
  try 
int main(int argc, char** argv) {
  try {
    Object object;
    // do useful work
    return 0; // object gets destroyed here
  } catch (...) {
    exit(EXIT_FAILUREthrowing_func();
  
}  }
}

Non-Compliant Code Example (throw() Declaration)

A function that declares exception specifications must list all unrelated exception classes that might be thrown during its invocation. If an exception is thrown that is not related to any of those listed in its exception specification, control automatically reverts to std::unexpected(), which does not return.

In the following code example, the function f() claims to throw exception1 but actually throws exception2. Consequently control flow is diverted to std::unexpected, and the toplevel catch clause may not be invoked. (It is not invoked on Linux with G++ 4.3).

Code Block
bgColor#FFcccc

using namespace std;
class exception1 : public exception {};
class exception2 : public exception {};

void f(void) throw( exception1) {
  // ...
  throw (exception2());catch (...) {
    // Handle error
  }
}

int main() {
  try {
    f();
    return 0;
  } catch (...if (0 != std::atexit(f)) {
    cerr// << "F called" << endl;Handle error
  }
  return EXIT_FAILURE;// ...
}

Compliant Solution (throw() Declaration)

Exceptions

ERR50-CPP-EX1: It is acceptable, after indicating the nature of the problem to the operator, to explicitly call std::abort()std::_Exit(), or std::terminate() in response to a critical program error for which no recovery is possible, as in this example.The following code example declares the same exception it actually throws

Code Block
bgColor#ccccff
langcpp
#include <exception>

void report(const char *msg) noexcept;
[[noreturn]] void fast_fail(const char *msg) {
  // Report error message to operator
  report(msg);
 
using namespace std;
class exception1 : public exception {};
class exception2 : public exception {};

void f(void) throw( exception1) {
  // ...Terminate
  throw (exception1());
}

int mainstd::terminate();
}
 
void critical_function_that_fails() noexcept(false);
 
void f() {
  try {
    fcritical_function_that_fails();
    return 0;
  } catch (...) {
    cerr << "F called" << endlfast_fail("Critical function failure");
  }
  return EXIT_FAILURE;
}
}

The assert() macro is permissible under this exception because failed assertions will notify the operator on the standard error stream in an implementation-defined manner before calling std::abort().

Risk Assessment

Failing to handle exceptions Allowing the application to abnormally terminate can lead to resources not being freed, closed, etcand so on. It is frequently a vector for denial-of-service attacks.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

ERR30

ERR50-CPP

low

Low

unlikely

Probable

low

Medium

P3

L3

Other Languages

This rule appears in the Java Secure Coding Standard as EXC08-J. Try to gracefully recover from system errors.

Bibliography

Wiki Markup
\[[ISO/IEC 14882-2003|AA. Bibliography#ISO/IEC 14882-2003]\]
\[[MISRA 08|AA. Bibliography#MISRA 08]\] Rule 15-3-2, 15-3-4

P4

L3

Automated Detection

Tool

Version

Checker

Description

Astrée

Include Page
Astrée_V
Astrée_V

stdlib-use
Partially checked
CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

BADFUNC.ABORT
BADFUNC.EXIT

Use of abort
Use of exit

Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C++5014
Klocwork
Include Page
Klocwork_V
Klocwork_V
MISRA.TERMINATE
CERT.ERR.ABRUPT_TERM

LDRA tool suite
Include Page
LDRA_V
LDRA_V

122 S

Enhanced Enforcement

Parasoft C/C++test

Include Page
Parasoft_V
Parasoft_V

CERT_CPP-ERR50-a
CERT_CPP-ERR50-b
CERT_CPP-ERR50-c
CERT_CPP-ERR50-d
CERT_CPP-ERR50-e
CERT_CPP-ERR50-f
CERT_CPP-ERR50-g
CERT_CPP-ERR50-h
CERT_CPP-ERR50-i
CERT_CPP-ERR50-j
CERT_CPP-ERR50-k
CERT_CPP-ERR50-l
CERT_CPP-ERR50-m
CERT_CPP-ERR50-n

The execution of a function registered with 'std::atexit()' or 'std::at_quick_exit()' should not exit via an exception
Never allow an exception to be thrown from a destructor, deallocation, and swap
Do not throw from within destructor
There should be at least one exception handler to catch all otherwise unhandled exceptions
An empty throw (throw;) shall only be used in the compound-statement of a catch handler
Exceptions shall be raised only after start-up and before termination of the program
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
Where a function's declaration includes an exception-specification, the function shall only be capable of throwing exceptions of the indicated type(s)
Function called in global or namespace scope shall not throw unhandled exceptions
Always catch exceptions
Properly define exit handlers
The 'abort()' function from the 'stdlib.h' or 'cstdlib' library shall not be used
Avoid throwing exceptions from functions that are declared not to throw
The 'quick_exit()' and '_Exit()' functions from the 'stdlib.h' or 'cstdlib' library shall not be used

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C++: ERR50-CPPChecks for implicit call to terminate() function (rule partially covered)
PVS-Studio

Include Page
PVS-Studio_V
PVS-Studio_V

V667, V2014
RuleChecker
Include Page
RuleChecker_V
RuleChecker_V
stdlib-use
Partially checked
SonarQube C/C++ Plugin
Include Page
SonarQube C/C++ Plugin_V
SonarQube C/C++ Plugin_V
S990

Related Vulnerabilities

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

Related Guidelines

Bibliography

[ISO/IEC 9899-2011]Subclause 7.20.4.1, "The abort Function"
Subclause 7.20.4.4, "The _Exit Function"
[ISO/IEC 14882-2014]

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

[MISRA 2008]Rule 15-3-2 (Advisory)
Rule 15-3-4 (Required)


...

Image Added Image Added Image Added ERR14-CPP. Do not allow an exception class's copy constructor to throw exceptions      12. Exceptions and Error Handling (ERR)      ERR31-CPP. Don't redefine errno