The C standard provides three functions which cause an application to terminate normally: _Exit()
, exit()
and quick_exit()
. (See ERR04-C. Choose an appropriate termination strategy.) These are collectively called exit functions. When the exit()
function is called, or control transfers out of the main()
entrypoint function, functions registered with atexit()
are called. 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.
No exit No atexit()
-registered handler should terminate in any way other than by returning. It is important and potentially safety-critical for all the atexit()
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 atexit()
-registered exit handler by invoking longjmp
.
The C exit()
function is used for normal program termination. (See ERR04-C. Choose an appropriate termination strategy.) Nested calls to an exit ()function result in undefined behavior. (See also undefined behavior 182 in Annex J.) This behavior can occur only when an exit ()function is invoked from a function registered with atexit()
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 condition
evaluates to true, exit()
is called a second time, resulting in undefined behavior.
...
Because all functions registered by the atexit()
function are called in the reverse order of their registration, 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, and not in any other manner:
Code Block | ||||
---|---|---|---|---|
| ||||
#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 ... */ return 0; } |
Noncompliant Code Example
The function exit1()
is registered by atexit()
, so upon program termination, exit1()
is called. Execution will jump back to main()
and return, with undefined results.
Code Block | ||||
---|---|---|---|---|
| ||||
#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
Careful thought about program flow is the best prevention for an invalid call to longjmp()
. After the exit
function has been called, avoid using longjmp()
where it will cause a function to terminate.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.h> void exit1(void) { return; } int main(void) { if (atexit(exit1) != 0) { /* Handle error */ } return 0; } |
Risk Assessment
Terminating a call to an atexit()
-registered exit handler in any way other than by returning results in 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 |
---|---|---|---|---|---|
ENV32-C | Medium | Likely | Medium | P12 | L1 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
|
| Can detect violations of this rule. In particular, it ensures that all functions registered with |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
CERT C Secure Coding Standard | ERR04-C. Choose an appropriate termination strategy SIG30-C. Call only asynchronous-safe functions within signal handlers |
CERT C++ Secure Coding Standard | ENV32-CPP. All atexit handlers must return normally |
ISO/IEC TR 24772:2013 | Structured Programming [EWD] Termination Strategy [REU] |
MITRE CWE | CWE-705, Incorrect control flow scoping |
...