forked from github/codeql
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDefUse.qll
More file actions
309 lines (285 loc) · 10.7 KB
/
Copy pathDefUse.qll
File metadata and controls
309 lines (285 loc) · 10.7 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
/** Provides classes and predicates for working with variable definitions and uses. */
import javascript
/**
* Holds if `def` is a CFG node that assigns the value of `rhs` to `lhs`.
*
* This predicate covers four kinds of definitions:
*
* <table border="1">
* <tr><th>Example<th><code>def</code><th><code>lhs</code><th><code>rhs</code></tr>
* <tr><td><code>x = y</code><td><code>x = y</code><td><code>x</code><td><code>y</code></tr>
* <tr><td><code>var a = b</code><td><code>var a = b</code><td><code>a</code><td><code>b</code></tr>
* <tr><td><code>function f { ... }</code><td><code>f</code><td><code>f</code><td><code>function f { ... }</code></tr>
* <tr><td><code>function f ( x = y ){ ... }</code><td><code>x</code><td><code>x</code><td><code>y</code></tr>
* <tr><td><code>class C { ... }</code><td><code>C</code><td><code>C</code><td><code>class C { ... }</code></tr>
* <tr><td><code>namespace N { ... }</code><td><code>N</code><td><code>N</code><td><code>namespace N { ... }</code></tr>
* <tr><td><code>enum E { ... }</code><td><code>E</code><td><code>E</code><td><code>enum E { ... }</code></tr>
* <tr><td><code>import x = y</code><td><code>x</code><td><code>x</code><td><code>y</code></tr>
* <tr><td><code>enum { x = y }</code><td><code>x</code><td><code>x</code><td><code>y</code></tr>
* </table>
*
* Note that `def` and `lhs` are not in general the same: the latter
* represents the point where `lhs` is evaluated to an assignable reference,
* the former the point where the value of `rhs` is actually assigned
* to that reference.
*/
private predicate defn(ControlFlowNode def, Expr lhs, AST::ValueNode rhs) {
exists(AssignExpr assgn | def = assgn | lhs = assgn.getTarget() and rhs = assgn.getRhs())
or
exists(VariableDeclarator vd | def = vd | lhs = vd.getBindingPattern() and rhs = vd.getInit())
or
exists(Function f | def = f.getIdentifier() | lhs = def and rhs = f)
or
exists(ClassDefinition c | lhs = c.getIdentifier() | def = c and rhs = c and not c.isAmbient())
or
exists(NamespaceDeclaration n | def = n | lhs = n.getIdentifier() and rhs = n)
or
exists(EnumDeclaration ed | def = ed.getIdentifier() | lhs = def and rhs = ed)
or
exists(ImportEqualsDeclaration i | def = i |
lhs = i.getIdentifier() and rhs = i.getImportedEntity()
)
or
exists(ImportSpecifier i | def = i and not i.isTypeOnly() | lhs = i.getLocal() and rhs = i)
or
exists(EnumMember member | def = member.getIdentifier() |
lhs = def and rhs = member.getInitializer()
)
}
/**
* Holds if `def` is a CFG node that assigns to `lhs`.
*
* This predicate extends the three-argument version of `defn` to also cover definitions
* where there is no explicit right hand side:
*
* <table border="1">
* <tr><th>Example<th><code>def</code><th><code>lhs</code></tr>
* <tr><td><code>x += y</code><td><code>x += y</code><td><code>x</code></tr>
* <tr><td><code>++z.q</code><td><code>++z.q</code><td><code>z.q</code></tr>
* <tr><td><code>import { a as b } from 'm'</code><td><code>a as b</code><td><code>b</code></tr>
* <tr><td><code>for (var p in o) ...</code><td><code>var p</code><td><code>p</code></tr>
* <tr><td><code>enum { x }</code><td><code>x</code><td><code>x</code></tr>
* </table>
*
* Additionally, parameters are also considered definitions, which are their own `lhs`.
*/
private predicate defn(ControlFlowNode def, Expr lhs) {
defn(def, lhs, _)
or
lhs = def.(CompoundAssignExpr).getTarget()
or
lhs = def.(UpdateExpr).getOperand().getUnderlyingReference()
or
exists(EnhancedForLoop efl | def = efl.getIteratorExpr() |
lhs = def.(Expr).stripParens() or
lhs = def.(VariableDeclarator).getBindingPattern()
)
or
lhs = def and
(
def instanceof Parameter or
def = any(ComprehensionBlock cb).getIterator()
)
or
exists(EnumMember member | def = member.getIdentifier() |
lhs = def and not exists(member.getInitializer())
)
}
/**
* Holds if `l` is one of the lvalues in the assignment `def`, or
* a destructuring pattern that contains some of the lvalues.
*
* For example, if `def` is `[{ x: y }] = e`, then `l` can be any
* of `y`, `{ x: y }` and `[{ x: y }]`.
*/
private predicate lvalAux(Expr l, ControlFlowNode def) {
defn(def, l)
or
exists(ArrayPattern ap | lvalAux(ap, def) | l = ap.getAnElement().stripParens())
or
exists(ObjectPattern op | lvalAux(op, def) |
l = op.getAPropertyPattern().getValuePattern().stripParens() or
l = op.getRest().stripParens()
)
}
/**
* An expression that can be evaluated to a reference, that is,
* a variable reference or a property access.
*/
class RefExpr extends Expr {
RefExpr() {
this instanceof VarRef or
this instanceof PropAccess
}
}
/**
* A variable reference or property access that is written to.
*
* For instance, in the assignment `x.p = x.q`, `x.p` is written to
* and `x.q` is not; in the expression `++i`, `i` is written to
* (and also read from).
*/
class LValue extends RefExpr {
LValue() { lvalAux(this, _) }
/** Gets the definition in which this lvalue occurs. */
ControlFlowNode getDefNode() { lvalAux(this, result) }
/** Gets the source of the assignment. */
AST::ValueNode getRhs() { defn(_, this, result) }
}
/**
* A variable reference or property access that is read from.
*
* For instance, in the assignment `x.p = x.q`, `x.q` is read from
* and `x.p` is not; in the expression `++i`, `i` is read from
* (and also written to).
*/
class RValue extends RefExpr {
RValue() {
not this instanceof LValue and not this instanceof VarDecl
or
// in `x++` and `x += 1`, `x` is both RValue and LValue
this = any(CompoundAssignExpr a).getTarget()
or
this = any(UpdateExpr u).getOperand().getUnderlyingReference()
or
this = any(NamespaceDeclaration decl).getIdentifier()
}
}
/**
* A ControlFlowNode that defines (that is, initializes or updates) variables or properties.
*
* The following program elements are definitions:
*
* - assignment expressions (`x = 42`)
* - update expressions (`++x`)
* - variable declarators with an initializer (`var x = 42`)
* - for-in and for-of statements (`for (x in o) { ... }`)
* - parameters of functions or catch clauses (`function (x) { ... }`)
* - named functions (`function x() { ... }`)
* - named classes (`class x { ... }`)
* - import specifiers (`import { x } from 'm'`)
*
* Note that due to destructuring, a single `VarDef` may define multiple
* variables and/or properties; for example, `{ x, y: z.p } = e` defines variable
* `x` as well as property `p` of `z`.
*/
class VarDef extends ControlFlowNode {
VarDef() { defn(this, _) }
/**
* Gets the target of this definition, which is either a simple variable
* reference, a destructuring pattern, or a property access.
*/
Expr getTarget() { defn(this, result) }
/** Gets a variable defined by this node, if any. */
Variable getAVariable() { result = getTarget().(BindingPattern).getAVariable() }
/**
* Gets the source of this definition, that is, the data flow node representing
* the value that this definition assigns to its target.
*
* This predicate is not defined for `VarDef`s where the source is implicit,
* such as `for-in` loops, parameters or destructuring assignments.
*/
AST::ValueNode getSource() {
exists(Expr target | not target instanceof DestructuringPattern and defn(this, target, result))
}
/**
* Gets the source that this definition destructs, that is, the
* right hand side of a destructuring assignment.
*/
AST::ValueNode getDestructuringSource() {
exists(Expr target | target instanceof DestructuringPattern and defn(this, target, result))
}
/**
* Holds if this definition of `v` is overwritten by another definition, that is,
* another definition of `v` is reachable from it in the CFG.
*/
predicate isOverwritten(Variable v) {
exists(BasicBlock bb, int i | bb.defAt(i, v, this) |
exists(int j | bb.defAt(j, v, _) and j > i)
or
bb.getASuccessor+().defAt(_, v, _)
)
}
}
/**
* A ControlFlowNode that uses (that is, reads from) a single variable.
*
* Some variable definitions are also uses, notably the operands of update expressions.
*/
class VarUse extends ControlFlowNode, @varref {
VarUse() { this instanceof RValue }
/** Gets the variable this use refers to. */
Variable getVariable() { result = this.(VarRef).getVariable() }
/**
* Gets a definition that may reach this use.
*
* For global variables, each definition is considered to reach each use.
*/
VarDef getADef() {
result = getSsaVariable().getDefinition().getAContributingVarDef() or
result.getAVariable() = getVariable().(GlobalVariable)
}
/**
* Gets the unique SSA variable this use refers to.
*
* This predicate is only defined for variables that can be SSA-converted.
*/
SsaVariable getSsaVariable() { result.getAUse() = this }
}
/**
* Holds if the definition of `v` in `def` reaches `use` along some control flow path
* without crossing another definition of `v`.
*/
predicate definitionReaches(Variable v, VarDef def, VarUse use) {
v = use.getVariable() and
exists(BasicBlock bb, int i, int next | next = nextDefAfter(bb, v, i, def) |
exists(int j | j in [i + 1 .. next - 1] | bb.useAt(j, v, use))
or
exists(BasicBlock succ | succ = bb.getASuccessor() |
succ.isLiveAtEntry(v, use) and
next = bb.length()
)
)
}
/**
* Holds if the definition of local variable `v` in `def` reaches `use` along some control flow path
* without crossing another definition of `v`.
*/
predicate localDefinitionReaches(LocalVariable v, VarDef def, VarUse use) {
exists(SsaExplicitDefinition ssa |
ssa.defines(def, v) and
ssa = getAPseudoDefinitionInput*(use.getSsaVariable().getDefinition())
)
}
/** Holds if `nd` is a pseudo-definition and the result is one of its inputs. */
private SsaDefinition getAPseudoDefinitionInput(SsaDefinition nd) {
result = nd.(SsaPseudoDefinition).getAnInput()
}
/**
* Holds if `d` is a definition of `v` at index `i` in `bb`, and the result is the next index
* in `bb` after `i` at which the same variable is defined, or `bb.length()` if there is none.
*/
private int nextDefAfter(BasicBlock bb, Variable v, int i, VarDef d) {
bb.defAt(i, v, d) and
result =
min(int jj |
(bb.defAt(jj, v, _) or jj = bb.length()) and
jj > i
)
}
/**
* Holds if the `later` definition of `v` could overwrite its `earlier` definition.
*
* This is the case if there is a path from `earlier` to `later` that does not cross
* another definition of `v`.
*/
predicate localDefinitionOverwrites(LocalVariable v, VarDef earlier, VarDef later) {
exists(BasicBlock bb, int i, int next | next = nextDefAfter(bb, v, i, earlier) |
bb.defAt(next, v, later)
or
exists(BasicBlock succ | succ = bb.getASuccessor() |
succ.localMayBeOverwritten(v, later) and
next = bb.length()
)
)
}