Skip to content

Commit df6abdc

Browse files
authored
Merge pull request github#3389 from jbj/dataflow-defbyref-to-field
C++: Post-update flow through &, *, +, ...
2 parents 0909774 + 71c21e6 commit df6abdc

26 files changed

+854
-214
lines changed

cpp/ql/src/semmle/code/cpp/dataflow/EscapesTree.qll

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,28 @@
44
* passed to a function, or similar.
55
*/
66

7+
/*
8+
* Maintainer note: this file is one of several files that are similar but not
9+
* identical. Many changes to this file will also apply to the others:
10+
* - AddressConstantExpression.qll
11+
* - AddressFlow.qll
12+
* - EscapesTree.qll
13+
*/
14+
715
private import cpp
816

917
/**
1018
* Holds if `f` is an instantiation of the `std::move` or `std::forward`
1119
* template functions, these functions are essentially casts, so we treat them
1220
* as such.
1321
*/
14-
private predicate stdIdentityFunction(Function f) {
15-
f.getNamespace().getParentNamespace() instanceof GlobalNamespace and
16-
f.getNamespace().getName() = "std" and
17-
(
18-
f.getName() = "move"
19-
or
20-
f.getName() = "forward"
21-
)
22-
}
22+
private predicate stdIdentityFunction(Function f) { f.hasQualifiedName("std", ["move", "forward"]) }
23+
24+
/**
25+
* Holds if `f` is an instantiation of `std::addressof`, which effectively
26+
* converts a reference to a pointer.
27+
*/
28+
private predicate stdAddressOf(Function f) { f.hasQualifiedName("std", "addressof") }
2329

2430
private predicate lvalueToLvalueStepPure(Expr lvalueIn, Expr lvalueOut) {
2531
lvalueIn = lvalueOut.(DotFieldAccess).getQualifier().getFullyConverted()
@@ -91,12 +97,17 @@ private predicate lvalueToReferenceStep(Expr lvalueIn, Expr referenceOut) {
9197
}
9298

9399
private predicate referenceToLvalueStep(Expr referenceIn, Expr lvalueOut) {
94-
// This probably cannot happen. It would require an expression to be
95-
// converted to a reference and back again without an intermediate variable
96-
// assignment.
97100
referenceIn.getConversion() = lvalueOut.(ReferenceDereferenceExpr)
98101
}
99102

103+
private predicate referenceToPointerStep(Expr referenceIn, Expr pointerOut) {
104+
pointerOut =
105+
any(FunctionCall call |
106+
stdAddressOf(call.getTarget()) and
107+
referenceIn = call.getArgument(0).getFullyConverted()
108+
)
109+
}
110+
100111
private predicate referenceToReferenceStep(Expr referenceIn, Expr referenceOut) {
101112
referenceOut =
102113
any(FunctionCall call |
@@ -145,6 +156,12 @@ private predicate pointerFromVariableAccess(VariableAccess va, Expr pointer) {
145156
pointerToPointerStep(prev, pointer)
146157
)
147158
or
159+
// reference -> pointer
160+
exists(Expr prev |
161+
referenceFromVariableAccess(va, prev) and
162+
referenceToPointerStep(prev, pointer)
163+
)
164+
or
148165
// lvalue -> pointer
149166
exists(Expr prev |
150167
lvalueFromVariableAccess(va, prev) and
@@ -169,7 +186,8 @@ private predicate referenceFromVariableAccess(VariableAccess va, Expr reference)
169186
private predicate addressMayEscapeAt(Expr e) {
170187
exists(Call call |
171188
e = call.getAnArgument().getFullyConverted() and
172-
not stdIdentityFunction(call.getTarget())
189+
not stdIdentityFunction(call.getTarget()) and
190+
not stdAddressOf(call.getTarget())
173191
or
174192
e = call.getQualifier().getFullyConverted() and
175193
e.getUnderlyingType() instanceof PointerType
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
/**
2+
* Provides a local analysis for identifying where a variable address
3+
* is effectively taken. Array-like offsets are allowed to pass through but
4+
* not field-like offsets.
5+
*
6+
* This library is specialized to meet the needs of `FlowVar.qll`.
7+
*/
8+
9+
/*
10+
* Maintainer note: this file is one of several files that are similar but not
11+
* identical. Many changes to this file will also apply to the others:
12+
* - AddressConstantExpression.qll
13+
* - AddressFlow.qll
14+
* - EscapesTree.qll
15+
*/
16+
17+
private import cpp
18+
19+
/**
20+
* Holds if `f` is an instantiation of the `std::move` or `std::forward`
21+
* template functions, these functions are essentially casts, so we treat them
22+
* as such.
23+
*/
24+
private predicate stdIdentityFunction(Function f) { f.hasQualifiedName("std", ["move", "forward"]) }
25+
26+
/**
27+
* Holds if `f` is an instantiation of `std::addressof`, which effectively
28+
* converts a reference to a pointer.
29+
*/
30+
private predicate stdAddressOf(Function f) { f.hasQualifiedName("std", "addressof") }
31+
32+
private predicate lvalueToLvalueStep(Expr lvalueIn, Expr lvalueOut) {
33+
lvalueIn.getConversion() = lvalueOut.(ParenthesisExpr)
34+
or
35+
// When an object is implicitly converted to a reference to one of its base
36+
// classes, it gets two `Conversion`s: there is first an implicit
37+
// `CStyleCast` to its base class followed by a `ReferenceToExpr` to a
38+
// reference to its base class. Whereas an explicit cast to the base class
39+
// would produce an rvalue, which would not be convertible to an lvalue
40+
// reference, this implicit cast instead produces an lvalue. The following
41+
// case ensures that we propagate the property of being an lvalue through
42+
// such casts.
43+
lvalueIn.getConversion() = lvalueOut and
44+
lvalueOut.(CStyleCast).isImplicit()
45+
or
46+
// C++ only
47+
lvalueIn = lvalueOut.(PrefixCrementOperation).getOperand().getFullyConverted()
48+
or
49+
// C++ only
50+
lvalueIn = lvalueOut.(Assignment).getLValue().getFullyConverted()
51+
}
52+
53+
private predicate pointerToLvalueStep(Expr pointerIn, Expr lvalueOut) {
54+
pointerIn = lvalueOut.(ArrayExpr).getArrayBase().getFullyConverted()
55+
or
56+
pointerIn = lvalueOut.(PointerDereferenceExpr).getOperand().getFullyConverted()
57+
}
58+
59+
private predicate lvalueToPointerStep(Expr lvalueIn, Expr pointerOut) {
60+
lvalueIn.getConversion() = pointerOut.(ArrayToPointerConversion)
61+
or
62+
lvalueIn = pointerOut.(AddressOfExpr).getOperand().getFullyConverted()
63+
}
64+
65+
private predicate pointerToPointerStep(Expr pointerIn, Expr pointerOut) {
66+
(
67+
pointerOut instanceof PointerAddExpr
68+
or
69+
pointerOut instanceof PointerSubExpr
70+
) and
71+
pointerIn = pointerOut.getAChild().getFullyConverted() and
72+
pointerIn.getUnspecifiedType() instanceof PointerType
73+
or
74+
pointerIn = pointerOut.(UnaryPlusExpr).getOperand().getFullyConverted()
75+
or
76+
pointerIn.getConversion() = pointerOut.(Cast)
77+
or
78+
pointerIn.getConversion() = pointerOut.(ParenthesisExpr)
79+
or
80+
pointerIn = pointerOut.(ConditionalExpr).getThen().getFullyConverted()
81+
or
82+
pointerIn = pointerOut.(ConditionalExpr).getElse().getFullyConverted()
83+
or
84+
pointerIn = pointerOut.(CommaExpr).getRightOperand().getFullyConverted()
85+
or
86+
pointerIn = pointerOut.(StmtExpr).getResultExpr().getFullyConverted()
87+
}
88+
89+
private predicate lvalueToReferenceStep(Expr lvalueIn, Expr referenceOut) {
90+
lvalueIn.getConversion() = referenceOut.(ReferenceToExpr)
91+
}
92+
93+
private predicate referenceToLvalueStep(Expr referenceIn, Expr lvalueOut) {
94+
referenceIn.getConversion() = lvalueOut.(ReferenceDereferenceExpr)
95+
}
96+
97+
private predicate referenceToPointerStep(Expr referenceIn, Expr pointerOut) {
98+
pointerOut =
99+
any(FunctionCall call |
100+
stdAddressOf(call.getTarget()) and
101+
referenceIn = call.getArgument(0).getFullyConverted()
102+
)
103+
}
104+
105+
private predicate referenceToReferenceStep(Expr referenceIn, Expr referenceOut) {
106+
referenceOut =
107+
any(FunctionCall call |
108+
stdIdentityFunction(call.getTarget()) and
109+
referenceIn = call.getArgument(0).getFullyConverted()
110+
)
111+
or
112+
referenceIn.getConversion() = referenceOut.(Cast)
113+
or
114+
referenceIn.getConversion() = referenceOut.(ParenthesisExpr)
115+
}
116+
117+
private predicate assignmentTo(Expr updated, ControlFlowNode node) {
118+
updated = node.(Assignment).getLValue().getFullyConverted()
119+
or
120+
updated = node.(CrementOperation).getOperand().getFullyConverted()
121+
}
122+
123+
private predicate lvalueToUpdate(Expr lvalue, Expr outer, ControlFlowNode node) {
124+
(
125+
exists(Call call | node = call |
126+
outer = call.getQualifier().getFullyConverted() and
127+
outer.getUnspecifiedType() instanceof Class and
128+
not call.getTarget().hasSpecifier("const")
129+
)
130+
or
131+
assignmentTo(outer, node)
132+
or
133+
exists(DotFieldAccess fa |
134+
// `fa.otherField = ...` or `f(&fa)` or similar
135+
outer = fa.getQualifier().getFullyConverted() and
136+
valueToUpdate(fa, _, node)
137+
)
138+
) and
139+
lvalue = outer
140+
or
141+
exists(Expr lvalueMid |
142+
lvalueToLvalueStep(lvalue, lvalueMid) and
143+
lvalueToUpdate(lvalueMid, outer, node)
144+
)
145+
or
146+
exists(Expr pointerMid |
147+
lvalueToPointerStep(lvalue, pointerMid) and
148+
pointerToUpdate(pointerMid, outer, node)
149+
)
150+
or
151+
exists(Expr referenceMid |
152+
lvalueToReferenceStep(lvalue, referenceMid) and
153+
referenceToUpdate(referenceMid, outer, node)
154+
)
155+
}
156+
157+
private predicate pointerToUpdate(Expr pointer, Expr outer, ControlFlowNode node) {
158+
(
159+
exists(Call call | node = call |
160+
outer = call.getAnArgument().getFullyConverted() and
161+
exists(PointerType pt | pt = outer.getType().stripTopLevelSpecifiers() |
162+
not pt.getBaseType().isConst()
163+
)
164+
or
165+
outer = call.getQualifier().getFullyConverted() and
166+
outer.getUnspecifiedType() instanceof PointerType and
167+
not call.getTarget().hasSpecifier("const")
168+
)
169+
or
170+
exists(PointerFieldAccess fa |
171+
// `fa.otherField = ...` or `f(&fa)` or similar
172+
outer = fa.getQualifier().getFullyConverted() and
173+
valueToUpdate(fa, _, node)
174+
)
175+
) and
176+
pointer = outer
177+
or
178+
exists(Expr lvalueMid |
179+
pointerToLvalueStep(pointer, lvalueMid) and
180+
lvalueToUpdate(lvalueMid, outer, node)
181+
)
182+
or
183+
exists(Expr pointerMid |
184+
pointerToPointerStep(pointer, pointerMid) and
185+
pointerToUpdate(pointerMid, outer, node)
186+
)
187+
}
188+
189+
private predicate referenceToUpdate(Expr reference, Expr outer, ControlFlowNode node) {
190+
exists(Call call |
191+
node = call and
192+
outer = call.getAnArgument().getFullyConverted() and
193+
not stdIdentityFunction(call.getTarget()) and
194+
not stdAddressOf(call.getTarget()) and
195+
exists(ReferenceType rt | rt = outer.getType().stripTopLevelSpecifiers() |
196+
not rt.getBaseType().isConst()
197+
)
198+
) and
199+
reference = outer
200+
or
201+
exists(Expr lvalueMid |
202+
referenceToLvalueStep(reference, lvalueMid) and
203+
lvalueToUpdate(lvalueMid, outer, node)
204+
)
205+
or
206+
exists(Expr pointerMid |
207+
referenceToPointerStep(reference, pointerMid) and
208+
pointerToUpdate(pointerMid, outer, node)
209+
)
210+
or
211+
exists(Expr referenceMid |
212+
referenceToReferenceStep(reference, referenceMid) and
213+
referenceToUpdate(referenceMid, outer, node)
214+
)
215+
}
216+
217+
/**
218+
* Holds if `node` is a control-flow node that may modify `inner` (or what it
219+
* points to) through `outer`. The two expressions may be `Conversion`s. Plain
220+
* assignments to variables are not included in this predicate since they are
221+
* assumed to be analyzed by SSA or similar means.
222+
*
223+
* For example, in `f(& (*a).x)`, there are two results:
224+
* - `inner` = `... .x`, `outer` = `&...`, `node` = `f(...)`.
225+
* - `inner` = `a`, `outer` = `(...)`, `node` = `f(...)`.
226+
*/
227+
cached
228+
predicate valueToUpdate(Expr inner, Expr outer, ControlFlowNode node) {
229+
(
230+
lvalueToUpdate(inner, outer, node)
231+
or
232+
pointerToUpdate(inner, outer, node)
233+
or
234+
referenceToUpdate(inner, outer, node)
235+
) and
236+
(
237+
inner instanceof VariableAccess and
238+
// Don't track non-field assignments
239+
(assignmentTo(outer, _) implies inner instanceof FieldAccess)
240+
or
241+
inner instanceof ThisExpr
242+
or
243+
inner instanceof Call
244+
// `inner` could also be `*` or `ReferenceDereferenceExpr`, but we
245+
// can't do anything useful with those at the moment.
246+
)
247+
}

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -311,9 +311,6 @@ predicate isImmutableOrUnobservable(Node n) {
311311
or
312312
dt.getBaseType() instanceof RoutineType
313313
)
314-
or
315-
// Isn't something we can track
316-
n.asExpr() instanceof Call
317314
// The above list of cases isn't exhaustive, but it narrows down the
318315
// consistency alerts enough that most of them are interesting.
319316
}

0 commit comments

Comments
 (0)