Skip to content

Commit 12e24a2

Browse files
committed
CPP: Exclude incorrect scanf checks from missing scanf checks
1 parent f48e8b6 commit 12e24a2

File tree

4 files changed

+40
-27
lines changed

4 files changed

+40
-27
lines changed

cpp/ql/src/Critical/IncorrectCheckScanf.ql

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,8 @@
1515

1616
import cpp
1717
import semmle.code.cpp.commons.Scanf
18-
19-
predicate exprInBooleanContext(Expr e) {
20-
e.getParent() instanceof BinaryLogicalOperation
21-
or
22-
e.getParent() instanceof UnaryLogicalOperation
23-
or
24-
e = any(IfStmt ifStmt).getCondition()
25-
or
26-
e = any(WhileStmt whileStmt).getCondition()
27-
or
28-
exists(EqualityOperation eqOp, Expr other |
29-
eqOp.hasOperands(e, other) and
30-
other.getValue() = "0"
31-
)
32-
or
33-
exists(Variable v |
34-
v.getAnAssignedValue() = e and
35-
forex(Expr use | use = v.getAnAccess() | exprInBooleanContext(use))
36-
)
37-
}
18+
import ScanfChecks
3819

3920
from ScanfFunctionCall call
40-
where
41-
exprInBooleanContext(call) and
42-
not exists(call.getTarget().getDefinition()) // ignore calls where the scanf is defined as they might be different (e.g. linux)
21+
where incorrectlyCheckedScanf(call)
4322
select call, "The result of scanf is onyl checkeck against 0, but it can also return EOF."

cpp/ql/src/Critical/MissingCheckScanf.ql

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import semmle.code.cpp.controlflow.Guards
1919
import semmle.code.cpp.dataflow.new.DataFlow::DataFlow
2020
import semmle.code.cpp.ir.IR
2121
import semmle.code.cpp.ir.ValueNumbering
22+
import ScanfChecks
2223

2324
/** Holds if `n` reaches an argument to a call to a `scanf`-like function. */
2425
pragma[nomagic]
@@ -60,7 +61,9 @@ predicate isSink(ScanfFunctionCall call, int index, Node n, Expr input) {
6061
* argument that has not been previously initialized.
6162
*/
6263
predicate isRelevantScanfCall(ScanfFunctionCall call, int index, Expr output) {
63-
exists(Node n | fwdFlow0(n) and isSink(call, index, n, output))
64+
exists(Node n | fwdFlow0(n) and isSink(call, index, n, output)) and
65+
// Exclude results from incorrectky checked scanf query
66+
not incorrectlyCheckedScanf(call)
6467
}
6568

6669
/**

cpp/ql/src/Critical/ScanfChecks.qll

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
private import cpp
2+
private import semmle.code.cpp.commons.Scanf
3+
4+
private predicate exprInBooleanContext(Expr e) {
5+
e.getParent() instanceof BinaryLogicalOperation
6+
or
7+
e.getParent() instanceof UnaryLogicalOperation
8+
or
9+
e = any(IfStmt ifStmt).getCondition()
10+
or
11+
e = any(WhileStmt whileStmt).getCondition()
12+
or
13+
exists(EqualityOperation eqOp, Expr other |
14+
eqOp.hasOperands(e, other) and
15+
other.getValue() = "0"
16+
)
17+
or
18+
exists(Variable v |
19+
v.getAnAssignedValue() = e and
20+
forex(Expr use | use = v.getAnAccess() | exprInBooleanContext(use))
21+
)
22+
}
23+
24+
private predicate isLinuxKernel() {
25+
exists(Macro macro | macro.getName() = "_LINUX_KERNEL_SPRINTF_H_")
26+
}
27+
28+
/**
29+
* Holds if `call` is a `scanf`-like call were the result is only checked against 0, but it can also return EOF.
30+
*/
31+
predicate incorrectlyCheckedScanf(ScanfFunctionCall call) {
32+
exprInBooleanContext(call) and
33+
not isLinuxKernel() // scanf in the linux kernel can't return EOF
34+
}

cpp/ql/test/query-tests/Critical/MissingCheckScanf/MissingCheckScanf.expected

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@
55
| test.cpp:98:7:98:8 | * ... | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:97:3:97:7 | call to scanf | call to scanf |
66
| test.cpp:108:7:108:7 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:107:3:107:8 | call to fscanf | call to fscanf |
77
| test.cpp:115:7:115:7 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:114:3:114:8 | call to sscanf | call to sscanf |
8-
| test.cpp:164:8:164:8 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:162:7:162:11 | call to scanf | call to scanf |
9-
| test.cpp:173:8:173:8 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:171:7:171:11 | call to scanf | call to scanf |
10-
| test.cpp:205:8:205:8 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:204:7:204:11 | call to scanf | call to scanf |
118
| test.cpp:224:8:224:8 | j | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 2. | test.cpp:221:7:221:11 | call to scanf | call to scanf |
129
| test.cpp:248:9:248:9 | d | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 2. | test.cpp:246:25:246:29 | call to scanf | call to scanf |
1310
| test.cpp:252:9:252:9 | d | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 2. | test.cpp:250:14:250:18 | call to scanf | call to scanf |

0 commit comments

Comments
 (0)