mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Python points-to: Fix up module attributes and classmethods.
This commit is contained in:
@@ -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, _)` */
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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() }
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -363,6 +363,8 @@ module ObjectInternal {
|
||||
result = TBuiltinOpaqueObject(b)
|
||||
or
|
||||
result = TBuiltinModuleObject(b)
|
||||
or
|
||||
result = TBuiltinMethodObject(b)
|
||||
}
|
||||
|
||||
ObjectInternal classMethod() {
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
fail
|
||||
@@ -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
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
|
||||
|
||||
class Foo(object):
|
||||
pass
|
||||
|
||||
class Bar(object):
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user