Skip to content

Commit 68f5724

Browse files
authored
Merge branch 'main' into automation/version-bump-2.11.0-dev
2 parents 13d4dea + d734833 commit 68f5724

File tree

40 files changed

+1697
-90
lines changed

40 files changed

+1697
-90
lines changed

c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql

Lines changed: 4 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -13,57 +13,10 @@
1313

1414
import cpp
1515
import codingstandards.c.cert
16-
import semmle.code.cpp.dataflow.DataFlow
17-
import DataFlow::PathGraph
16+
import codingstandards.cpp.rules.constlikereturnvalue.ConstLikeReturnValue
1817

19-
/*
20-
* Call to functions that return pointers to environment objects that should not be modified.
21-
*/
22-
23-
class NotModifiableCall extends FunctionCall {
24-
NotModifiableCall() {
25-
this.getTarget()
26-
.hasGlobalName(["getenv", "setlocale", "localeconv", "asctime", "strerror"])
27-
}
28-
}
29-
30-
/*
31-
* An expression that modifies an object.
32-
*/
33-
34-
class ObjectWrite extends Expr {
35-
ObjectWrite() {
36-
// the pointed object is reassigned
37-
exists(Expr e |
38-
e = [any(AssignExpr ae).getLValue(), any(CrementOperation co).getOperand()] and
39-
(
40-
this = e.(PointerDereferenceExpr).getOperand()
41-
or
42-
this = e.(PointerFieldAccess).getQualifier()
43-
)
44-
)
18+
class DoNotModifyTheReturnValueOfCertainFunctionsQuery extends ConstLikeReturnValueSharedQuery {
19+
DoNotModifyTheReturnValueOfCertainFunctionsQuery() {
20+
this = Contracts1Package::doNotModifyTheReturnValueOfCertainFunctionsQuery()
4521
}
4622
}
47-
48-
/**
49-
* DF configuration for flows from a `NotModifiableCall` to a object modifications.
50-
*/
51-
class DFConf extends DataFlow::Configuration {
52-
DFConf() { this = "DFConf" }
53-
54-
override predicate isSource(DataFlow::Node source) {
55-
source.asExpr() instanceof NotModifiableCall
56-
}
57-
58-
override predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof ObjectWrite }
59-
}
60-
61-
from DataFlow::PathNode source, DataFlow::PathNode sink
62-
where
63-
not isExcluded(sink.getNode().asExpr(),
64-
Contracts1Package::doNotModifyTheReturnValueOfCertainFunctionsQuery()) and
65-
// the modified object comes from a call to one of the ENV functions
66-
any(DFConf d).hasFlowPath(source, sink)
67-
select sink.getNode(), source, sink,
68-
"The object returned by the function " +
69-
source.getNode().asExpr().(FunctionCall).getTarget().getName() + " should no be modified."
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
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)
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/**
2+
* @id c/cert/exit-handlers-must-return-normally
3+
* @name ENV32-C: All exit handlers must return normally
4+
* @description Exit handlers must terminate by returning, as a nested call to an exit function is
5+
* undefined behavior.
6+
* @kind path-problem
7+
* @precision very-high
8+
* @problem.severity error
9+
* @tags external/cert/id/env32-c
10+
* correctness
11+
* external/cert/obligation/rule
12+
*/
13+
14+
import cpp
15+
import codingstandards.c.cert
16+
17+
class ExitFunction extends Function {
18+
ExitFunction() { this.hasGlobalName(["_Exit", "exit", "quick_exit", "longjmp"]) }
19+
}
20+
21+
class ExitFunctionCall extends FunctionCall {
22+
ExitFunctionCall() { this.getTarget() instanceof ExitFunction }
23+
}
24+
25+
class RegisteredAtexit extends FunctionAccess {
26+
RegisteredAtexit() {
27+
exists(FunctionCall ae |
28+
this = ae.getArgument(0).(FunctionAccess) and
29+
ae.getTarget().hasGlobalName(["atexit", "at_quick_exit"])
30+
)
31+
}
32+
}
33+
34+
/**
35+
* Nodes of type Function, FunctionCall or FunctionAccess that \
36+
* are reachable from a redistered atexit handler and
37+
* can reach an exit function.
38+
*/
39+
class InterestingNode extends ControlFlowNode {
40+
InterestingNode() {
41+
exists(Function f |
42+
(
43+
this = f and
44+
// exit functions are not part of edges
45+
not this = any(ExitFunction ec)
46+
or
47+
this.(FunctionCall).getTarget() = f
48+
or
49+
this.(FunctionAccess).getTarget() = f
50+
) and
51+
// reaches an exit function
52+
f.calls*(any(ExitFunction e)) and
53+
// is reachable from a registered atexit function
54+
exists(RegisteredAtexit re | re.getTarget().calls*(f))
55+
)
56+
}
57+
}
58+
59+
/**
60+
* An `edges` predicate to support the `path-problem` query. Reports edges between
61+
* `FunctionAccess`/`FunctionCall` nodes and their target `Function` and between
62+
* `Function` and `FunctionCall` in their body.
63+
*/
64+
query predicate edges(InterestingNode a, InterestingNode b) {
65+
a.(FunctionAccess).getTarget() = b
66+
or
67+
a.(FunctionCall).getTarget() = b
68+
or
69+
a.(Function).calls(_, b)
70+
}
71+
72+
from RegisteredAtexit hr, Function f, ExitFunctionCall e
73+
where edges(hr, f) and edges+(f, e)
74+
select f, hr, e, "The function is $@ and $@. It must instead terminate by returning.", hr,
75+
"registered as `exit handler`", e, "calls an `exit function`"

0 commit comments

Comments
 (0)