Skip to content

Commit 94c625f

Browse files
author
Robert Marsh
authored
Merge pull request #1777 from jbj/ast-field-flow-defbyref
C++: Don't use definitionByReference for data flow
2 parents 33329f9 + 79c713b commit 94c625f

File tree

5 files changed

+60
-13
lines changed

5 files changed

+60
-13
lines changed

change-notes/1.23/analysis-cpp.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ The following changes in version 1.23 affect C/C++ analysis in all applications.
2828
picture of the partial flow paths from a given source. The feature is
2929
disabled by default and can be enabled for individual configurations by
3030
overriding `int explorationLimit()`.
31+
* The `DataFlow::DefinitionByReferenceNode` class now considers `f(x)` to be a
32+
definition of `x` when `x` is a variable of pointer type. It no longer
33+
considers deep paths such as `f(&x.myField)` to be definitions of `x`. These
34+
changes are in line with the user expectations we've observed.
3135
* There is now a `DataFlow::localExprFlow` predicate and a
3236
`TaintTracking::localExprTaint` predicate to make it easy to use the most
3337
common case of local data flow and taint: from one `Expr` to another.

cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ private module PartialDefinitions {
105105
)
106106
} or
107107
TExplicitCallQualifier(Expr qualifier, Call call) { qualifier = call.getQualifier() } or
108-
TReferenceArgument(Expr arg, VariableAccess va) { definitionByReference(va, arg) }
108+
TReferenceArgument(Expr arg, VariableAccess va) { referenceArgument(va, arg) }
109109

110110
private predicate isInstanceFieldWrite(FieldAccess fa, ControlFlowNode node) {
111111
not fa.getTarget().isStatic() and
@@ -154,18 +154,48 @@ private module PartialDefinitions {
154154
}
155155

156156
/**
157-
* A partial definition that's a definition by reference (in the sense of the
158-
* `definitionByReference` predicate).
157+
* A partial definition that's a definition by reference.
159158
*/
160159
class DefinitionByReference extends PartialDefinition, TReferenceArgument {
161160
VariableAccess va;
162161

163-
DefinitionByReference() { definitionByReference(va, definedExpr) }
162+
DefinitionByReference() {
163+
// `this` is not restricted in this charpred. That's because the full
164+
// extent of this class includes the charpred of the superclass, which
165+
// relates `this` to `definedExpr`, and `va` is functionally determined
166+
// by `definedExpr`.
167+
referenceArgument(va, definedExpr)
168+
}
164169

165170
VariableAccess getVariableAccess() { result = va }
166171

167172
override predicate partiallyDefines(Variable v) { va = v.getAnAccess() }
168173
}
174+
175+
private predicate referenceArgument(VariableAccess va, Expr argument) {
176+
argument = any(Call c).getAnArgument() and
177+
exists(Type argumentType |
178+
argumentType = argument.getFullyConverted().getType().stripTopLevelSpecifiers()
179+
|
180+
argumentType instanceof ReferenceType and
181+
not argumentType.(ReferenceType).getBaseType().isConst() and
182+
va = argument
183+
or
184+
argumentType instanceof PointerType and
185+
not argumentType.(PointerType).getBaseType().isConst() and
186+
(
187+
// f(variable)
188+
va = argument
189+
or
190+
// f(&variable)
191+
va = argument.(AddressOfExpr).getOperand()
192+
or
193+
// f(&array[0])
194+
va.getType().getUnspecifiedType() instanceof ArrayType and
195+
va = argument.(AddressOfExpr).getOperand().(ArrayExpr).getArrayBase()
196+
)
197+
)
198+
}
169199
}
170200
import PartialDefinitions
171201
private import FlowVar_internal
@@ -236,7 +266,7 @@ module FlowVar_internal {
236266
not v instanceof Field and // Fields are interprocedural data flow, not local
237267
reachable(sbb) and
238268
(
239-
initializer(sbb.getANode(), v, _)
269+
initializer(v, sbb.getANode())
240270
or
241271
assignmentLikeOperation(sbb, v, _, _)
242272
or
@@ -353,7 +383,12 @@ module FlowVar_internal {
353383
assignmentLikeOperation(node, v, _, e) and
354384
node = sbb
355385
or
356-
initializer(node, v, e) and
386+
// We pick the defining `ControlFlowNode` of an `Initializer` to be its
387+
// expression rather than the `Initializer` itself. That's because the
388+
// `Initializer` of a `ConditionDeclExpr` is for historical reasons not
389+
// part of the CFG and therefore ends up in the wrong basic block.
390+
initializer(v, e) and
391+
node = e and
357392
node = sbb.getANode()
358393
}
359394

@@ -711,13 +746,11 @@ module FlowVar_internal {
711746
}
712747

713748
/**
714-
* Holds if `v` is initialized by `init` to have value `assignedExpr`.
749+
* Holds if `v` is initialized to have value `assignedExpr`.
715750
*/
716-
predicate initializer(
717-
Initializer init, LocalVariable v, Expr assignedExpr)
751+
predicate initializer(LocalVariable v, Expr assignedExpr)
718752
{
719-
v = init.getDeclaration() and
720-
assignedExpr = init.getExpr()
753+
assignedExpr = v.getInitializer().getExpr()
721754
}
722755

723756
/**

cpp/ql/test/library-tests/dataflow/fields/A.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ class A
129129
{
130130
B *b = new B();
131131
f7(b);
132-
sink(b->c); // flow [NOT DETECTED]
132+
sink(b->c); // flow
133133
}
134134

135135
class D
@@ -151,7 +151,7 @@ class A
151151
D *d = new D(b, r());
152152
sink(d->b); // flow x2
153153
sink(d->b->c); // flow
154-
sink(b->c); // flow [NOT DETECTED]
154+
sink(b->c); // flow
155155
}
156156

157157
void f10()

cpp/ql/test/library-tests/dataflow/fields/flow.expected

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,12 @@ edges
2424
| A.cpp:103:14:103:14 | c [a, ... (1)] | A.cpp:120:12:120:13 | c1 [a, ... (1)] |
2525
| A.cpp:107:12:107:13 | c1 [a, ... (1)] | A.cpp:107:16:107:16 | a |
2626
| A.cpp:120:12:120:13 | c1 [a, ... (1)] | A.cpp:120:16:120:16 | a |
27+
| A.cpp:126:5:126:5 | b [post update] [c, ... (1)] | A.cpp:131:8:131:8 | ref arg b [c, ... (1)] |
28+
| A.cpp:126:12:126:18 | new [void] | A.cpp:126:5:126:5 | b [post update] [c, ... (1)] |
29+
| A.cpp:131:8:131:8 | ref arg b [c, ... (1)] | A.cpp:132:10:132:10 | b [c, ... (1)] |
30+
| A.cpp:132:10:132:10 | b [c, ... (1)] | A.cpp:132:13:132:13 | c |
2731
| A.cpp:142:7:142:7 | b [post update] [c, ... (1)] | A.cpp:143:7:143:31 | ... = ... [c, ... (1)] |
32+
| A.cpp:142:7:142:7 | b [post update] [c, ... (1)] | A.cpp:151:18:151:18 | ref arg b [c, ... (1)] |
2833
| A.cpp:142:7:142:20 | ... = ... [void] | A.cpp:142:7:142:7 | b [post update] [c, ... (1)] |
2934
| A.cpp:142:14:142:20 | new [void] | A.cpp:142:7:142:20 | ... = ... [void] |
3035
| A.cpp:143:7:143:10 | this [post update] [b, ... (1)] | A.cpp:151:12:151:24 | call to D [b, ... (1)] |
@@ -36,9 +41,11 @@ edges
3641
| A.cpp:151:12:151:24 | call to D [b, ... (1)] | A.cpp:152:10:152:10 | d [b, ... (1)] |
3742
| A.cpp:151:12:151:24 | call to D [b, ... (2)] | A.cpp:153:10:153:10 | d [b, ... (2)] |
3843
| A.cpp:151:18:151:18 | b [void] | A.cpp:151:12:151:24 | call to D [b, ... (1)] |
44+
| A.cpp:151:18:151:18 | ref arg b [c, ... (1)] | A.cpp:154:10:154:10 | b [c, ... (1)] |
3945
| A.cpp:152:10:152:10 | d [b, ... (1)] | A.cpp:152:13:152:13 | b |
4046
| A.cpp:153:10:153:10 | d [b, ... (2)] | A.cpp:153:13:153:13 | b [c, ... (1)] |
4147
| A.cpp:153:13:153:13 | b [c, ... (1)] | A.cpp:153:16:153:16 | c |
48+
| A.cpp:154:10:154:10 | b [c, ... (1)] | A.cpp:154:13:154:13 | c |
4249
| A.cpp:159:12:159:18 | new [void] | A.cpp:160:29:160:29 | b [void] |
4350
| A.cpp:160:18:160:60 | call to MyList [head, ... (1)] | A.cpp:161:38:161:39 | l1 [head, ... (1)] |
4451
| A.cpp:160:29:160:29 | b [void] | A.cpp:160:18:160:60 | call to MyList [head, ... (1)] |
@@ -178,9 +185,11 @@ edges
178185
| A.cpp:75:14:75:14 | c | A.cpp:73:25:73:32 | new [void] | A.cpp:75:14:75:14 | c | c flows from $@ | A.cpp:73:25:73:32 | new [void] | new [void] |
179186
| A.cpp:107:16:107:16 | a | A.cpp:98:12:98:18 | new [void] | A.cpp:107:16:107:16 | a | a flows from $@ | A.cpp:98:12:98:18 | new [void] | new [void] |
180187
| A.cpp:120:16:120:16 | a | A.cpp:98:12:98:18 | new [void] | A.cpp:120:16:120:16 | a | a flows from $@ | A.cpp:98:12:98:18 | new [void] | new [void] |
188+
| A.cpp:132:13:132:13 | c | A.cpp:126:12:126:18 | new [void] | A.cpp:132:13:132:13 | c | c flows from $@ | A.cpp:126:12:126:18 | new [void] | new [void] |
181189
| A.cpp:152:13:152:13 | b | A.cpp:143:25:143:31 | new [void] | A.cpp:152:13:152:13 | b | b flows from $@ | A.cpp:143:25:143:31 | new [void] | new [void] |
182190
| A.cpp:152:13:152:13 | b | A.cpp:150:12:150:18 | new [void] | A.cpp:152:13:152:13 | b | b flows from $@ | A.cpp:150:12:150:18 | new [void] | new [void] |
183191
| A.cpp:153:16:153:16 | c | A.cpp:142:14:142:20 | new [void] | A.cpp:153:16:153:16 | c | c flows from $@ | A.cpp:142:14:142:20 | new [void] | new [void] |
192+
| A.cpp:154:13:154:13 | c | A.cpp:142:14:142:20 | new [void] | A.cpp:154:13:154:13 | c | c flows from $@ | A.cpp:142:14:142:20 | new [void] | new [void] |
184193
| A.cpp:165:26:165:29 | head | A.cpp:159:12:159:18 | new [void] | A.cpp:165:26:165:29 | head | head flows from $@ | A.cpp:159:12:159:18 | new [void] | new [void] |
185194
| A.cpp:169:15:169:18 | head | A.cpp:159:12:159:18 | new [void] | A.cpp:169:15:169:18 | head | head flows from $@ | A.cpp:159:12:159:18 | new [void] | new [void] |
186195
| B.cpp:9:20:9:24 | elem1 | B.cpp:6:15:6:24 | new [void] | B.cpp:9:20:9:24 | elem1 | elem1 flows from $@ | B.cpp:6:15:6:24 | new [void] | new [void] |

cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@
133133
| taint.cpp:165:22:165:25 | {...} | taint.cpp:172:10:172:15 | buffer | |
134134
| taint.cpp:165:22:165:25 | {...} | taint.cpp:173:8:173:13 | buffer | |
135135
| taint.cpp:165:24:165:24 | 0 | taint.cpp:165:22:165:25 | {...} | TAINT |
136+
| taint.cpp:168:8:168:14 | ref arg tainted | taint.cpp:172:18:172:24 | tainted | |
136137
| taint.cpp:170:10:170:15 | buffer | taint.cpp:170:3:170:8 | call to strcpy | |
137138
| taint.cpp:170:10:170:15 | ref arg buffer | taint.cpp:170:3:170:8 | call to strcpy | |
138139
| taint.cpp:170:10:170:15 | ref arg buffer | taint.cpp:171:8:171:13 | buffer | |

0 commit comments

Comments
 (0)