Python points-to: Fix up module attributes and classmethods.

This commit is contained in:
Mark Shannon
2019-04-08 13:08:01 +01:00
parent fc2c46fe4a
commit 662aedcb13
13 changed files with 112 additions and 94 deletions

View File

@@ -217,6 +217,11 @@ class ControlFlowNode extends @py_flow_node {
this.pointsTo(_, value, _)
}
/** Gets the value that this ControlFlowNode points-to. */
Value pointsTo() {
this.pointsTo(_, result, _)
}
/** The value and origin that this ControlFlowNode points-to. */
predicate pointsTo(Value value, ControlFlowNode origin) {
this.pointsTo(_, value, origin)
@@ -239,14 +244,7 @@ class ControlFlowNode extends @py_flow_node {
/** Gets what this expression might "refer-to" in the given `context`.
*/
predicate refersTo(Context context, Object obj, ClassObject cls, ControlFlowNode origin) {
exists(Value value |
PointsTo::pointsTo(this, context, value, origin) and
cls = value.getClass().getSource() |
if exists(value.getSource().(Object)) then
obj = value.getSource()
else
obj = origin
)
PointsTo::points_to(this, context, obj, cls, origin)
}
/** Whether this flow node might "refer-to" to `value` which is from `origin`
@@ -254,13 +252,7 @@ class ControlFlowNode extends @py_flow_node {
* where the class cannot be inferred.
*/
predicate refersTo(Object obj, ControlFlowNode origin) {
exists(Value value |
PointsTo::pointsTo(this, _, value, origin) |
if exists(value.getSource().(Object)) then
obj = value.getSource()
else
obj = origin
)
PointsTo::points_to(this, _, obj, _, origin)
}
/** Equivalent to `this.refersTo(value, _)` */

View File

@@ -80,7 +80,7 @@ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFuncti
this = TPythonFunctionObject(result)
}
pragma [noinline]
pragma [nomagic]
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
exists(Function func, ControlFlowNode rval, ControlFlowNode forigin |
func = this.getScope() and
@@ -88,7 +88,15 @@ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFuncti
rval = func.getAReturnValueFlowNode() and
PointsToInternal::pointsTo(rval, callee, obj, forigin) and
origin = CfgOrigin::fromCfgNode(forigin)
or
)
or
procedureReturnsNone(callee, obj, origin)
}
private predicate procedureReturnsNone(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
exists(Function func |
func = this.getScope() and
callee.appliesToScope(func) |
PointsToInternal::reachableBlock(blockReturningNone(func), callee) and
obj = ObjectInternal::none_() and
origin = CfgOrigin::unknown()
@@ -349,7 +357,7 @@ class BoundMethodObjectInternal extends CallableObjectInternal, TBoundMethod {
class ClassMethodObjectInternal extends ObjectInternal, TClassMethod {
override string toString() {
result = "classmethod()"
result = "classmethod(" + this.getFunction() + ")"
}
override boolean booleanValue() { result = true }
@@ -369,7 +377,7 @@ class ClassMethodObjectInternal extends ObjectInternal, TClassMethod {
override boolean isClass() { result = false }
override ObjectInternal getClass() { result = ObjectInternal::builtin("classmethod") }
override ObjectInternal getClass() { result = ObjectInternal::classMethod() }
override boolean isComparable() { none() }
@@ -385,9 +393,7 @@ class ClassMethodObjectInternal extends ObjectInternal, TClassMethod {
override string strValue() { none() }
override predicate calleeAndOffset(Function scope, int paramOffset) {
this.getFunction().calleeAndOffset(scope, paramOffset)
}
override predicate calleeAndOffset(Function scope, int paramOffset) { none() }
override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() }
@@ -397,17 +403,25 @@ class ClassMethodObjectInternal extends ObjectInternal, TClassMethod {
override predicate descriptorGet(ObjectInternal instance, ObjectInternal value, CfgOrigin origin) {
any(ObjectInternal obj).binds(instance, _, this) and
(
instance.isClass() = false and
value = TBoundMethod(instance.getClass(), this.getFunction())
exists(ObjectInternal cls |
instance.isClass() = false and cls = instance.getClass()
or
instance.isClass() = true and
value = TBoundMethod(instance, this.getFunction())
) and
origin = CfgOrigin::unknown()
instance.isClass() = true and cls = instance
|
value = TBoundMethod(cls, this.getFunction()) and
origin = CfgOrigin::unknown()
)
}
override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { none() }
override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) {
descriptor = this.getFunction() and
exists(ObjectInternal instance |
any(ObjectInternal obj).binds(instance, name, this) |
instance.isClass() = false and cls = instance.getClass()
or
instance.isClass() = true and cls = instance
)
}
}

View File

@@ -156,8 +156,11 @@ class PackageObjectInternal extends ModuleObjectInternal, TPackageObject {
or
exists(Module init |
init = this.getSourceModule() and
not exists(EssaVariable var | var.getAUse() = init.getANormalExit() and var.getSourceVariable().getName() = name) and
ModuleAttributes::pointsToAtExit(init, name, ObjectInternal::undefined(), _) and
(
not exists(EssaVariable var | var.getAUse() = init.getANormalExit() and var.getSourceVariable().getName() = name)
or
ModuleAttributes::pointsToAtExit(init, name, ObjectInternal::undefined(), _)
) and
value = this.submodule(name) and
origin = CfgOrigin::fromObject(value)
)
@@ -176,6 +179,14 @@ class PackageObjectInternal extends ModuleObjectInternal, TPackageObject {
result = this.getSourceModule().getEntryNode()
}
override @py_object getSource() {
exists(Module package |
package.isPackage() and
package.getPath() = this.getFolder() and
result = package.getEntryNode()
)
}
}
/** Get the ESSA pseudo-variable used to retain module state
@@ -237,14 +248,8 @@ class PythonModuleObjectInternal extends ModuleObjectInternal, TPythonModule {
imp.isImport() and
value != ObjectInternal::undefined()
)
// TO DO, dollar variable...
//or
//not exists(EssaVariable var | var.getAUse() = m.getANormalExit() and var.getSourceVariable().getName() = name) and
//exists(EssaVariable var, PointsToContext imp |
// var.getAUse() = m.getANormalExit() and isModuleStateVariable(var) |
// PointsToInternal::ssa_variable_named_attribute_pointsTo(var, imp, name, obj, origin) and
// imp.isImport() and obj != ObjectInternal::undefined()
//)
or
ModuleAttributes::pointsToAtExit(this.getSourceModule(), name, value, origin)
}
override predicate attributesUnknown() { none() }

View File

@@ -55,12 +55,6 @@ class Value extends TObject {
/* For backwards compatibility with old API */
deprecated ObjectSource getSource() {
result = this.(ObjectInternal).getSource()
or
exists(Module p |
p.isPackage() and
p.getPath() = this.(PackageObjectInternal).getFolder() and
result = p.getEntryNode()
)
}
/** Gets the `ControlFlowNode` that will be passed as the nth argument to `this` when called at `call`.

View File

@@ -363,6 +363,8 @@ module ObjectInternal {
result = TBuiltinOpaqueObject(b)
or
result = TBuiltinModuleObject(b)
or
result = TBuiltinMethodObject(b)
}
ObjectInternal classMethod() {

View File

@@ -6,7 +6,7 @@ private import semmle.python.pointsto.PointsToContext
newtype TObject =
TBuiltinClassObject(Builtin bltn) {
bltn.isClass() and
bltn.isClass() and
not bltn = Builtin::unknownType() and
not bltn = Builtin::special("type")
}
@@ -137,7 +137,7 @@ predicate static_method(CallNode instantiation, CallableObjectInternal function,
}
predicate class_method(CallNode instantiation, CallableObjectInternal function, PointsToContext context) {
PointsToInternal::pointsTo(instantiation.getFunction(), context, ObjectInternal::builtin("classmethod"), _) and
PointsToInternal::pointsTo(instantiation.getFunction(), context, ObjectInternal::classMethod(), _) and
PointsToInternal::pointsTo(instantiation.getArg(0), context, function, _)
}
@@ -321,8 +321,8 @@ library class ClassDecl extends @py_object {
name = "unicode" or
name = "tuple" or
name = "property" or
name = "classmethod" or
name = "staticmethod" or
name = "ClassMethod" or
name = "StaticMethod" or
name = "MethodType" or
name = "ModuleType"
)

View File

@@ -84,6 +84,7 @@ library class CfgOrigin extends TCfgOrigin {
this = TFlowNodeOrigin(result)
}
pragma[inline]
CfgOrigin fix(ControlFlowNode here) {
this = TUnknownOrigin() and result = TFlowNodeOrigin(here)
or
@@ -128,7 +129,7 @@ module PointsTo {
pointsToValue(f, context, value, origin) and
cls = value.getClass().getSource() |
obj = value.getSource() or
not exists(value.getSource()) and obj = origin
not exists(value.getSource()) and obj = origin and not cls = theBoundMethodType()
)
or
/* Backwards compatibility for *args and **kwargs */
@@ -663,22 +664,20 @@ module InterModulePointsTo {
/** Points-to for `from ... import *`. */
predicate import_star_points_to(ImportStarRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin) {
exists(CfgOrigin orig |
origin = orig.fix(def.getDefiningNode())
|
exists(ModuleObjectInternal mod, string name |
PointsToInternal::pointsTo(def.getDefiningNode().(ImportStarNode).getModule(), context, mod, _) and
name = def.getSourceVariable().getName() |
/* Attribute from imported module */
module_exports_boolean(mod, name) = true and
mod.attribute(name, value, origin)
)
or
exists(EssaVariable var |
/* Retain value held before import */
variable_not_redefined_by_import_star(var, context, def) and
PointsToInternal::variablePointsTo(var, context, value,orig)
)
/* Attribute from imported module */
exists(CfgOrigin orig, ImportStarNode imp, ModuleObjectInternal mod, string name |
imp = def.getDefiningNode() and
PointsToInternal::pointsTo(imp.getModule(), context, mod, _) and
name = def.getSourceVariable().getName() and
module_exports_boolean(mod, name) = true and
mod.attribute(name, value, orig) and
origin = orig.fix(imp)
)
or
/* Retain value held before import */
exists(EssaVariable var |
variable_not_redefined_by_import_star(var, context, def) and
PointsToInternal::variablePointsTo(var, context, value, origin)
)
}
@@ -751,6 +750,8 @@ module InterModulePointsTo {
)
or
name = "__name__" and result = true
or
exists(mod.(BuiltinModuleObjectInternal).getBuiltin().getMember(name)) and result = true
}
}
@@ -1650,14 +1651,18 @@ cached module Types {
module AttributePointsTo {
predicate attributePointsTo(ControlFlowNode f, Context context, string name, ObjectInternal value, ControlFlowNode origin) {
exists(ObjectInternal obj, Context prev, AttributeAssignment def |
PointsToInternal::pointsTo(f, context, obj, _) and
PointsToInternal::variablePointsTo(def.getInput(), prev, obj, _) and
exists(ObjectInternal defobj, Context prev, AttributeAssignment def, ObjectInternal useobj |
PointsToInternal::pointsTo(f, context, useobj, _) and
PointsToInternal::variablePointsTo(def.getInput(), prev, defobj, _) and
PointsToInternal::pointsTo(def.getValue(), prev, value, origin) and name = def.getName()
|
prev.getOuter*().getCall().getBasicBlock().reaches(context.getOuter*().getCall().getBasicBlock())
prev.getOuter*().getCall().getBasicBlock().reaches(context.getOuter*().getCall().getBasicBlock()) and
useobj.(SelfInstanceInternal).getClass() = defobj.(SelfInstanceInternal).getClass()
or
def.getScope().getScope*().precedes(f.getScope().getScope*())
def.getScope().getScope*().precedes(f.getScope().getScope*()) and
useobj.(SelfInstanceInternal).getClass() = defobj.(SelfInstanceInternal).getClass()
or
def.getDefiningNode().getBasicBlock().dominates(f.getBasicBlock()) and defobj = useobj
)
}
@@ -1697,8 +1702,9 @@ module ModuleAttributes {
private predicate importStarPointsTo(ImportStarRefinement def, string name, ObjectInternal value, CfgOrigin origin) {
def.getVariable().getName() = "$" and
exists(ImportStarNode imp, ModuleObjectInternal mod, CfgOrigin orig |
imp = def.getDefiningNode() and
origin = orig.fix(imp) and
PointsToInternal::pointsTo(def.getDefiningNode().(ImportStarNode).getModule(), any(Context ctx | ctx.isImport()), mod, _) |
PointsToInternal::pointsTo(imp.getModule(), any(Context ctx | ctx.isImport()), mod, _) |
/* Attribute from imported module */
InterModulePointsTo::module_exports_boolean(mod, name) = true and
mod.attribute(name, value, orig) and

View File

@@ -84,9 +84,6 @@ class Builtin extends @py_cobject {
predicate isMethod() {
this.getClass() = Builtin::special("MethodDescriptorType")
or
this.getClass() = Builtin::special("BuiltinFunctionType") and
exists(Builtin cls | cls.isClass() and cls.getMember(_) = this)
or
this.getClass().getName() = "wrapper_descriptor"
}

View File

@@ -57,6 +57,15 @@ abstract class ModuleObject extends Object {
)
}
predicate attributeRefersTo(string name, Object obj, ClassObject cls, ControlFlowNode origin) {
exists(Value val, CfgOrigin valorig |
theModule().(ModuleObjectInternal).attribute(name, val, valorig) and
obj = val.getSource() and
cls = val.getClass().getSource() and
origin = valorig.toCfgNode()
)
}
/** Gets the package for this module. */
PackageObject getPackage() {
this.getName().matches("%.%") and

View File

@@ -501,3 +501,20 @@ Object theUnknownType() {
result.asBuiltin() = Builtin::unknownType()
}
/* For backwards compatibility */
class SuperBoundMethod extends Object {
string name;
SuperBoundMethod() {
this.(AttrNode).getObject(name).pointsTo().getClass() = Value::named("super")
}
override string toString() {
result = "super()." + name
}
}

View File

@@ -1,9 +0,0 @@
import python
private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.PointsTo2
from ClassObjectInternal cls, ControlFlowNode f
where cls.introduced(f, _)
select cls.getName(), f

View File

@@ -1,8 +0,0 @@
class Foo(object):
pass
class Bar(object):
pass