forked from github/codeql
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBase.qll
More file actions
378 lines (339 loc) · 12.2 KB
/
Copy pathBase.qll
File metadata and controls
378 lines (339 loc) · 12.2 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
/**
* Combined points-to and type-inference for "run-time" (as opposed to "import-time" values)
* The main relation `runtime_points_to(node, object, cls, origin)` relates a control flow node
* to the possible objects it points-to the inferred types of those objects and the 'origin'
* of those objects. The 'origin' is the point in source code that the object can be traced
* back to.
*
* This file contains non-layered parts of the points-to analysis.
*/
import python
import semmle.python.essa.SsaDefinitions
private import semmle.python.types.Builtins
private import semmle.python.internal.CachedStages
deprecated module BasePointsTo {
/** INTERNAL -- Use n.refersTo(value, _, origin) instead */
pragma[noinline]
predicate points_to(ControlFlowNode f, Object value, ControlFlowNode origin) {
(
f.isLiteral() and value = f and not f.getNode() instanceof ImmutableLiteral
or
f.isFunction() and value = f
) and
origin = f
}
}
/** Gets the kwargs parameter (`**kwargs`). In a function definition this is always a dict. */
deprecated predicate kwargs_points_to(ControlFlowNode f, ClassObject cls) {
exists(Function func | func.getKwarg() = f.getNode()) and
cls = theDictType()
}
/** Gets the varargs parameter (`*varargs`). In a function definition this is always a tuple. */
deprecated predicate varargs_points_to(ControlFlowNode f, ClassObject cls) {
exists(Function func | func.getVararg() = f.getNode()) and
cls = theTupleType()
}
/**
* Gets the class of the object for simple cases, namely constants, functions,
* comprehensions and built-in objects.
*
* This exists primarily for internal use. Use getAnInferredType() instead.
*/
pragma[noinline]
deprecated ClassObject simple_types(Object obj) {
result = comprehension(obj.getOrigin())
or
result = collection_literal(obj.getOrigin())
or
obj.getOrigin() instanceof CallableExpr and result = thePyFunctionType()
or
obj.getOrigin() instanceof Module and result = theModuleType()
or
result.asBuiltin() = obj.asBuiltin().getClass()
or
obj = unknownValue() and result = theUnknownType()
}
deprecated private ClassObject comprehension(Expr e) {
e instanceof ListComp and result = theListType()
or
e instanceof SetComp and result = theSetType()
or
e instanceof DictComp and result = theDictType()
or
e instanceof GeneratorExp and result = theGeneratorType()
}
deprecated private ClassObject collection_literal(Expr e) {
e instanceof List and result = theListType()
or
e instanceof Set and result = theSetType()
or
e instanceof Dict and result = theDictType()
or
e instanceof Tuple and result = theTupleType()
}
deprecated private int tuple_index_value(Object t, int i) {
result = t.(TupleNode).getElement(i).getNode().(Num).getN().toInt()
or
exists(Object item |
py_citems(t, i, item) and
result = item.(NumericObject).intValue()
)
}
pragma[noinline]
deprecated int version_tuple_value(Object t) {
not exists(tuple_index_value(t, 1)) and result = tuple_index_value(t, 0) * 10
or
not exists(tuple_index_value(t, 2)) and
result = tuple_index_value(t, 0) * 10 + tuple_index_value(t, 1)
or
tuple_index_value(t, 2) = 0 and result = tuple_index_value(t, 0) * 10 + tuple_index_value(t, 1)
or
tuple_index_value(t, 2) > 0 and
result = tuple_index_value(t, 0) * 10 + tuple_index_value(t, 1) + 1
}
/** Choose a version numbers that represent the extreme of supported versions. */
deprecated private int major_minor() {
if major_version() = 3
then (
result = 33 or result = 37
) else (
// 3.3 to 3.7
result = 25 or result = 27
) // 2.5 to 2.7
}
/** Compares the given tuple object to both the maximum and minimum possible sys.version_info values */
deprecated int version_tuple_compare(Object t) {
version_tuple_value(t) < major_minor() and result = -1
or
version_tuple_value(t) = major_minor() and result = 0
or
version_tuple_value(t) > major_minor() and result = 1
}
/** Holds if `cls` is a new-style class if it were to have no explicit base classes */
deprecated predicate baseless_is_new_style(ClassObject cls) {
cls.isBuiltin()
or
major_version() = 3 and exists(cls)
or
exists(cls.declaredMetaClass())
}
/*
* The following predicates exist in order to provide
* more precise type information than the underlying
* database relations. This help to optimise the points-to
* analysis.
*/
/** Holds if this class (not on a super-class) declares name */
pragma[noinline]
predicate class_declares_attribute(ClassObject cls, string name) {
exists(Class defn |
defn = cls.getPyClass() and
class_defines_name(defn, name)
)
or
exists(Builtin o |
o = cls.asBuiltin().getMember(name) and
not exists(Builtin sup |
sup = cls.asBuiltin().getBaseClass() and
o = sup.getMember(name)
)
)
}
/** Holds if the class defines name */
private predicate class_defines_name(Class cls, string name) {
exists(SsaVariable var | name = var.getId() and var.getAUse() = cls.getANormalExit())
}
/** Gets a return value CFG node, provided that is safe to track across returns */
deprecated ControlFlowNode safe_return_node(PyFunctionObject func) {
result = func.getAReturnedNode() and
// Not a parameter
not exists(Parameter p, SsaVariable pvar |
p.asName().getAFlowNode() = pvar.getDefinition() and
result = pvar.getAUse()
) and
// No alternatives
not exists(ControlFlowNode branch | branch.isBranch() and branch.getScope() = func.getFunction())
}
/** Holds if it can be determined from the control flow graph alone that this function can never return */
deprecated predicate function_can_never_return(FunctionObject func) {
/*
* A Python function never returns if it has no normal exits that are not dominated by a
* call to a function which itself never returns.
*/
exists(Function f |
f = func.getFunction() and
not exists(f.getAnExitNode())
)
or
func = ModuleObject::named("sys").attr("exit")
}
/** Hold if outer contains inner, both are contained within a test and inner is a use is a plain use or an attribute lookup */
pragma[noinline]
deprecated predicate contains_interesting_expression_within_test(
ControlFlowNode outer, ControlFlowNode inner
) {
inner.isLoad() and
exists(ControlFlowNode test |
outer.getAChild*() = inner and
test_contains(test, outer) and
test_contains(test, inner)
|
inner instanceof NameNode or
inner instanceof AttrNode
)
}
/** Hold if `expr` is a test (a branch) and `use` is within that test */
predicate test_contains(ControlFlowNode expr, ControlFlowNode use) {
expr.getNode() instanceof Expr and
expr.isBranch() and
expr.getAChild*() = use
}
/** Holds if `test` is a test (a branch), `use` is within that test and `def` is an edge from that test with `sense` */
deprecated predicate refinement_test(
ControlFlowNode test, ControlFlowNode use, boolean sense, PyEdgeRefinement def
) {
/*
* Because calls such as `len` may create a new variable, we need to go via the source variable
* That is perfectly safe as we are only dealing with calls that do not mutate their arguments.
*/
use = def.getInput().getSourceVariable().(Variable).getAUse() and
test = def.getPredecessor().getLastNode() and
test_contains(test, use) and
sense = def.getSense()
}
/** Holds if `f` is an import of the form `from .[...] import name` and the enclosing scope is an __init__ module */
pragma[noinline]
deprecated predicate live_import_from_dot_in_init(ImportMemberNode f, EssaVariable var) {
exists(string name |
import_from_dot_in_init(f.getModule(name)) and
var.getSourceVariable().getName() = name and
var.getAUse() = f
)
}
/** Holds if `f` is an import of the form `from .[...] import ...` and the enclosing scope is an __init__ module */
predicate import_from_dot_in_init(ImportExprNode f) {
f.getScope() = any(Module m).getInitModule() and
(
f.getNode().getLevel() = 1 and
not exists(f.getNode().getName())
or
f.getNode().getImportedModuleName() = f.getEnclosingModule().getPackage().getName()
)
}
/** Gets the pseudo-object representing the value referred to by an undefined variable */
Object undefinedVariable() { py_special_objects(result, "_semmle_undefined_value") }
/** Gets the pseudo-object representing an unknown value */
Object unknownValue() { result.asBuiltin() = Builtin::unknown() }
deprecated BuiltinCallable theTypeNewMethod() {
result.asBuiltin() = theTypeType().asBuiltin().getMember("__new__")
}
/** Gets the `value, cls, origin` that `f` would refer to if it has not been assigned some other value */
pragma[noinline]
deprecated predicate potential_builtin_points_to(
NameNode f, Object value, ClassObject cls, ControlFlowNode origin
) {
f.isGlobal() and
f.isLoad() and
origin = f and
(
builtin_name_points_to(f.getId(), value, cls)
or
not exists(Object::builtin(f.getId())) and value = unknownValue() and cls = theUnknownType()
)
}
pragma[noinline]
deprecated predicate builtin_name_points_to(string name, Object value, ClassObject cls) {
value = Object::builtin(name) and cls.asBuiltin() = value.asBuiltin().getClass()
}
pragma[nomagic]
private predicate essa_var_scope(SsaSourceVariable var, Scope pred_scope, EssaVariable pred_var) {
BaseFlow::reaches_exit(pred_var) and
pred_var.getScope() = pred_scope and
var = pred_var.getSourceVariable()
}
pragma[nomagic]
private predicate scope_entry_def_scope(
SsaSourceVariable var, Scope succ_scope, ScopeEntryDefinition succ_def
) {
var = succ_def.getSourceVariable() and
succ_def.getScope() = succ_scope
}
pragma[nomagic]
private predicate step_through_init(Scope succ_scope, Scope pred_scope, Scope init) {
init.getName() = "__init__" and
init.precedes(succ_scope) and
pred_scope.precedes(init)
}
pragma[nomagic]
private predicate scope_entry_value_transfer_through_init(
EssaVariable pred_var, Scope pred_scope, ScopeEntryDefinition succ_def, Scope succ_scope
) {
exists(SsaSourceVariable var, Scope init |
var instanceof GlobalVariable and
essa_var_scope(var, pragma[only_bind_into](pred_scope), pred_var) and
scope_entry_def_scope(var, succ_scope, succ_def) and
step_through_init(succ_scope, pragma[only_bind_into](pred_scope), init) and
not var.(Variable).getAStore().getScope() = init
)
}
module BaseFlow {
predicate reaches_exit(EssaVariable var) { var.getAUse() = var.getScope().getANormalExit() }
/* Helper for this_scope_entry_value_transfer(...). Transfer of values from earlier scope to later on */
cached
predicate scope_entry_value_transfer_from_earlier(
EssaVariable pred_var, Scope pred_scope, ScopeEntryDefinition succ_def, Scope succ_scope
) {
Stages::PointsTo::ref() and
exists(SsaSourceVariable var |
essa_var_scope(var, pred_scope, pred_var) and
scope_entry_def_scope(var, succ_scope, succ_def)
|
pred_scope.precedes(succ_scope)
)
or
// If an `__init__` method does not modify the global variable, then
// we can skip it and take the value directly from the module.
scope_entry_value_transfer_through_init(pred_var, pred_scope, succ_def, succ_scope)
}
}
/** Points-to for syntactic elements where context is not relevant */
deprecated predicate simple_points_to(
ControlFlowNode f, Object value, ClassObject cls, ControlFlowNode origin
) {
kwargs_points_to(f, cls) and value = f and origin = f
or
varargs_points_to(f, cls) and value = f and origin = f
or
BasePointsTo::points_to(f, value, origin) and cls = simple_types(value)
or
value = f.getNode().(ImmutableLiteral).getLiteralObject() and
cls = simple_types(value) and
origin = f
}
/**
* Holds if `bit` is a binary expression node with a bitwise operator.
* Helper for `this_binary_expr_points_to`.
*/
deprecated predicate bitwise_expression_node(
BinaryExprNode bit, ControlFlowNode left, ControlFlowNode right
) {
exists(Operator op | op = bit.getNode().getOp() |
op instanceof BitAnd or
op instanceof BitOr or
op instanceof BitXor
) and
left = bit.getLeft() and
right = bit.getRight()
}
deprecated private Module theCollectionsAbcModule() {
result.getName() = "_abcoll"
or
result.getName() = "_collections_abc"
}
deprecated ClassObject collectionsAbcClass(string name) {
exists(Class cls |
result.getPyClass() = cls and
cls.getName() = name and
cls.getScope() = theCollectionsAbcModule()
)
}