|
| 1 | +# ENV32-C: All exit handlers must return normally |
| 2 | + |
| 3 | +This query implements the CERT-C rule ENV32-C: |
| 4 | + |
| 5 | +> All exit handlers must return normally |
| 6 | +
|
| 7 | + |
| 8 | +## Description |
| 9 | + |
| 10 | +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. |
| 11 | + |
| 12 | +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`. |
| 13 | + |
| 14 | +A nested call to an exit function is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See [undefined behavior 182](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior).) 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](https://wiki.sei.cmu.edu/confluence/display/c/SIG30-C.+Call+only+asynchronous-safe+functions+within+signal+handlers).) |
| 15 | + |
| 16 | +If a call to the `longjmp()` function is made that would terminate the call to a function registered with `atexit()`, the behavior is [undefined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). |
| 17 | + |
| 18 | +## Noncompliant Code Example |
| 19 | + |
| 20 | +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](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). |
| 21 | + |
| 22 | +```cpp |
| 23 | +#include <stdlib.h> |
| 24 | + |
| 25 | +void exit1(void) { |
| 26 | + /* ... Cleanup code ... */ |
| 27 | + return; |
| 28 | +} |
| 29 | + |
| 30 | +void exit2(void) { |
| 31 | + extern int some_condition; |
| 32 | + if (some_condition) { |
| 33 | + /* ... More cleanup code ... */ |
| 34 | + exit(0); |
| 35 | + } |
| 36 | + return; |
| 37 | +} |
| 38 | + |
| 39 | +int main(void) { |
| 40 | + if (atexit(exit1) != 0) { |
| 41 | + /* Handle error */ |
| 42 | + } |
| 43 | + if (atexit(exit2) != 0) { |
| 44 | + /* Handle error */ |
| 45 | + } |
| 46 | + /* ... Program code ... */ |
| 47 | + return 0; |
| 48 | +} |
| 49 | + |
| 50 | +``` |
| 51 | +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. |
| 52 | +
|
| 53 | +## Compliant Solution |
| 54 | +
|
| 55 | +A function that is registered as an exit handler by `atexit()` must exit by returning, as in this compliant solution: |
| 56 | +
|
| 57 | +```cpp |
| 58 | +#include <stdlib.h> |
| 59 | +
|
| 60 | +void exit1(void) { |
| 61 | + /* ... Cleanup code ... */ |
| 62 | + return; |
| 63 | +} |
| 64 | + |
| 65 | +void exit2(void) { |
| 66 | + extern int some_condition; |
| 67 | + if (some_condition) { |
| 68 | + /* ... More cleanup code ... */ |
| 69 | + } |
| 70 | + return; |
| 71 | +} |
| 72 | +
|
| 73 | +int main(void) { |
| 74 | + if (atexit(exit1) != 0) { |
| 75 | + /* Handle error */ |
| 76 | + } |
| 77 | + if (atexit(exit2) != 0) { |
| 78 | + /* Handle error */ |
| 79 | + } |
| 80 | + /* ... Program code ... */ |
| 81 | + return 0; |
| 82 | +} |
| 83 | +
|
| 84 | +``` |
| 85 | + |
| 86 | +## Noncompliant Code Example |
| 87 | + |
| 88 | +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. |
| 89 | + |
| 90 | +```cpp |
| 91 | +#include <stdlib.h> |
| 92 | +#include <setjmp.h> |
| 93 | + |
| 94 | +jmp_buf env; |
| 95 | +int val; |
| 96 | + |
| 97 | +void exit1(void) { |
| 98 | + longjmp(env, 1); |
| 99 | +} |
| 100 | + |
| 101 | +int main(void) { |
| 102 | + if (atexit(exit1) != 0) { |
| 103 | + /* Handle error */ |
| 104 | + } |
| 105 | + if (setjmp(env) == 0) { |
| 106 | + exit(0); |
| 107 | + } else { |
| 108 | + return 0; |
| 109 | + } |
| 110 | +} |
| 111 | + |
| 112 | +``` |
| 113 | +
|
| 114 | +## Compliant Solution |
| 115 | +
|
| 116 | +This compliant solution does not call `longjmp()``but instead returns from the exit handler normally:` |
| 117 | +
|
| 118 | +```cpp |
| 119 | +#include <stdlib.h> |
| 120 | +
|
| 121 | +void exit1(void) { |
| 122 | + return; |
| 123 | +} |
| 124 | +
|
| 125 | +int main(void) { |
| 126 | + if (atexit(exit1) != 0) { |
| 127 | + /* Handle error */ |
| 128 | + } |
| 129 | + return 0; |
| 130 | +} |
| 131 | +
|
| 132 | +``` |
| 133 | + |
| 134 | +## Risk Assessment |
| 135 | + |
| 136 | +Terminating a call to an exit handler in any way other than by returning is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) and may result in [abnormal program termination](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-abnormaltermination) or other unpredictable behavior. It may also prevent other registered handlers from being invoked. |
| 137 | + |
| 138 | +<table> <tbody> <tr> <th> Rule </th> <th> Severity </th> <th> Likelihood </th> <th> Remediation Cost </th> <th> Priority </th> <th> Level </th> </tr> <tr> <td> ENV32-C </td> <td> Medium </td> <td> Likely </td> <td> Medium </td> <td> <strong>P12</strong> </td> <td> <strong>L1</strong> </td> </tr> </tbody> </table> |
| 139 | + |
| 140 | + |
| 141 | +## Automated Detection |
| 142 | + |
| 143 | +<table> <tbody> <tr> <th> Tool </th> <th> Version </th> <th> Checker </th> <th> Description </th> </tr> <tr> <td> <a> Axivion Bauhaus Suite </a> </td> <td> 7.2.0 </td> <td> <strong>CertC-ENV32</strong> </td> <td> </td> </tr> <tr> <td> <a> CodeSonar </a> </td> <td> 7.0p0 </td> <td> <strong>BADFUNC.ABORT</strong> <strong>BADFUNC.EXIT</strong> <strong>BADFUNC.LONGJMP</strong> </td> <td> Use of abort Use of exit Use of longjmp </td> </tr> <tr> <td> <a> Compass/ROSE </a> </td> <td> </td> <td> </td> <td> Can detect violations of this rule. In particular, it ensures that all functions registered with <code>atexit()</code> do not call functions such as <code>exit()</code> </td> </tr> <tr> <td> <a> Helix QAC </a> </td> <td> 2022.2 </td> <td> <strong>C4856, C4857, C4858</strong> <strong>C++4856, C++4857, C++4858</strong> </td> <td> </td> </tr> <tr> <td> <a> Klocwork </a> </td> <td> 2022.2 </td> <td> <strong>CERT.EXIT.HANDLER_TERMINATE</strong> </td> <td> </td> </tr> <tr> <td> <a> LDRA tool suite </a> </td> <td> 9.7.1 </td> <td> <strong>122 S7 S</strong> </td> <td> Enhanced enforcement </td> </tr> <tr> <td> <a> Parasoft C/C++test </a> </td> <td> 2022.1 </td> <td> <strong>CERT_C-ENV32-a</strong> </td> <td> Properly define exit handlers </td> </tr> <tr> <td> <a> Polyspace Bug Finder </a> </td> <td> R2022a </td> <td> <a> CERT C: Rule ENV32-C </a> </td> <td> Checks for abnormal termination of exit handler (rule fully covered) </td> </tr> </tbody> </table> |
| 144 | + |
| 145 | + |
| 146 | +## Related Vulnerabilities |
| 147 | + |
| 148 | +Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+ENV32-C). |
| 149 | + |
| 150 | +## Related Guidelines |
| 151 | + |
| 152 | +[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) |
| 153 | + |
| 154 | +<table> <tbody> <tr> <th> Taxonomy </th> <th> Taxonomy item </th> <th> Relationship </th> </tr> <tr> <td> <a> CERT C Secure Coding Standard </a> </td> <td> <a> SIG30-C. Call only asynchronous-safe functions within signal handlers </a> </td> <td> Prior to 2018-01-12: CERT: Unspecified Relationship </td> </tr> <tr> <td> <a> ISO/IEC TR 24772:2013 </a> </td> <td> Structured Programming \[EWD\] </td> <td> Prior to 2018-01-12: CERT: Unspecified Relationship </td> </tr> <tr> <td> <a> ISO/IEC TR 24772:2013 </a> </td> <td> Termination Strategy \[REU\] </td> <td> Prior to 2018-01-12: CERT: Unspecified Relationship </td> </tr> <tr> <td> <a> CWE 2.11 </a> </td> <td> <a> CWE-705 </a> , Incorrect Control Flow Scoping </td> <td> 2017-07-10: CERT: Rule subset of CWE </td> </tr> </tbody> </table> |
| 155 | + |
| 156 | + |
| 157 | +## CERT-CWE Mapping Notes |
| 158 | + |
| 159 | +[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes |
| 160 | + |
| 161 | +**CWE-705 and ENV32-C** |
| 162 | + |
| 163 | +CWE-705 = Union( ENV32-C, list) where list = |
| 164 | + |
| 165 | +* Improper control flow besides a non-returning exit handler |
| 166 | + |
| 167 | +## Implementation notes |
| 168 | + |
| 169 | +None |
| 170 | + |
| 171 | +## References |
| 172 | + |
| 173 | +* CERT-C: [ENV32-C: All exit handlers must return normally](https://wiki.sei.cmu.edu/confluence/display/c) |
0 commit comments