Versions Compared

Key

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

The C Standard provides three functions that cause an application to terminate normally: _Exit()exit(), and quick_exit(). These are collectively called exit functions. When the exit() function is called, or control transfers out of the main() entry point function, functions registered with atexit() are called (but not at_quick_exit()). When the quick_exit() function is called, functions registered with at_quick_exit() (but not atexit()) are called. These functions are collectively called exit handlers.  When the _Exit() function is called, no exit handlers or signal handlers are called.

Exit handlers must terminate by returning. It is important and potentially safety-critical for all exit handlers to be allowed to perform their cleanup actions. This is particularly true because the application programmer does not always know about handlers that may have been installed by support libraries. Two specific issues include nested calls to an exit function and terminating a call to an exit handler by invoking longjmp.

A nested call to an exit function is undefined behavior. (See undefined behavior 182.) This behavior can occur only when an exit function is invoked from an exit handler or when an exit function is called from within a signal handler. (See SIG30-C. Call only asynchronous-safe functions within signal handlers.)

If a call to the longjmp() function is made that would terminate the call to a function registered with atexit(), the behavior is undefined.

Noncompliant Code Example

In this noncompliant code example, the exit1() and exit2() functions are registered by atexit() to perform required cleanup upon program termination. However, if some_condition evaluates to true, exit() is called a second time, resulting in undefined behavior.

Code Block
bgColor#FFcccc
langc
#include <stdlib.h>

void exit1(void) {
  /* ... Cleanup code ... */
  return;
}
 
void exit2(void) {
  extern int some_condition;
  if (some_condition) {
    /* ... More cleanup code ... */
    exit(0);
  }
  return;
}

int main(void) {
  if (atexit(exit1) != 0) {
    /* Handle error */
  }
  if (atexit(exit2) != 0) {
    /* Handle error */
  }
  /* ... Program code ... */
  return 0;
}

Functions registered by the atexit() function are called in the reverse order from which they were registered. Consequently, if exit2() exits in any way other than by returning, exit1() will not be executed. The same may also be true for atexit() handlers installed by support libraries.

Compliant Solution

A function that is registered as an exit handler by atexit() must exit by returning, as in this compliant solution:

Code Block
bgColor#ccccFF
langc
#include <stdlib.h>

void exit1(void) {
  /* ... Cleanup code ... */
  return;
}
 
void exit2(void) {
  extern int some_condition;
  if (some_condition) {
    /* ... More cleanup code ... */
  }
  return;
}

int main(void) {
  if (atexit(exit1) != 0) {
    /* Handle error */
  }
  if (atexit(exit2) != 0) {
    /* Handle error */
  }
  /* ... Program code ... */

void exit(int status)

The exit function is used for normal program termination to occur.  

If more than one call to exit() is executed by a program, the behavior is undefined.

This type of situation typically only happens when functions are registered with atexit(), a function that causes the functions registered to it to be called with when the program exits. If a function called due to being registered with atexit() has an exit() call in it there is undefined behavior.

Non-Compliant Code Example

In this code as the main function exits two functions registered with atexit() are called. Given the define statement exitearly=1 the program calls exit within the first function called in the atexit() cycle. The behavior that follows is undefined: some implementations will ignore the second call to exit and just continue calling the atexit functions in the appropriate order. Other implementations will enter an infinite loop with the atexit functions being called repeatedly.

Code Block
bgColor#FFcccc

#include <stdio.h>
#include <stdlib.h>

#define exitearly 1

void exit1 (void)
{
  printf("Exit second.\n");

}

void exit2 (void)
{
  printf("Exit first.\n");
  if(exitearly)
  {
     exit(1);
  }
}

int main ()
{
  atexit (exit1);
  atexit (exit2);
  printf("In main\n");
  exit(1);

  return 0;
}

...

Noncompliant Code

...

To have functionality where the program can quit from within a function registered by at_exit() one has to use a function used for abnormal termination such as _exit() or abort().

From the man page of _exit():
The function _exit terminates the calling process "immediately". Any open file descriptors belonging to the process are closed; any children of the process are inherited by process 1, init, and the process's parent is sent a SIGCHLD signal.

Example

In this noncompliant code example, exit1() is registered by atexit() so that upon program termination, exit1() is called. The exit1() function jumps back to main() to return, with undefined results.

Code Block
bgColor#FFcccc
langc
#include <stdlib.h>
#include <setjmp.h>

jmp_buf env;
int val;

void exit1(void) {
  longjmp(env, 1);
}

int main(void) {
  if (atexit(exit1) != 0) {
    /* Handle error */
  }
  if (setjmp(env) == 0) {
    exit(0);
  } else {
    return 0;
  }
}

Compliant Solution

This compliant solution does not call longjmp()but instead returns from the exit handler normally:

Code Block
bgColor#ccccFF
langc
#include <stdlib.h>

void exit1(void) {
  return;
}

int main(void) {
  if (atexit(exit1) != 0) {
    /* Handle error */
  }
Code Block
bgColor#ccccFF

#include <stdio.h>
#include <stdlib.h>

#define exitearly 1

void exit1 (void)
{
  printf("Exit second.\n");

}

void exit2 (void)
{
  printf("Exit first.\n");
  if(exitearly)
  {
     _exit(1);
  }
}

int main ()
{
  atexit (exit1);
  atexit (exit2);
  printf("In main\n");
  exit(1);

  return 0;
}

Risk Assessment

Terminating a call to an exit handler in any way other than by returning is undefined behavior and may result in abnormal program termination or other unpredictable behavior. It may also prevent other registered handlers from being invoked.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

MSC31-C

1 (low)

1 (unlikely)

3(low)

P3

L3

References

ENV32-C

Medium

Likely

Medium

P12

L1

Automated Detection

Tool

Version

Checker

Description

Astrée
Include Page
Astrée_V
Astrée_V

user_defined

bad-function

bad-function-use

Soundly supported
Axivion Bauhaus Suite

Include Page
Axivion Bauhaus Suite_V
Axivion Bauhaus Suite_V

CertC-ENV32
CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

BADFUNC.ABORT
BADFUNC.EXIT
BADFUNC.LONGJMP

Use of abort
Use of exit
Use of longjmp

Compass/ROSE



Can detect violations of this rule. In particular, it ensures that all functions registered with atexit() do not call functions such as exit()

Cppcheck Premium
24.9.0
premium-cert-env32-c

Partially Implemented

Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

DF4856, DF4857, DF4858


Klocwork

Include Page
Klocwork_V
Klocwork_V

CERT.EXIT.HANDLER_TERMINATE


LDRA tool suite
Include Page
LDRA_V
LDRA_V
122 S
7 S
Enhanced enforcement
Parasoft C/C++test

Include Page
Parasoft_V
Parasoft_V

CERT_C-ENV32-a

Properly define exit handlers

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C: Rule ENV32-CChecks for abnormal termination of exit handler (rule fully covered)
RuleChecker

Include Page
RuleChecker_V
RuleChecker_V

bad-function

bad-function-use

Supported

Related Vulnerabilities

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

Related Guidelines

Key here (explains table format and definitions)

Taxonomy

Taxonomy item

Relationship

CERT C Secure Coding StandardSIG30-C. Call only asynchronous-safe functions within signal handlersPrior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013Structured Programming [EWD]Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013Termination Strategy [REU]Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11CWE-705, Incorrect Control Flow Scoping2017-07-10: CERT: Rule subset of CWE

CERT-CWE Mapping Notes

Key here for mapping notes

CWE-705 and ENV32-C

CWE-705 = Union( ENV32-C, list) where list =


  • Improper control flow besides a non-returning exit handler



...

Image Added Image Added Image Added Wiki Markup\[[ISO/IEC 9899-1999|AA. C References#ISO/IEC 9899-1999]\] Section 7.20.4.3, "The exit function"