Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 662aedc

Browse files
committed
Python points-to: Fix up module attributes and classmethods.
1 parent fc2c46f commit 662aedc

13 files changed

Lines changed: 112 additions & 94 deletions

File tree

python/ql/src/semmle/python/Flow.qll

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,11 @@ class ControlFlowNode extends @py_flow_node {
217217
this.pointsTo(_, value, _)
218218
}
219219

220+
/** Gets the value that this ControlFlowNode points-to. */
221+
Value pointsTo() {
222+
this.pointsTo(_, result, _)
223+
}
224+
220225
/** The value and origin that this ControlFlowNode points-to. */
221226
predicate pointsTo(Value value, ControlFlowNode origin) {
222227
this.pointsTo(_, value, origin)
@@ -239,28 +244,15 @@ class ControlFlowNode extends @py_flow_node {
239244
/** Gets what this expression might "refer-to" in the given `context`.
240245
*/
241246
predicate refersTo(Context context, Object obj, ClassObject cls, ControlFlowNode origin) {
242-
exists(Value value |
243-
PointsTo::pointsTo(this, context, value, origin) and
244-
cls = value.getClass().getSource() |
245-
if exists(value.getSource().(Object)) then
246-
obj = value.getSource()
247-
else
248-
obj = origin
249-
)
247+
PointsTo::points_to(this, context, obj, cls, origin)
250248
}
251249

252250
/** Whether this flow node might "refer-to" to `value` which is from `origin`
253251
* Unlike `this.refersTo(value, _, origin)` this predicate includes results
254252
* where the class cannot be inferred.
255253
*/
256254
predicate refersTo(Object obj, ControlFlowNode origin) {
257-
exists(Value value |
258-
PointsTo::pointsTo(this, _, value, origin) |
259-
if exists(value.getSource().(Object)) then
260-
obj = value.getSource()
261-
else
262-
obj = origin
263-
)
255+
PointsTo::points_to(this, _, obj, _, origin)
264256
}
265257

266258
/** Equivalent to `this.refersTo(value, _)` */

python/ql/src/semmle/python/objects/Callables.qll

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,23 @@ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFuncti
8080
this = TPythonFunctionObject(result)
8181
}
8282

83-
pragma [noinline]
83+
pragma [nomagic]
8484
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
8585
exists(Function func, ControlFlowNode rval, ControlFlowNode forigin |
8686
func = this.getScope() and
8787
callee.appliesToScope(func) |
8888
rval = func.getAReturnValueFlowNode() and
8989
PointsToInternal::pointsTo(rval, callee, obj, forigin) and
9090
origin = CfgOrigin::fromCfgNode(forigin)
91-
or
91+
)
92+
or
93+
procedureReturnsNone(callee, obj, origin)
94+
}
95+
96+
private predicate procedureReturnsNone(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
97+
exists(Function func |
98+
func = this.getScope() and
99+
callee.appliesToScope(func) |
92100
PointsToInternal::reachableBlock(blockReturningNone(func), callee) and
93101
obj = ObjectInternal::none_() and
94102
origin = CfgOrigin::unknown()
@@ -349,7 +357,7 @@ class BoundMethodObjectInternal extends CallableObjectInternal, TBoundMethod {
349357
class ClassMethodObjectInternal extends ObjectInternal, TClassMethod {
350358

351359
override string toString() {
352-
result = "classmethod()"
360+
result = "classmethod(" + this.getFunction() + ")"
353361
}
354362

355363
override boolean booleanValue() { result = true }
@@ -369,7 +377,7 @@ class ClassMethodObjectInternal extends ObjectInternal, TClassMethod {
369377

370378
override boolean isClass() { result = false }
371379

372-
override ObjectInternal getClass() { result = ObjectInternal::builtin("classmethod") }
380+
override ObjectInternal getClass() { result = ObjectInternal::classMethod() }
373381

374382
override boolean isComparable() { none() }
375383

@@ -385,9 +393,7 @@ class ClassMethodObjectInternal extends ObjectInternal, TClassMethod {
385393

386394
override string strValue() { none() }
387395

388-
override predicate calleeAndOffset(Function scope, int paramOffset) {
389-
this.getFunction().calleeAndOffset(scope, paramOffset)
390-
}
396+
override predicate calleeAndOffset(Function scope, int paramOffset) { none() }
391397

392398
override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() }
393399

@@ -397,17 +403,25 @@ class ClassMethodObjectInternal extends ObjectInternal, TClassMethod {
397403

398404
override predicate descriptorGet(ObjectInternal instance, ObjectInternal value, CfgOrigin origin) {
399405
any(ObjectInternal obj).binds(instance, _, this) and
400-
(
401-
instance.isClass() = false and
402-
value = TBoundMethod(instance.getClass(), this.getFunction())
406+
exists(ObjectInternal cls |
407+
instance.isClass() = false and cls = instance.getClass()
403408
or
404-
instance.isClass() = true and
405-
value = TBoundMethod(instance, this.getFunction())
406-
) and
407-
origin = CfgOrigin::unknown()
409+
instance.isClass() = true and cls = instance
410+
|
411+
value = TBoundMethod(cls, this.getFunction()) and
412+
origin = CfgOrigin::unknown()
413+
)
408414
}
409415

410-
override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { none() }
416+
override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) {
417+
descriptor = this.getFunction() and
418+
exists(ObjectInternal instance |
419+
any(ObjectInternal obj).binds(instance, name, this) |
420+
instance.isClass() = false and cls = instance.getClass()
421+
or
422+
instance.isClass() = true and cls = instance
423+
)
424+
}
411425

412426
}
413427

python/ql/src/semmle/python/objects/Modules.qll

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,11 @@ class PackageObjectInternal extends ModuleObjectInternal, TPackageObject {
156156
or
157157
exists(Module init |
158158
init = this.getSourceModule() and
159-
not exists(EssaVariable var | var.getAUse() = init.getANormalExit() and var.getSourceVariable().getName() = name) and
160-
ModuleAttributes::pointsToAtExit(init, name, ObjectInternal::undefined(), _) and
159+
(
160+
not exists(EssaVariable var | var.getAUse() = init.getANormalExit() and var.getSourceVariable().getName() = name)
161+
or
162+
ModuleAttributes::pointsToAtExit(init, name, ObjectInternal::undefined(), _)
163+
) and
161164
value = this.submodule(name) and
162165
origin = CfgOrigin::fromObject(value)
163166
)
@@ -176,6 +179,14 @@ class PackageObjectInternal extends ModuleObjectInternal, TPackageObject {
176179
result = this.getSourceModule().getEntryNode()
177180
}
178181

182+
override @py_object getSource() {
183+
exists(Module package |
184+
package.isPackage() and
185+
package.getPath() = this.getFolder() and
186+
result = package.getEntryNode()
187+
)
188+
}
189+
179190
}
180191

181192
/** Get the ESSA pseudo-variable used to retain module state
@@ -237,14 +248,8 @@ class PythonModuleObjectInternal extends ModuleObjectInternal, TPythonModule {
237248
imp.isImport() and
238249
value != ObjectInternal::undefined()
239250
)
240-
// TO DO, dollar variable...
241-
//or
242-
//not exists(EssaVariable var | var.getAUse() = m.getANormalExit() and var.getSourceVariable().getName() = name) and
243-
//exists(EssaVariable var, PointsToContext imp |
244-
// var.getAUse() = m.getANormalExit() and isModuleStateVariable(var) |
245-
// PointsToInternal::ssa_variable_named_attribute_pointsTo(var, imp, name, obj, origin) and
246-
// imp.isImport() and obj != ObjectInternal::undefined()
247-
//)
251+
or
252+
ModuleAttributes::pointsToAtExit(this.getSourceModule(), name, value, origin)
248253
}
249254

250255
override predicate attributesUnknown() { none() }

python/ql/src/semmle/python/objects/ObjectAPI.qll

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,6 @@ class Value extends TObject {
5555
/* For backwards compatibility with old API */
5656
deprecated ObjectSource getSource() {
5757
result = this.(ObjectInternal).getSource()
58-
or
59-
exists(Module p |
60-
p.isPackage() and
61-
p.getPath() = this.(PackageObjectInternal).getFolder() and
62-
result = p.getEntryNode()
63-
)
6458
}
6559

6660
/** Gets the `ControlFlowNode` that will be passed as the nth argument to `this` when called at `call`.

python/ql/src/semmle/python/objects/ObjectInternal.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,8 @@ module ObjectInternal {
363363
result = TBuiltinOpaqueObject(b)
364364
or
365365
result = TBuiltinModuleObject(b)
366+
or
367+
result = TBuiltinMethodObject(b)
366368
}
367369

368370
ObjectInternal classMethod() {

python/ql/src/semmle/python/objects/TObject.qll

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ private import semmle.python.pointsto.PointsToContext
66

77
newtype TObject =
88
TBuiltinClassObject(Builtin bltn) {
9-
bltn.isClass() and
9+
bltn.isClass() and
1010
not bltn = Builtin::unknownType() and
1111
not bltn = Builtin::special("type")
1212
}
@@ -137,7 +137,7 @@ predicate static_method(CallNode instantiation, CallableObjectInternal function,
137137
}
138138

139139
predicate class_method(CallNode instantiation, CallableObjectInternal function, PointsToContext context) {
140-
PointsToInternal::pointsTo(instantiation.getFunction(), context, ObjectInternal::builtin("classmethod"), _) and
140+
PointsToInternal::pointsTo(instantiation.getFunction(), context, ObjectInternal::classMethod(), _) and
141141
PointsToInternal::pointsTo(instantiation.getArg(0), context, function, _)
142142
}
143143

@@ -321,8 +321,8 @@ library class ClassDecl extends @py_object {
321321
name = "unicode" or
322322
name = "tuple" or
323323
name = "property" or
324-
name = "classmethod" or
325-
name = "staticmethod" or
324+
name = "ClassMethod" or
325+
name = "StaticMethod" or
326326
name = "MethodType" or
327327
name = "ModuleType"
328328
)

python/ql/src/semmle/python/pointsto/PointsTo.qll

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ library class CfgOrigin extends TCfgOrigin {
8484
this = TFlowNodeOrigin(result)
8585
}
8686

87+
pragma[inline]
8788
CfgOrigin fix(ControlFlowNode here) {
8889
this = TUnknownOrigin() and result = TFlowNodeOrigin(here)
8990
or
@@ -128,7 +129,7 @@ module PointsTo {
128129
pointsToValue(f, context, value, origin) and
129130
cls = value.getClass().getSource() |
130131
obj = value.getSource() or
131-
not exists(value.getSource()) and obj = origin
132+
not exists(value.getSource()) and obj = origin and not cls = theBoundMethodType()
132133
)
133134
or
134135
/* Backwards compatibility for *args and **kwargs */
@@ -663,22 +664,20 @@ module InterModulePointsTo {
663664

664665
/** Points-to for `from ... import *`. */
665666
predicate import_star_points_to(ImportStarRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin) {
666-
exists(CfgOrigin orig |
667-
origin = orig.fix(def.getDefiningNode())
668-
|
669-
exists(ModuleObjectInternal mod, string name |
670-
PointsToInternal::pointsTo(def.getDefiningNode().(ImportStarNode).getModule(), context, mod, _) and
671-
name = def.getSourceVariable().getName() |
672-
/* Attribute from imported module */
673-
module_exports_boolean(mod, name) = true and
674-
mod.attribute(name, value, origin)
675-
)
676-
or
677-
exists(EssaVariable var |
678-
/* Retain value held before import */
679-
variable_not_redefined_by_import_star(var, context, def) and
680-
PointsToInternal::variablePointsTo(var, context, value,orig)
681-
)
667+
/* Attribute from imported module */
668+
exists(CfgOrigin orig, ImportStarNode imp, ModuleObjectInternal mod, string name |
669+
imp = def.getDefiningNode() and
670+
PointsToInternal::pointsTo(imp.getModule(), context, mod, _) and
671+
name = def.getSourceVariable().getName() and
672+
module_exports_boolean(mod, name) = true and
673+
mod.attribute(name, value, orig) and
674+
origin = orig.fix(imp)
675+
)
676+
or
677+
/* Retain value held before import */
678+
exists(EssaVariable var |
679+
variable_not_redefined_by_import_star(var, context, def) and
680+
PointsToInternal::variablePointsTo(var, context, value, origin)
682681
)
683682
}
684683

@@ -751,6 +750,8 @@ module InterModulePointsTo {
751750
)
752751
or
753752
name = "__name__" and result = true
753+
or
754+
exists(mod.(BuiltinModuleObjectInternal).getBuiltin().getMember(name)) and result = true
754755
}
755756

756757
}
@@ -1650,14 +1651,18 @@ cached module Types {
16501651
module AttributePointsTo {
16511652

16521653
predicate attributePointsTo(ControlFlowNode f, Context context, string name, ObjectInternal value, ControlFlowNode origin) {
1653-
exists(ObjectInternal obj, Context prev, AttributeAssignment def |
1654-
PointsToInternal::pointsTo(f, context, obj, _) and
1655-
PointsToInternal::variablePointsTo(def.getInput(), prev, obj, _) and
1654+
exists(ObjectInternal defobj, Context prev, AttributeAssignment def, ObjectInternal useobj |
1655+
PointsToInternal::pointsTo(f, context, useobj, _) and
1656+
PointsToInternal::variablePointsTo(def.getInput(), prev, defobj, _) and
16561657
PointsToInternal::pointsTo(def.getValue(), prev, value, origin) and name = def.getName()
16571658
|
1658-
prev.getOuter*().getCall().getBasicBlock().reaches(context.getOuter*().getCall().getBasicBlock())
1659+
prev.getOuter*().getCall().getBasicBlock().reaches(context.getOuter*().getCall().getBasicBlock()) and
1660+
useobj.(SelfInstanceInternal).getClass() = defobj.(SelfInstanceInternal).getClass()
1661+
or
1662+
def.getScope().getScope*().precedes(f.getScope().getScope*()) and
1663+
useobj.(SelfInstanceInternal).getClass() = defobj.(SelfInstanceInternal).getClass()
16591664
or
1660-
def.getScope().getScope*().precedes(f.getScope().getScope*())
1665+
def.getDefiningNode().getBasicBlock().dominates(f.getBasicBlock()) and defobj = useobj
16611666
)
16621667
}
16631668

@@ -1697,8 +1702,9 @@ module ModuleAttributes {
16971702
private predicate importStarPointsTo(ImportStarRefinement def, string name, ObjectInternal value, CfgOrigin origin) {
16981703
def.getVariable().getName() = "$" and
16991704
exists(ImportStarNode imp, ModuleObjectInternal mod, CfgOrigin orig |
1705+
imp = def.getDefiningNode() and
17001706
origin = orig.fix(imp) and
1701-
PointsToInternal::pointsTo(def.getDefiningNode().(ImportStarNode).getModule(), any(Context ctx | ctx.isImport()), mod, _) |
1707+
PointsToInternal::pointsTo(imp.getModule(), any(Context ctx | ctx.isImport()), mod, _) |
17021708
/* Attribute from imported module */
17031709
InterModulePointsTo::module_exports_boolean(mod, name) = true and
17041710
mod.attribute(name, value, orig) and

python/ql/src/semmle/python/types/Builtins.qll

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,6 @@ class Builtin extends @py_cobject {
8484
predicate isMethod() {
8585
this.getClass() = Builtin::special("MethodDescriptorType")
8686
or
87-
this.getClass() = Builtin::special("BuiltinFunctionType") and
88-
exists(Builtin cls | cls.isClass() and cls.getMember(_) = this)
89-
or
9087
this.getClass().getName() = "wrapper_descriptor"
9188
}
9289

python/ql/src/semmle/python/types/ModuleObject.qll

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@ abstract class ModuleObject extends Object {
5757
)
5858
}
5959

60+
predicate attributeRefersTo(string name, Object obj, ClassObject cls, ControlFlowNode origin) {
61+
exists(Value val, CfgOrigin valorig |
62+
theModule().(ModuleObjectInternal).attribute(name, val, valorig) and
63+
obj = val.getSource() and
64+
cls = val.getClass().getSource() and
65+
origin = valorig.toCfgNode()
66+
)
67+
}
68+
6069
/** Gets the package for this module. */
6170
PackageObject getPackage() {
6271
this.getName().matches("%.%") and

python/ql/src/semmle/python/types/Object.qll

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,3 +501,20 @@ Object theUnknownType() {
501501
result.asBuiltin() = Builtin::unknownType()
502502
}
503503

504+
/* For backwards compatibility */
505+
506+
class SuperBoundMethod extends Object {
507+
508+
string name;
509+
510+
SuperBoundMethod() {
511+
this.(AttrNode).getObject(name).pointsTo().getClass() = Value::named("super")
512+
}
513+
514+
override string toString() {
515+
result = "super()." + name
516+
}
517+
518+
}
519+
520+

0 commit comments

Comments
 (0)