forked from github/codeql
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSSA.qll
More file actions
359 lines (311 loc) · 12.3 KB
/
Copy pathSSA.qll
File metadata and controls
359 lines (311 loc) · 12.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
/**
* Provides classes and predicates for SSA representation (Static Single Assignment form).
*
* An SSA variable consists of the pair of a `SsaSourceVariable` and a
* `ControlFlowNode` at which it is defined. Each SSA variable is defined
* either by a phi node, an implicit initial value (for parameters and fields),
* an explicit update, or an implicit update (for fields).
* An implicit update occurs either at a `Call` that might modify a field, at
* another update that can update the qualifier of a field, or at a `FieldRead`
* of the field in case the field is not amenable to a non-trivial SSA
* representation.
*/
import java
private import internal.SsaImpl
/**
* A fully qualified variable in the context of a `Callable` in which it is
* accessed.
*
* This is either a local variable or a fully qualified field, `q.f1.f2....fn`,
* where the base qualifier `q` is either `this`, a local variable, or a type
* in case `f1` is static.
*/
class SsaSourceVariable extends TSsaSourceVariable {
/** Gets the variable corresponding to this `SsaSourceVariable`. */
Variable getVariable() {
this = TLocalVar(_, result) or
this = TPlainField(_, result) or
this = TEnclosingField(_, result, _) or
this = TQualifiedField(_, _, result)
}
/**
* Gets an access of this `SsaSourceVariable`. This access is within
* `this.getEnclosingCallable()`. Note that `LocalScopeVariable`s that are
* accessed from nested callables are therefore associated with several
* `SsaSourceVariable`s.
*/
cached
VarAccess getAnAccess() {
exists(LocalScopeVariable v, Callable c |
this = TLocalVar(c, v) and result = v.getAnAccess() and result.getEnclosingCallable() = c
)
or
exists(Field f, Callable c | fieldAccessInCallable(result, f, c) |
(result.(FieldAccess).isOwnFieldAccess() or f.isStatic()) and
this = TPlainField(c, f)
or
exists(RefType t |
this = TEnclosingField(c, f, t) and result.(FieldAccess).isEnclosingFieldAccess(t)
)
or
exists(SsaSourceVariable q |
result.getQualifier() = q.getAnAccess() and this = TQualifiedField(c, q, f)
)
)
}
/** Gets the `Callable` in which this `SsaSourceVariable` is defined. */
Callable getEnclosingCallable() {
this = TLocalVar(result, _) or
this = TPlainField(result, _) or
this = TEnclosingField(result, _, _) or
this = TQualifiedField(result, _, _)
}
/** Gets a textual representation of this `SsaSourceVariable`. */
string toString() {
exists(LocalScopeVariable v, Callable c | this = TLocalVar(c, v) |
if c = v.getCallable()
then result = v.getName()
else result = c.getName() + "(..)." + v.getName()
)
or
result = this.(SsaSourceField).ppQualifier() + "." + this.getVariable().toString()
}
/**
* Gets the first access to `this` in terms of source code location. This is
* used as the representative location for named fields that otherwise would
* not have a specific source code location.
*/
private VarAccess getFirstAccess() {
result =
min(this.getAnAccess() as a
order by
a.getLocation().getStartLine(), a.getLocation().getStartColumn()
)
}
/** Gets the source location for this element. */
Location getLocation() {
exists(LocalScopeVariable v | this = TLocalVar(_, v) and result = v.getLocation())
or
this instanceof SsaSourceField and result = this.getFirstAccess().getLocation()
}
/** Gets the type of this variable. */
Type getType() { result = this.getVariable().getType() }
/** Gets the qualifier, if any. */
SsaSourceVariable getQualifier() { this = TQualifiedField(_, result, _) }
/** Gets an SSA variable that has this variable as its underlying source variable. */
SsaVariable getAnSsaVariable() { result.getSourceVariable() = this }
}
/**
* A fully qualified field in the context of a `Callable` in which it is
* accessed.
*/
class SsaSourceField extends SsaSourceVariable {
SsaSourceField() {
this = TPlainField(_, _) or this = TEnclosingField(_, _, _) or this = TQualifiedField(_, _, _)
}
/** Gets the field corresponding to this named field. */
Field getField() { result = this.getVariable() }
/** Gets a string representation of the qualifier. */
string ppQualifier() {
exists(Field f | this = TPlainField(_, f) |
if f.isStatic() then result = f.getDeclaringType().getQualifiedName() else result = "this"
)
or
exists(RefType t | this = TEnclosingField(_, _, t) | result = t.toString() + ".this")
or
exists(SsaSourceVariable q | this = TQualifiedField(_, q, _) | result = q.toString())
}
/** Holds if the field itself or any of the fields part of the qualifier are volatile. */
predicate isVolatile() {
this.getField().isVolatile() or
this.getQualifier().(SsaSourceField).isVolatile()
}
}
/**
* An SSA variable.
*/
class SsaVariable extends Definition {
/** Gets the SSA source variable underlying this SSA variable. */
SsaSourceVariable getSourceVariable() { result = super.getSourceVariable() }
/** Gets the `ControlFlowNode` at which this SSA variable is defined. */
pragma[nomagic]
ControlFlowNode getCfgNode() {
exists(BasicBlock bb, int i, int j |
this.definesAt(_, bb, i) and
// untracked definitions are inserted just before reads
(if this instanceof UntrackedDef then j = i + 1 else j = i) and
// phi nodes are inserted at position `-1`
result = bb.getNode(0.maximum(j))
)
}
/** Gets a textual representation of this SSA variable. */
string toString() { none() }
/** Gets the source location for this element. */
Location getLocation() { result = this.getCfgNode().getLocation() }
/** Gets the `BasicBlock` in which this SSA variable is defined. */
BasicBlock getBasicBlock() { result = super.getBasicBlock() }
/** Gets an access of this SSA variable. */
VarRead getAUse() { result = getAUse(this) }
/**
* Gets an access of the SSA source variable underlying this SSA variable
* that can be reached from this SSA variable without passing through any
* other uses, but potentially through phi nodes and uncertain implicit
* updates.
*
* Subsequent uses can be found by following the steps defined by
* `adjacentUseUse`.
*/
VarRead getAFirstUse() { firstUse(this, result) }
/** Holds if this SSA variable is live at the end of `b`. */
predicate isLiveAtEndOfBlock(BasicBlock b) { ssaDefReachesEndOfBlock(b, this) }
/**
* Gets an SSA variable whose value can flow to this one in one step. This
* includes inputs to phi nodes, the prior definition of uncertain updates,
* and the captured ssa variable for a closure variable.
*/
SsaVariable getAPhiInputOrPriorDef() {
result = this.(SsaPhiNode).getAPhiInput() or
result = this.(SsaUncertainImplicitUpdate).getPriorDef() or
this.(SsaImplicitInit).captures(result)
}
/** Gets a definition that ultimately defines this variable and is not itself a phi node. */
SsaVariable getAnUltimateDefinition() {
result = this.getAPhiInputOrPriorDef*() and not result instanceof SsaPhiNode
}
}
/** An SSA variable that either explicitly or implicitly updates the variable. */
class SsaUpdate extends SsaVariable instanceof WriteDefinition {
SsaUpdate() { not this instanceof SsaImplicitInit }
}
/** An SSA variable that is defined by a `VariableUpdate`. */
class SsaExplicitUpdate extends SsaUpdate {
private VariableUpdate upd;
SsaExplicitUpdate() { ssaExplicitUpdate(this, upd) }
override string toString() { result = "SSA def(" + this.getSourceVariable() + ")" }
/** Gets the `VariableUpdate` defining the SSA variable. */
VariableUpdate getDefiningExpr() { result = upd }
}
/**
* An SSA variable that represents any sort of implicit update. This can be a
* `Call` that might reach a non-local update of the field, an explicit or
* implicit update of the qualifier of the field, or the implicit update that
* occurs just prior to a `FieldRead` of an untracked field.
*/
class SsaImplicitUpdate extends SsaUpdate {
SsaImplicitUpdate() { not this instanceof SsaExplicitUpdate }
override string toString() {
result = "SSA impl upd[" + this.getKind() + "](" + this.getSourceVariable() + ")"
}
private predicate hasExplicitQualifierUpdate() {
exists(SsaUpdate qdef, BasicBlock bb, int i |
qdef.definesAt(this.getSourceVariable().getQualifier(), bb, i) and
this.definesAt(_, bb, i) and
not qdef instanceof SsaUncertainImplicitUpdate
)
}
private predicate hasImplicitQualifierUpdate() {
exists(SsaUncertainImplicitUpdate qdef, BasicBlock bb, int i |
qdef.definesAt(this.getSourceVariable().getQualifier(), bb, i) and
this.definesAt(_, bb, i)
)
}
private string getKind() {
this instanceof UntrackedDef and result = "untracked"
or
this.hasExplicitQualifierUpdate() and
result = "explicit qualifier"
or
if this.hasImplicitQualifierUpdate()
then
if exists(this.getANonLocalUpdate())
then result = "nonlocal + nonlocal qualifier"
else result = "nonlocal qualifier"
else (
exists(this.getANonLocalUpdate()) and result = "nonlocal"
)
}
/**
* Gets a reachable `FieldWrite` that might represent this ssa update, if any.
*/
FieldWrite getANonLocalUpdate() {
exists(SsaSourceField f, Callable setter |
relevantFieldUpdate(setter, f.getField(), result) and
defUpdatesNamedField(this, f, setter)
)
}
/**
* Holds if this ssa variable might change the value to something unknown.
*
* Examples include updates that might change the value of the qualifier, or
* reads from untracked variables, for example those where the field or one
* of its qualifiers is volatile.
*/
predicate assignsUnknownValue() {
this instanceof UntrackedDef
or
this.hasExplicitQualifierUpdate()
or
this.hasImplicitQualifierUpdate()
}
}
/**
* An SSA variable that represents an uncertain implicit update of the value.
* This is a `Call` that might reach a non-local update of the field or one of
* its qualifiers.
*/
class SsaUncertainImplicitUpdate extends SsaImplicitUpdate {
SsaUncertainImplicitUpdate() { ssaUncertainImplicitUpdate(this) }
/**
* Gets the immediately preceding definition. Since this update is uncertain
* the value from the preceding definition might still be valid.
*/
SsaVariable getPriorDef() { ssaDefReachesUncertainDef(result, this) }
}
/**
* An SSA variable that is defined by its initial value in the callable. This
* includes initial values of parameters, fields, and closure variables.
*/
class SsaImplicitInit extends SsaVariable instanceof WriteDefinition {
SsaImplicitInit() { ssaImplicitInit(this) }
override string toString() { result = "SSA init(" + this.getSourceVariable() + ")" }
/** Holds if this is a closure variable that captures the value of `capturedvar`. */
predicate captures(SsaVariable capturedvar) { captures(this, capturedvar) }
/**
* Holds if the SSA variable is a parameter defined by its initial value in the callable.
*/
predicate isParameterDefinition(Parameter p) {
this.getSourceVariable() = TLocalVar(p.getCallable(), p) and
p.getCallable().getBody().getControlFlowNode() = this.getCfgNode()
}
}
/** An SSA phi node. */
class SsaPhiNode extends SsaVariable instanceof PhiNode {
override string toString() { result = "SSA phi(" + this.getSourceVariable() + ")" }
/** Gets an input to the phi node defining the SSA variable. */
SsaVariable getAPhiInput() { this.hasInputFromBlock(result, _) }
/** Holds if `inp` is an input to the phi node along the edge originating in `bb`. */
predicate hasInputFromBlock(SsaVariable inp, BasicBlock bb) {
phiHasInputFromBlock(this, inp, bb)
}
}
private class RefTypeCastingExpr extends CastingExpr {
RefTypeCastingExpr() {
this.getType() instanceof RefType and
not this instanceof SafeCastExpr
}
}
/**
* Gets an expression that has the same value as the given SSA variable.
*
* The `VarAccess` represents the access to `v` that `result` has the same value as.
*/
Expr sameValue(SsaVariable v, VarAccess va) {
result = v.getAUse() and result = va
or
result.(AssignExpr).getDest() = va and result = v.(SsaExplicitUpdate).getDefiningExpr()
or
result.(AssignExpr).getSource() = sameValue(v, va)
or
result.(RefTypeCastingExpr).getExpr() = sameValue(v, va)
}
import SsaPublic