-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathSSA.qll
More file actions
430 lines (366 loc) · 14.5 KB
/
SSA.qll
File metadata and controls
430 lines (366 loc) · 14.5 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
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
/**
* Provides classes for working with static single assignment form (SSA).
*/
overlay[local]
module;
import go
private import SsaImpl
/**
* A variable that can be SSA converted, that is, a local variable, but not a variable
* declared in file scope.
*/
class SsaSourceVariable extends LocalVariable {
SsaSourceVariable() { not this.getScope() instanceof FileScope }
/**
* Holds if there may be indirect references of this variable that are not covered by `getAReference()`.
*
* This is the case for variables that have their address taken, and for variables whose
* name resolution information may be incomplete (for instance due to an extractor error).
*/
predicate mayHaveIndirectReferences() {
// variables that have their address taken
exists(AddressExpr addr | addr.getOperand().stripParens() = this.getAReference())
or
exists(DataFlow::MethodReadNode mrn |
mrn.getReceiver() = this.getARead() and
mrn.getMethod().getReceiverType() instanceof PointerType
)
or
// variables where there is an unresolved reference with the same name in the same
// scope or a nested scope, suggesting that name resolution information may be incomplete
exists(FunctionScope scope, FuncDef inner |
scope = this.getScope().(LocalScope).getEnclosingFunctionScope() and
unresolvedReference(this.getName(), inner) and
inner.getScope().getOuterScope*() = scope
)
}
}
/**
* Holds if there is an unresolved reference to `name` in `fn`.
*/
private predicate unresolvedReference(string name, FuncDef fn) {
exists(Ident unresolved |
unresolvedIdentifier(unresolved, name) and
not unresolved = any(SelectorExpr sel).getSelector() and
fn = unresolved.getEnclosingFunction()
)
}
/**
* Holds if `id` is an unresolved identifier with the given `name`.
*/
pragma[noinline]
private predicate unresolvedIdentifier(Ident id, string name) {
id.getName() = name and
id instanceof ReferenceExpr and
not id.refersTo(_)
}
/**
* An SSA variable.
*/
class SsaVariable extends TSsaDefinition {
/** Gets the source variable corresponding to this SSA variable. */
SsaSourceVariable getSourceVariable() { result = this.(SsaDefinition).getSourceVariable() }
/** Gets the (unique) definition of this SSA variable. */
SsaDefinition getDefinition() { result = this }
/** Gets the type of this SSA variable. */
Type getType() { result = this.getSourceVariable().getType() }
/** Gets a use in basic block `bb` that refers to this SSA variable. */
IR::Instruction getAUseIn(ReachableBasicBlock bb) {
exists(int i, SsaSourceVariable v | v = this.getSourceVariable() |
result = bb.getNode(i) and
this = getDefinition(bb, i, v)
)
}
/** Gets a use that refers to this SSA variable. */
IR::Instruction getAUse() { result = this.getAUseIn(_) }
/** Gets a textual representation of this element. */
string toString() { result = this.getDefinition().prettyPrintRef() }
/** Gets the location of this SSA variable. */
Location getLocation() { result = this.getDefinition().getLocation() }
/**
* DEPRECATED: Use `getLocation()` instead.
*
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
deprecated predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
/**
* An SSA definition.
*/
class SsaDefinition extends TSsaDefinition {
/** Gets the SSA variable defined by this definition. */
SsaVariable getVariable() { result = this }
/** Gets the source variable defined by this definition. */
abstract SsaSourceVariable getSourceVariable();
/**
* Gets the basic block to which this definition belongs.
*/
abstract ReachableBasicBlock getBasicBlock();
/**
* INTERNAL: Use `getBasicBlock()` and `getSourceVariable()` instead.
*
* Holds if this is a definition of source variable `v` at index `idx` in basic block `bb`.
*
* Phi nodes are considered to be at index `-1`, all other definitions at the index of
* the control flow node they correspond to.
*/
abstract predicate definesAt(ReachableBasicBlock bb, int idx, SsaSourceVariable v);
/**
* INTERNAL: Use `toString()` instead.
*
* Gets a pretty-printed representation of this SSA definition.
*/
abstract string prettyPrintDef();
/**
* INTERNAL: Do not use.
*
* Gets a pretty-printed representation of a reference to this SSA definition.
*/
abstract string prettyPrintRef();
/** Gets the innermost function or file to which this SSA definition belongs. */
ControlFlow::Root getRoot() { result = this.getBasicBlock().getScope() }
/** Gets a textual representation of this element. */
string toString() { result = this.prettyPrintDef() }
/** Gets the source location for this element. */
abstract Location getLocation();
/**
* DEPRECATED: Use `getLocation()` instead.
*
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
deprecated predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/**
* Gets the first instruction that the value of this `SsaDefinition` can
* reach without passing through any other instructions, but possibly through
* phi nodes.
*/
IR::Instruction getAFirstUse() { firstUse(this, result) }
}
/**
* An SSA definition that corresponds to an explicit assignment or other variable definition.
*/
class SsaExplicitDefinition extends SsaDefinition, TExplicitDef {
/** Gets the instruction where the definition happens. */
IR::Instruction getInstruction() {
exists(BasicBlock bb, int i | this = TExplicitDef(bb, i, _) | result = bb.getNode(i))
}
/** Gets the right-hand side of the definition. */
IR::Instruction getRhs() { this.getInstruction().writes(_, result) }
override predicate definesAt(ReachableBasicBlock bb, int i, SsaSourceVariable v) {
this = TExplicitDef(bb, i, v)
}
override ReachableBasicBlock getBasicBlock() { this.definesAt(result, _, _) }
override SsaSourceVariable getSourceVariable() { this = TExplicitDef(_, _, result) }
override string prettyPrintRef() {
exists(Location loc | loc = this.getLocation() |
result = "def@" + loc.getStartLine() + ":" + loc.getStartColumn()
)
}
override string prettyPrintDef() { result = "definition of " + this.getSourceVariable() }
override Location getLocation() { result = this.getInstruction().getLocation() }
}
/** Provides a helper predicate for working with explicit SSA definitions. */
module SsaExplicitDefinition {
/**
* Gets the SSA definition corresponding to definition `def`.
*/
SsaExplicitDefinition of(IR::Instruction def) { result.getInstruction() = def }
}
/**
* An SSA definition that does not correspond to an explicit variable definition.
*/
abstract class SsaImplicitDefinition extends SsaDefinition {
/**
* INTERNAL: Do not use.
*
* Gets the definition kind to include in `prettyPrintRef`.
*/
abstract string getKind();
override string prettyPrintRef() {
exists(Location loc | loc = this.getLocation() |
result = this.getKind() + "@" + loc.getStartLine() + ":" + loc.getStartColumn()
)
}
override Location getLocation() { result = this.getBasicBlock().getLocation() }
}
/**
* An SSA definition representing the capturing of an SSA-convertible variable
* in the closure of a nested function.
*
* Capturing definitions appear at the beginning of such functions, as well as
* at any function call that may affect the value of the variable.
*/
class SsaVariableCapture extends SsaImplicitDefinition, TCapture {
override predicate definesAt(ReachableBasicBlock bb, int i, SsaSourceVariable v) {
this = TCapture(bb, i, v)
}
override ReachableBasicBlock getBasicBlock() { this.definesAt(result, _, _) }
override SsaSourceVariable getSourceVariable() { this.definesAt(_, _, result) }
override string getKind() { result = "capture" }
override string prettyPrintDef() { result = "capture variable " + this.getSourceVariable() }
override Location getLocation() {
exists(ReachableBasicBlock bb, int i | this.definesAt(bb, i, _) |
result = bb.getNode(i).getLocation()
)
}
}
/**
* An SSA definition such as a phi node that has no actual semantics, but simply serves to
* merge or filter data flow.
*/
abstract class SsaPseudoDefinition extends SsaImplicitDefinition {
/**
* Gets an input of this pseudo-definition.
*/
abstract SsaVariable getAnInput();
/**
* Gets a textual representation of the inputs of this pseudo-definition
* in lexicographical order.
*/
string ppInputs() { result = concat(this.getAnInput().getDefinition().prettyPrintRef(), ", ") }
}
/**
* An SSA phi node, that is, a pseudo-definition for a variable at a point
* in the flow graph where otherwise two or more definitions for the variable
* would be visible.
*/
class SsaPhiNode extends SsaPseudoDefinition, TPhi {
override SsaVariable getAnInput() {
result = getDefReachingEndOf(this.getBasicBlock().getAPredecessor(_), this.getSourceVariable())
}
override predicate definesAt(ReachableBasicBlock bb, int i, SsaSourceVariable v) {
bb = this.getBasicBlock() and v = this.getSourceVariable() and i = -1
}
override ReachableBasicBlock getBasicBlock() { this = TPhi(result, _) }
override SsaSourceVariable getSourceVariable() { this = TPhi(_, result) }
override string getKind() { result = "phi" }
override string prettyPrintDef() {
result = this.getSourceVariable() + " = phi(" + this.ppInputs() + ")"
}
override Location getLocation() { result = this.getBasicBlock().getLocation() }
}
/**
* An SSA variable, possibly with a chain of field reads on it.
*/
private newtype TSsaWithFields =
TRoot(SsaVariable v) or
TStep(SsaWithFields base, Field f) { exists(accessPathAux(base, f)) }
/**
* Gets a representation of `insn` as an ssa-with-fields value if there is one.
*/
private TSsaWithFields accessPath(IR::Instruction insn) {
exists(SsaVariable v | insn = v.getAUse() | result = TRoot(v))
or
exists(SsaWithFields base, Field f | insn = accessPathAux(base, f) | result = TStep(base, f))
}
/**
* Gets a data-flow node that reads a field `f` from a node that is represented
* by ssa-with-fields value `base`.
*/
private IR::Instruction accessPathAux(TSsaWithFields base, Field f) {
exists(IR::FieldReadInstruction fr, IR::Instruction frb |
fr.getBase() = frb or
fr.getBase() = IR::implicitDerefInstruction(frb.(IR::EvalInstruction).getExpr())
|
base = accessPath(frb) and
f = fr.getField() and
result = fr
)
}
/** An SSA variable with zero or more fields read from it. */
class SsaWithFields extends TSsaWithFields {
/**
* Gets the SSA variable corresponding to the base of this SSA variable with fields.
*
* For example, the SSA variable corresponding to `a` for the SSA variable with fields
* corresponding to `a.b`.
*/
SsaVariable getBaseVariable() {
this = TRoot(result)
or
exists(SsaWithFields base | this = TStep(base, _) | result = base.getBaseVariable())
}
/** Gets a use that refers to this SSA variable with fields. */
DataFlow::Node getAUse() { this = accessPath(result.asInstruction()) }
/** Gets the type of this SSA variable with fields. */
Type getType() {
exists(SsaVariable var | this = TRoot(var) | result = var.getType())
or
exists(Field f | this = TStep(_, f) | result = f.getType())
}
/** Gets a textual representation of this element. */
string toString() {
exists(SsaVariable var | this = TRoot(var) | result = "(" + var + ")")
or
exists(SsaWithFields base, Field f | this = TStep(base, f) | result = base + "." + f.getName())
}
/**
* Gets an SSA-with-fields variable that is similar to this SSA-with-fields variable in the
* sense that it has the same root variable and the same sequence of field accesses.
*/
SsaWithFields similar() {
result.getBaseVariable().getSourceVariable() = this.getBaseVariable().getSourceVariable() and
result.getQualifiedName() = this.getQualifiedName()
}
/**
* Gets the qualified name of the source variable or variable and fields that this represents.
*
* For example, for an SSA variable that represents the field `a.b`, this would get the string
* `"a.b"`.
*/
string getQualifiedName() {
exists(SsaVariable v | this = TRoot(v) and result = v.getSourceVariable().getName())
or
exists(SsaWithFields base, Field f | this = TStep(base, f) |
result = base.getQualifiedName() + "." + f.getName()
)
}
/** Gets the location of this SSA variable with fields. */
Location getLocation() { result = this.getBaseVariable().getLocation() }
/**
* DEPRECATED: Use `getLocation()` instead.
*
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
deprecated predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
/**
* Gets a read similar to `node`, according to the same rules as `SsaWithFields.similar()`.
*/
DataFlow::Node getASimilarReadNode(DataFlow::Node node) {
exists(SsaWithFields readFields | node = readFields.getAUse() |
result = readFields.similar().getAUse()
)
}
/**
* Gets an instruction such that `pred` and `result` form an adjacent
* use-use-pair of the same`SsaSourceVariable`, that is, the value read in
* `pred` can reach `result` without passing through any other use or any SSA
* definition of the variable except for phi nodes and uncertain implicit
* updates.
*/
IR::Instruction getAnAdjacentUse(IR::Instruction pred) { adjacentUseUse(pred, result) }