@@ -21,14 +21,6 @@ import semmle.code.cpp.ir.IR
2121import semmle.code.cpp.ir.ValueNumbering
2222import 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-
5433predicate 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 */
6370predicate 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 */
145121predicate 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