Skip to content

Commit d16077d

Browse files
committed
Exclude handlers that terminate the execution on SIG_ERR
1 parent af13e6f commit d16077d

File tree

4 files changed

+64
-21
lines changed

4 files changed

+64
-21
lines changed

c/cert/src/rules/ERR32-C/DoNotRelyOnIndeterminateValuesOfErrno.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+D
200200

201201
## Implementation notes
202202

203-
None
203+
The rule is enforced in the context of a single function.
204204

205205
## References
206206

c/cert/src/rules/ERR32-C/DoNotRelyOnIndeterminateValuesOfErrno.ql

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,54 @@
1313
import cpp
1414
import codingstandards.c.cert
1515
import codingstandards.c.Errno
16+
import semmle.code.cpp.controlflow.Guards
1617

1718
class SignalCall extends FunctionCall {
1819
SignalCall() { this.getTarget().hasGlobalName("signal") }
1920
}
2021

2122
/**
22-
* Models signal handlers that call signal()
23+
* A check on `signal` call return value
24+
* `if (signal(SIGINT, handler) == SIG_ERR)`
25+
*/
26+
class SignalCheckOperation extends EqualityOperation, GuardCondition {
27+
ControlFlowNode errorSuccessor;
28+
29+
SignalCheckOperation() {
30+
this.getAnOperand() = any(MacroInvocation m | m.getMacroName() = "SIG_ERR").getExpr() and
31+
(
32+
this.getOperator() = "==" and
33+
this.controls(errorSuccessor, true)
34+
or
35+
this.getOperator() = "!=" and
36+
this.controls(errorSuccessor, false)
37+
)
38+
}
39+
40+
ControlFlowNode getErrorSuccessor() { result = errorSuccessor }
41+
}
42+
43+
/**
44+
* Models signal handlers that call signal() and return
2345
*/
2446
class SignalCallingHandler extends Function {
2547
SignalCall sh;
2648

2749
SignalCallingHandler() {
2850
// is a signal handler
2951
this = sh.getArgument(1).(FunctionAccess).getTarget() and
30-
// calls signal()
31-
this.calls*(any(SignalCall c).getTarget())
52+
// calls signal() on the handled signal
53+
exists(SignalCall sCall |
54+
sCall.getEnclosingFunction() = this and
55+
DataFlow::localFlow(DataFlow::parameterNode(this.getParameter(0)),
56+
DataFlow::exprNode(sCall.getArgument(0))) and
57+
// does not abort on error
58+
not exists(SignalCheckOperation sCheck, FunctionCall abort |
59+
DataFlow::localExprFlow(sCall, sCheck.getAnOperand()) and
60+
abort.getTarget().hasGlobalName(["abort", "_Exit"]) and
61+
abort.getEnclosingElement*() = sCheck.getErrorSuccessor()
62+
)
63+
)
3264
}
3365

3466
SignalCall getHandler() { result = sh }
@@ -37,14 +69,12 @@ class SignalCallingHandler extends Function {
3769
from ErrnoRead errno, SignalCall h
3870
where
3971
not isExcluded(errno, Contracts5Package::doNotRelyOnIndeterminateValuesOfErrnoQuery()) and
40-
// errno read in the handler
41-
exists(SignalCallingHandler sc |
72+
exists(SignalCallingHandler sc | sc.getHandler() = h |
73+
// errno read in the handler
74+
sc.calls*(errno.getEnclosingFunction())
75+
or
76+
// errno is read after the handle returns
4277
sc.getHandler() = h and
43-
(
44-
sc.calls*(errno.getEnclosingFunction())
45-
or
46-
// errno is read after the handle
47-
errno.(ControlFlowNode).getAPredecessor+() = sc.getHandler()
48-
)
78+
errno.getAPredecessor+() = h
4979
)
5080
select errno, "`errno` has indeterminate value after this $@.", h, h.toString()
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1-
| test.c:12:5:12:10 | call to perror | `errno` has indeterminate value after this $@. | test.c:32:21:32:26 | call to signal | call to signal |
2-
| test.c:26:3:26:8 | call to perror | `errno` has indeterminate value after this $@. | test.c:41:17:41:22 | call to signal | call to signal |
3-
| test.c:34:5:34:10 | call to perror | `errno` has indeterminate value after this $@. | test.c:32:21:32:26 | call to signal | call to signal |
1+
| test.c:12:5:12:10 | call to perror | `errno` has indeterminate value after this $@. | test.c:35:21:35:26 | call to signal | call to signal |
2+
| test.c:37:5:37:10 | call to perror | `errno` has indeterminate value after this $@. | test.c:35:21:35:26 | call to signal | call to signal |
3+
| test.c:42:5:42:10 | call to perror | `errno` has indeterminate value after this $@. | test.c:35:21:35:26 | call to signal | call to signal |
4+
| test.c:51:5:51:10 | call to perror | `errno` has indeterminate value after this $@. | test.c:35:21:35:26 | call to signal | call to signal |
5+
| test.c:51:5:51:10 | call to perror | `errno` has indeterminate value after this $@. | test.c:45:17:45:22 | call to signal | call to signal |

c/cert/test/rules/ERR32-C/test.c

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,21 @@ void handler1(int signum) {
1515

1616
void handler2(int signum) {
1717
pfv old_handler = signal(signum, SIG_DFL);
18-
if (old_handler == SIG_ERR) {
19-
abort(); // COMPLIANT
18+
if (old_handler != SIG_ERR) {
19+
perror(""); // COMPLIANT
20+
} else {
21+
abort();
2022
}
2123
}
2224

2325
void handler3(int signum) { pfv old_handler = signal(signum, SIG_DFL); }
2426

25-
pfv helper4(int signum) {
26-
perror(""); // NON_COMPLIANT
27-
return signal(signum, SIG_DFL);
27+
void handler4(int signum) {
28+
pfv old_handler = signal(signum, SIG_DFL);
29+
if (old_handler == SIG_ERR) {
30+
_Exit(0);
31+
}
2832
}
29-
void handler4(int signum) { pfv old_handler = helper4(signum); }
3033

3134
int main(void) {
3235
pfv old_handler = signal(SIGINT, handler1);
@@ -35,8 +38,16 @@ int main(void) {
3538
}
3639

3740
old_handler = signal(SIGINT, handler2);
41+
if (old_handler == SIG_ERR) {
42+
perror(""); // NON_COMPLIANT
43+
}
3844

3945
old_handler = signal(SIGINT, handler3);
4046

4147
old_handler = signal(SIGINT, handler4);
48+
49+
FILE *fp = fopen("something", "r");
50+
if (fp == NULL) {
51+
perror("Error: "); // NON_COMPLIANT
52+
}
4253
}

0 commit comments

Comments
 (0)