Skip to content

Commit 0fe3072

Browse files
authored
Merge pull request #15988 from MathiasVP/clean-up-missing-check-scanf
C++: Rewrite 'cpp/missing-check-scanf' to use standard dataflow configs
2 parents 616015f + c9dbb7c commit 0fe3072

File tree

1 file changed

+44
-68
lines changed

1 file changed

+44
-68
lines changed

cpp/ql/src/Critical/MissingCheckScanf.ql

Lines changed: 44 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,6 @@ import semmle.code.cpp.ir.IR
2121
import semmle.code.cpp.ir.ValueNumbering
2222
import ScanfChecks
2323

24-
/** Holds if `n` reaches an argument to a call to a `scanf`-like function. */
25-
pragma[nomagic]
26-
predicate revFlow0(Node n) {
27-
isSink(_, _, n, _)
28-
or
29-
exists(Node succ | revFlow0(succ) | localFlowStep(n, succ))
30-
}
31-
3224
/**
3325
* Holds if `n` represents an uninitialized stack-allocated variable, or a
3426
* newly (and presumed uninitialized) heap allocation.
@@ -38,30 +30,45 @@ predicate isUninitialized(Node n) {
3830
n.asIndirectExpr(1) instanceof AllocationExpr
3931
}
4032

41-
pragma[nomagic]
42-
predicate fwdFlow0(Node n) {
43-
revFlow0(n) and
44-
(
45-
isUninitialized(n)
46-
or
47-
exists(Node prev |
48-
fwdFlow0(prev) and
49-
localFlowStep(prev, n)
50-
)
51-
)
52-
}
53-
5433
predicate isSink(ScanfFunctionCall call, int index, Node n, Expr input) {
5534
input = call.getOutputArgument(index) and
5635
n.asIndirectExpr() = input
5736
}
5837

38+
/**
39+
* A configuration to track a uninitialized data flowing to a `scanf`-like
40+
* output parameter position.
41+
*
42+
* This is meant to be a simple flow to rule out cases like:
43+
* ```
44+
* int x = 0;
45+
* scanf(..., &x);
46+
* use(x);
47+
* ```
48+
* since `x` is already initialized it's not a security concern that `x` is
49+
* used without checking the return value of `scanf`.
50+
*
51+
* Since this flow is meant to be simple, we disable field flow and require the
52+
* source and the sink to be in the same callable.
53+
*/
54+
module UninitializedToScanfConfig implements ConfigSig {
55+
predicate isSource(Node source) { isUninitialized(source) }
56+
57+
predicate isSink(Node sink) { isSink(_, _, sink, _) }
58+
59+
FlowFeature getAFeature() { result instanceof FeatureEqualSourceSinkCallContext }
60+
61+
int accessPathLimit() { result = 0 }
62+
}
63+
64+
module UninitializedToScanfFlow = Global<UninitializedToScanfConfig>;
65+
5966
/**
6067
* Holds if `call` is a `scanf`-like call and `output` is the `index`'th
6168
* argument that has not been previously initialized.
6269
*/
6370
predicate isRelevantScanfCall(ScanfFunctionCall call, int index, Expr output) {
64-
exists(Node n | fwdFlow0(n) and isSink(call, index, n, output)) and
71+
exists(Node n | UninitializedToScanfFlow::flowTo(n) and isSink(call, index, n, output)) and
6572
// Exclude results from incorrectky checked scanf query
6673
not incorrectlyCheckedScanf(call)
6774
}
@@ -77,31 +84,6 @@ predicate isSource(ScanfFunctionCall call, int index, Node n, Expr output) {
7784
n.asDefiningArgument() = output
7885
}
7986

80-
/**
81-
* Holds if `n` is reachable from an output argument of a relevant call to
82-
* a `scanf`-like function.
83-
*/
84-
pragma[nomagic]
85-
predicate fwdFlow(Node n) {
86-
isSource(_, _, n, _)
87-
or
88-
exists(Node prev |
89-
fwdFlow(prev) and
90-
localFlowStep(prev, n) and
91-
not isSanitizerOut(prev)
92-
)
93-
}
94-
95-
/** Holds if `n` should not have outgoing flow. */
96-
predicate isSanitizerOut(Node n) {
97-
// We disable flow out of sinks to reduce result duplication
98-
isSink(n, _)
99-
or
100-
// If the node is being passed to a function it may be
101-
// modified, and thus it's safe to later read the value.
102-
exists(n.asIndirectArgument())
103-
}
104-
10587
/**
10688
* Holds if `n` is a node such that `n.asExpr() = e` and `e` is not an
10789
* argument of a deallocation expression.
@@ -112,39 +94,33 @@ predicate isSink(Node n, Expr e) {
11294
}
11395

11496
/**
115-
* Holds if `n` is part of a path from a call to a `scanf`-like function
116-
* to a use of the written variable.
97+
* A configuration to track flow from the output argument of a call to a
98+
* `scanf`-like function, and to a use of the defined variable.
11799
*/
118-
pragma[nomagic]
119-
predicate revFlow(Node n) {
120-
fwdFlow(n) and
121-
(
100+
module ScanfToUseConfig implements ConfigSig {
101+
predicate isSource(Node source) { isSource(_, _, source, _) }
102+
103+
predicate isSink(Node sink) { isSink(sink, _) }
104+
105+
predicate isBarrierOut(Node n) {
106+
// We disable flow out of sinks to reduce result duplication
122107
isSink(n, _)
123108
or
124-
exists(Node succ |
125-
revFlow(succ) and
126-
localFlowStep(n, succ) and
127-
not isSanitizerOut(n)
128-
)
129-
)
130-
}
131-
132-
/** A local flow step, restricted to relevant dataflow nodes. */
133-
private predicate step(Node n1, Node n2) {
134-
revFlow(n1) and
135-
revFlow(n2) and
136-
localFlowStep(n1, n2)
109+
// If the node is being passed to a function it may be
110+
// modified, and thus it's safe to later read the value.
111+
exists(n.asIndirectArgument())
112+
}
137113
}
138114

139-
predicate hasFlow(Node n1, Node n2) = fastTC(step/2)(n1, n2)
115+
module ScanfToUseFlow = Global<ScanfToUseConfig>;
140116

141117
/**
142118
* Holds if `source` is the `index`'th argument to the `scanf`-like call `call`, and `sink` is
143119
* a dataflow node that represents the expression `e`.
144120
*/
145121
predicate hasFlow(Node source, ScanfFunctionCall call, int index, Node sink, Expr e) {
146122
isSource(call, index, source, _) and
147-
hasFlow(source, sink) and
123+
ScanfToUseFlow::flow(source, sink) and
148124
isSink(sink, e)
149125
}
150126

0 commit comments

Comments
 (0)