Python points-to: Further types and flow.

This commit is contained in:
Mark Shannon
2019-03-21 19:33:17 +00:00
parent e3ed8c6abf
commit aa30745492
8 changed files with 511 additions and 83 deletions

View File

@@ -81,6 +81,10 @@ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFuncti
)
}
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
none()
}
override predicate calleeAndOffset(Function scope, int paramOffset) {
scope = this.getScope() and paramOffset = 0
}
@@ -98,7 +102,7 @@ class BuiltinFunctionObjectInternal extends CallableObjectInternal, TBuiltinFunc
}
override string toString() {
result = "builtin function " + this.getBuiltin().getName()
result = "Builtin-function " + this.getBuiltin().getName()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
@@ -112,6 +116,10 @@ class BuiltinFunctionObjectInternal extends CallableObjectInternal, TBuiltinFunc
override boolean isComparable() { result = true }
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
none()
}
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
exists(Builtin func, ClassObjectInternal cls |
func = this.getBuiltin() and
func != Builtin::builtin("isinstance") and
@@ -121,8 +129,7 @@ class BuiltinFunctionObjectInternal extends CallableObjectInternal, TBuiltinFunc
cls = ObjectInternal::fromBuiltin(this.getReturnType()) and
obj = TUnknownInstance(cls)
) and
origin = CfgOrigin::unknown() and
callee_for_object(callee, this)
origin = CfgOrigin::unknown()
}
override ControlFlowNode getOrigin() {
@@ -185,6 +192,10 @@ class BuiltinMethodObjectInternal extends CallableObjectInternal, TBuiltinMethod
override boolean isComparable() { result = true }
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
none()
}
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
// TO DO .. Result should be be a unknown value of a known class if the return type is known or just an unknown.
none()
}
@@ -235,8 +246,12 @@ class BoundMethodObjectInternal extends CallableObjectInternal, TBoundMethod {
this.getFunction().callResult(callee, obj, origin)
}
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
this.getFunction().callResult(obj, origin)
}
override ControlFlowNode getOrigin() {
this = TBoundMethod(result, _, _, _)
none()
}
override predicate calleeAndOffset(Function scope, int paramOffset) {

View File

@@ -86,7 +86,11 @@ class PythonClassObjectInternal extends ClassObjectInternal, TPythonClassObject
override predicate attributesUnknown() { none() }
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
// TO DO .. Result should (in most cases) be an instance
none()
}
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
// Handled by Instance classes.
none()
}
@@ -101,7 +105,7 @@ class BuiltinClassObjectInternal extends ClassObjectInternal, TBuiltinClassObjec
}
override string toString() {
result = "builtin class " + this.getBuiltin().getName()
result = "builtin-class " + this.getBuiltin().getName()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
@@ -132,6 +136,10 @@ class BuiltinClassObjectInternal extends ClassObjectInternal, TBuiltinClassObjec
override predicate attributesUnknown() { none() }
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
none()
}
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
// TO DO .. Result should (in most cases) be an instance
none()
}
@@ -166,8 +174,11 @@ class UnknownClassInternal extends ClassObjectInternal, TUnknownClass {
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() and
callee_for_object(callee, this)
none()
}
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown()
}
override ControlFlowNode getOrigin() {

View File

@@ -35,6 +35,11 @@ abstract class BooleanObjectInternal extends ObjectInternal {
none()
}
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
// Booleans aren't callable
none()
}
override ControlFlowNode getOrigin() {
none()
}
@@ -54,7 +59,7 @@ abstract class BooleanObjectInternal extends ObjectInternal {
class TrueObjectInternal extends BooleanObjectInternal, TTrue {
override string toString() {
result = "True"
result = "bool True"
}
override boolean booleanValue() {
@@ -78,7 +83,7 @@ class TrueObjectInternal extends BooleanObjectInternal, TTrue {
class FalseObjectInternal extends BooleanObjectInternal, TFalse {
override string toString() {
result = "False"
result = "bool False"
}
override boolean booleanValue() {
@@ -134,7 +139,12 @@ class NoneObjectInternal extends ObjectInternal, TNone {
// None isn't callable
none()
}
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
// None isn't callable
none()
}
override ControlFlowNode getOrigin() {
none()
}
@@ -163,7 +173,7 @@ class NoneObjectInternal extends ObjectInternal, TNone {
class IntObjectInternal extends ObjectInternal, TInt {
override string toString() {
result = this.intValue().toString()
result = "int " + this.intValue().toString()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
@@ -193,6 +203,11 @@ class IntObjectInternal extends ObjectInternal, TInt {
none()
}
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
// ints aren't callable
none()
}
override ControlFlowNode getOrigin() {
none()
}
@@ -256,6 +271,11 @@ class StringObjectInternal extends ObjectInternal, TString {
none()
}
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
// strings aren't callable
none()
}
override ControlFlowNode getOrigin() {
none()
}

View File

@@ -10,7 +10,7 @@ private import semmle.python.types.Builtins
class SpecificInstanceInternal extends TSpecificInstance, ObjectInternal {
override string toString() {
result = this.getOrigin().toString()
result = this.getOrigin().getNode().toString()
}
/** The boolean value of this object, if it has one */
@@ -53,16 +53,17 @@ class SpecificInstanceInternal extends TSpecificInstance, ObjectInternal {
this = TSpecificInstance(result, _, _)
}
/** Holds if `obj` is the result of calling `this` and `origin` is
* the origin of `obj`.
*/
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
none()
}
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
// In general instances aren't callable, but some are...
// TO DO -- Handle cases where class overrides __call__
none()
}
override int intValue() {
override int intValue() {
none()
}
@@ -96,7 +97,9 @@ class UnknownInstanceInternal extends TUnknownInstance, ObjectInternal {
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
none()
context.appliesTo(node) and
this.getClass() = ObjectInternal::builtin("float") and
node.getNode() instanceof FloatLiteral
}
/** Gets the class declaration for this object, if it is a declared class. */
@@ -129,10 +132,11 @@ class UnknownInstanceInternal extends TUnknownInstance, ObjectInternal {
none()
}
/** Holds if `obj` is the result of calling `this` and `origin` is
* the origin of `obj`.
*/
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
none()
}
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
// In general instances aren't callable, but some are...
// TO DO -- Handle cases where class overrides __call__
none()
@@ -157,3 +161,61 @@ class UnknownInstanceInternal extends TUnknownInstance, ObjectInternal {
override predicate attributesUnknown() { any() }
}
class SuperInstance extends TSuperInstance, ObjectInternal {
override string toString() {
result = "super()"
}
override boolean booleanValue() { result = true }
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
exists(ObjectInternal self, ClassObjectInternal startclass |
super_instantiation(node, self, startclass, context) and
this = TSuperInstance(self, startclass)
)
}
ClassObjectInternal getStartClass() {
this = TSuperInstance(_, result)
}
ObjectInternal getSelf() {
this = TSuperInstance(result, _)
}
override ClassDecl getClassDeclaration() { none() }
override boolean isClass() { result = false }
override ObjectInternal getClass() { none() }
override boolean isComparable() { none() }
override Builtin getBuiltin() { none() }
override ControlFlowNode getOrigin() {
none()
}
override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() }
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) { none() }
override int intValue() { none() }
override string strValue() { none() }
override predicate calleeAndOffset(Function scope, int paramOffset) { none() }
override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() }
override predicate attributesUnknown() { none() }
}

View File

@@ -17,6 +17,11 @@ abstract class ModuleObjectInternal extends ObjectInternal {
none()
}
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
// Modules aren't callable
none()
}
override ControlFlowNode getOrigin() {
result = this.getSourceModule().getEntryNode()
}
@@ -38,7 +43,7 @@ class BuiltinModuleObjectInternal extends ModuleObjectInternal, TBuiltinModuleOb
}
override string toString() {
result = "builtin module " + this.getBuiltin().getName()
result = "Module " + this.getBuiltin().getName()
}
override string getName() {
@@ -93,7 +98,7 @@ class PackageObjectInternal extends ModuleObjectInternal, TPackageObject {
}
override string toString() {
result = "package " + this.getName()
result = "Package " + this.getName()
}
override string getName() {
@@ -186,7 +191,7 @@ class PythonModuleObjectInternal extends ModuleObjectInternal, TPythonModule {
}
override string toString() {
result = "package " + this.getName()
result = "Module " + this.getName()
}
override string getName() {

View File

@@ -50,6 +50,11 @@ class ObjectInternal extends TObject {
/** Holds if `obj` is the result of calling `this` and `origin` is
* the origin of `obj`.
*/
abstract predicate callResult(ObjectInternal obj, CfgOrigin origin);
/** Holds if `obj` is the result of calling `this` and `origin` is
* the origin of `obj` with callee context `callee`.
*/
abstract predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin);
/** The integer value of things that have integer values.
@@ -107,8 +112,11 @@ class BuiltinOpaqueObjectInternal extends ObjectInternal, TBuiltinOpaqueObject {
override boolean isComparable() { result = false }
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() and
callee_for_object(callee, this)
none()
}
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown()
}
override ControlFlowNode getOrigin() {
@@ -167,9 +175,12 @@ class UnknownInternal extends ObjectInternal, TUnknown {
none()
}
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() and
callee_for_object(callee, this)
none()
}
override ControlFlowNode getOrigin() {
@@ -227,10 +238,13 @@ class UndefinedInternal extends ObjectInternal, TUndefined {
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
none()
}
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
// Accessing an undefined value raises a NameError, but if during import it probably
// means that we missed an import.
obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() and
callee.getOuter().isImport()
obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown()
}
override ControlFlowNode getOrigin() {
@@ -311,6 +325,15 @@ module ObjectInternal {
or
result = TBuiltinModuleObject(b)
}
ObjectInternal classMethod() {
result = TBuiltinClassObject(Builtin::special("ClassMethod"))
}
ObjectInternal staticMethod() {
result = TBuiltinClassObject(Builtin::special("StaticMethod"))
}
}
/** Helper for boolean predicates returning both `true` and `false` */

View File

@@ -72,26 +72,133 @@ newtype TObject =
s = "__main__"
}
or
TSpecificInstance(CallNode instantiation, ClassObjectInternal cls, PointsToContext2 context) {
PointsTo2::points_to(instantiation.getFunction(), context, cls, _) and
TSpecificInstance(ControlFlowNode instantiation, ClassObjectInternal cls, PointsToContext2 context) {
PointsTo2::points_to(instantiation.(CallNode).getFunction(), context, cls, _) and
cls.isSpecial() = false
or
// Self
self_parameter(instantiation.getNode(), context, cls)
}
or
TBoundMethod(AttrNode instantiation, ObjectInternal self, CallableObjectInternal function, PointsToContext2 context) {
exists(ControlFlowNode objnode, string name |
objnode = instantiation.getObject(name) and
PointsTo2::points_to(objnode, context, self, _) and
self.getClass().(ClassObjectInternal).attribute(name, function, _)
)
method_binding(instantiation, self, function, context)
}
or
TUnknownInstance(ClassObjectInternal cls) { cls != TUnknownClass() }
or
TSuperInstance(ObjectInternal self, ClassObjectInternal startclass) {
super_instantiation(_, self, startclass, _)
}
private predicate is_power_2(int n) {
n = 1 or
exists(int half | is_power_2(half) and n = half*2)
}
predicate super_instantiation(CallNode instantiation, ObjectInternal self, ClassObjectInternal startclass, PointsToContext2 context) {
PointsTo2::points_to(instantiation.getFunction(), context, ObjectInternal::builtin("super"), _) and
(
PointsTo2::points_to(instantiation.getArg(0), context, startclass, _) and
PointsTo2::points_to(instantiation.getArg(1), context, self, _)
or
major_version() = 3 and
not exists(instantiation.getArg(0)) and
exists(Function func |
instantiation.getScope() = func and
/* Implicit class argument is lexically enclosing scope */
func.getScope() = startclass.(PythonClassObjectInternal).getScope() and
/* Implicit 'self' is the 0th parameter */
PointsTo2::points_to(func.getArg(0).asName().getAFlowNode(), context, self, _)
)
)
}
predicate method_binding(AttrNode instantiation, ObjectInternal self, CallableObjectInternal function, PointsToContext2 context) {
exists(ObjectInternal obj, string name |
receiver(instantiation, context, obj, name) |
exists(ObjectInternal cls |
cls = obj.getClass() and
cls != ObjectInternal::builtin("super") and
cls.attribute(name, function, _) and
self = obj
)
or
exists(SuperInstance sup |
sup = obj and
sup.getStartClass().attribute(name, function, _) and
self = sup.getSelf()
)
)
}
/** Helper for method_binding */
pragma [noinline]
predicate receiver(AttrNode instantiation, PointsToContext2 context, ObjectInternal obj, string name) {
PointsTo2::points_to(instantiation.getObject(name), context, obj, _)
}
/** Helper self parameters: `def meth(self, ...): ...`. */
pragma [noinline]
private predicate self_parameter(Parameter def, PointsToContext2 context, PythonClassObjectInternal cls) {
def.isSelf() and
exists(Function scope |
def.(Name).getScope() = scope and
def.isSelf() and
context.isRuntime() and context.appliesToScope(scope) and
scope.getScope() = cls.getScope() and
concrete_class(cls) and
/* We want to allow decorated functions, otherwise we lose a lot of useful information.
* However, we want to exclude any function whose arguments are permuted by the decorator.
* In general we can't do that, but we can special case the most common ones.
*/
neither_class_nor_static_method(scope)
)
}
/** INTERNAL -- Use `not cls.isAbstract()` instead. */
cached predicate concrete_class(PythonClassObjectInternal cls) {
cls.getClass() != abcMetaClassObject()
or
exists(Class c |
c = cls.getScope() and
not exists(c.getMetaClass())
|
forall(Function f |
f.getScope() = c |
not exists(Raise r, Name ex |
r.getScope() = f and
(r.getException() = ex or r.getException().(Call).getFunc() = ex) and
(ex.getId() = "NotImplementedError" or ex.getId() = "NotImplemented")
)
)
)
}
private PythonClassObjectInternal abcMetaClassObject() {
/* Avoid using points-to and thus negative recursion */
exists(Class abcmeta |
result.getScope() = abcmeta |
abcmeta.getName() = "ABCMeta" and
abcmeta.getScope().getName() = "abc"
)
}
private predicate neither_class_nor_static_method(Function f) {
not exists(f.getADecorator())
or
exists(ControlFlowNode deco |
deco = f.getADecorator().getAFlowNode() |
exists(ObjectInternal o |
PointsTo2::points_to(deco, _, o, _) |
o != ObjectInternal::staticMethod() and
o != ObjectInternal::classMethod()
)
or not deco instanceof NameNode
)
}
library class ClassDecl extends @py_object {
@@ -135,11 +242,3 @@ library class ClassDecl extends @py_object {
}
predicate callee_for_object(PointsToContext2 callee, ObjectInternal obj) {
exists(CallNode call, PointsToContext2 caller |
callee.fromCall(call, caller) and
PointsTo2::points_to(call.getFunction(), caller, obj, _)
)
}

View File

@@ -104,6 +104,9 @@ module PointsTo2 {
predicate points_to_candidate(ControlFlowNode f, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
use_points_to(f, context, value, origin)
or
/* Not necessary, but for backwards compatibility */
def_points_to(f, context, value, origin)
or
attribute_load_points_to(f, context, value, origin)
or
subscript_points_to(f, context, value, origin)
@@ -138,10 +141,7 @@ module PointsTo2 {
guard = b.getImmediatelyControllingBlock() and
reachableBlock(guard, context)
|
exists(ObjectInternal value |
points_to(guard.getLastNode(), context, value, _) and
guard.controls(b, value.booleanValue())
)
allowsFlow(guard, b, context)
or
/* Assume the true edge of an assert is reachable (except for assert 0/False) */
guard.controls(b, true) and
@@ -153,16 +153,30 @@ module PointsTo2 {
)
}
pragma [noopt]
private predicate allowsFlow(ConditionBlock guard, BasicBlock b, PointsToContext2 context) {
exists(ObjectInternal value, boolean sense, ControlFlowNode test |
test = guard.getLastNode() and
points_to(test, context, value, _) and
sense = value.booleanValue() and
guard.controls(b, sense)
)
}
/* Holds if the edge `pred` -> `succ` is reachable, given the context `context`.
*/
pragma [noopt]
predicate controlledReachableEdge(BasicBlock pred, BasicBlock succ, PointsToContext2 context) {
exists(ConditionBlock guard, ObjectInternal value |
points_to(guard.getLastNode(), context, value, _) and
guard.controlsEdge(pred, succ, value.booleanValue())
exists(ConditionBlock guard, ObjectInternal value, boolean sense, ControlFlowNode test |
test = guard.getLastNode() and
points_to(test, context, value, _) and
sense = value.booleanValue() and
guard.controlsEdge(pred, succ, sense)
)
}
/** Gets an object pointed to by a use (of a variable). */
pragma [noinline]
private predicate use_points_to(NameNode f, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
exists(CfgOrigin origin_or_obj |
value != ObjectInternal::undefined() and
@@ -171,6 +185,13 @@ module PointsTo2 {
)
}
/** Gets an object pointed to by the definition of an ESSA variable. */
pragma [noinline]
private predicate def_points_to(DefinitionNode f, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
points_to(f.getValue(), context, value, origin)
}
pragma [noinline]
private predicate use_points_to_maybe_origin(NameNode f, PointsToContext2 context, ObjectInternal value, CfgOrigin origin_or_obj) {
ssa_variable_points_to(fast_local_variable(f), context, value, origin_or_obj)
or
@@ -181,10 +202,12 @@ module PointsTo2 {
}
/** Holds if `var` refers to `(value, origin)` given the context `context`. */
pragma [noinline]
predicate ssa_variable_points_to(EssaVariable var, PointsToContext2 context, ObjectInternal value, CfgOrigin origin) {
ssa_definition_points_to(var.getDefinition(), context, value, origin)
}
pragma [noinline]
private predicate name_lookup_points_to_maybe_origin(NameNode f, PointsToContext2 context, ObjectInternal value, CfgOrigin origin_or_obj) {
exists(EssaVariable var | var = name_local_variable(f) |
ssa_variable_points_to(var, context, value, origin_or_obj)
@@ -199,6 +222,7 @@ module PointsTo2 {
ssa_variable_points_to(name_local_variable(f), context, ObjectInternal::undefined(), _)
}
pragma [noinline]
private predicate global_lookup_points_to_maybe_origin(NameNode f, PointsToContext2 context, ObjectInternal value, CfgOrigin origin_or_obj) {
ssa_variable_points_to(global_variable(f), context, value, origin_or_obj)
or
@@ -235,13 +259,17 @@ module PointsTo2 {
}
/** Holds if `f` is an attribute `x.attr` and points to `(value, cls, origin)`. */
pragma [noinline]
private predicate attribute_load_points_to(AttrNode f, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
exists(ObjectInternal object, string name, CfgOrigin orig |
points_to(f.getObject(name), context, object, _) |
object.attribute(name, value, orig) and
origin = orig.fix(f)
or
object.attributesUnknown() and origin = f and value = ObjectInternal::unknown()
)
or
exists(ObjectInternal object |
points_to(f.getObject(), context, object, _) and
origin = f and value = ObjectInternal::unknown()
)
// TO DO -- Support CustomPointsToAttribute
//or
@@ -298,12 +326,12 @@ module PointsTo2 {
private predicate ssa_node_refinement_points_to(EssaNodeRefinement def, PointsToContext2 context, ObjectInternal value, CfgOrigin origin) {
//method_callsite_points_to(def, context, value, origin)
//or
//import_star_points_to(def, context, value, origin)
//or
InterModulePointsTo::import_star_points_to(def, context, value, origin)
or
//attribute_assignment_points_to(def, context, value, origin)
//or
//callsite_points_to(def, context, value, origin)
//or
InterProceduralPointsTo::callsite_points_to(def, context, value, origin)
or
//argument_points_to(def, context, value, origin)
//or
//attribute_delete_points_to(def, context, value, origin)
@@ -427,7 +455,7 @@ module PointsTo2 {
none()
}
pragma [noinline]
private predicate compare_expr_points_to(CompareNode cmp, PointsToContext2 context, ObjectInternal value) {
value = ObjectInternal::bool(Conditionals::comparisonEvaluatesTo(cmp, _, context, _, _))
// or
@@ -447,6 +475,7 @@ module PointsTo2 {
)
}
pragma [noinline]
private predicate unary_points_to(UnaryExprNode f, PointsToContext2 context, ObjectInternal value) {
exists(Unaryop op, ObjectInternal operand |
op = f.getNode().getOp() and
@@ -464,6 +493,7 @@ module PointsTo2 {
module InterModulePointsTo {
pragma [noinline]
predicate import_points_to(ControlFlowNode f, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
exists(string name, ImportExpr i |
i.getAFlowNode() = f and i.getImportedModuleName() = name and
@@ -474,22 +504,13 @@ module InterModulePointsTo {
}
predicate from_import_points_to(ImportMemberNode f, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
exists(string name, ModuleObjectInternal mod, CfgOrigin orig |
PointsTo2::points_to(f.getModule(name), context, mod, _) and
origin = orig.asCfgNodeOrHere(f)
|
// TO DO... $ variables.
//mod.getSourceModule() = f.getEnclosingModule() and
//not exists(EssaVariable var | var.getSourceVariable().getName() = name and var.getAUse() = f) and
//exists(EssaVariable dollar |
// isModuleStateVariable(dollar) and dollar.getAUse() = f and
// SSA::ssa_variable_named_attribute_points_to(dollar, context, name, value, orig)
//)
//or
(mod.getSourceModule() != f.getEnclosingModule() or mod.isBuiltin()) and
mod.attribute(name, value, origin)
)
from_self_import_points_to(f, context, value, origin)
or
from_other_import_points_to(f, context, value, origin)
}
pragma [noinline]
predicate from_self_import_points_to(ImportMemberNode f, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
exists(EssaVariable var, CfgOrigin orig |
var = ssa_variable_for_module_attribute(f, context) and
PointsTo2::ssa_variable_points_to(var, context, value, orig) and
@@ -497,6 +518,28 @@ module InterModulePointsTo {
)
}
pragma [noinline]
predicate from_other_import_points_to(ImportMemberNode f, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
exists(string name, ModuleObjectInternal mod, CfgOrigin orig |
from_import_imports(f, context, mod, name) and
(mod.getSourceModule() != f.getEnclosingModule() or mod.isBuiltin()) and
mod.attribute(name, value, origin) and
origin = orig.asCfgNodeOrHere(f)
// TO DO... $ variables.
//mod.getSourceModule() = f.getEnclosingModule() and
//not exists(EssaVariable var | var.getSourceVariable().getName() = name and var.getAUse() = f) and
//exists(EssaVariable dollar |
// isModuleStateVariable(dollar) and dollar.getAUse() = f and
// SSA::ssa_variable_named_attribute_points_to(dollar, context, name, value, orig)
//)
)
}
private predicate from_import_imports(ImportMemberNode f, PointsToContext2 context, ModuleObjectInternal mod, string name) {
PointsTo2::points_to(f.getModule(name), context, mod, _)
}
pragma [noinline]
private EssaVariable ssa_variable_for_module_attribute(ImportMemberNode f, PointsToContext2 context) {
exists(string name, ModuleObjectInternal mod, Module m |
mod.getSourceModule() = m and m = f.getEnclosingModule() and m = result.getScope() and
@@ -538,16 +581,114 @@ module InterModulePointsTo {
)
}
/** Points-to for `from ... import *`. */
predicate import_star_points_to(ImportStarRefinement def, PointsToContext2 context, ObjectInternal value, CfgOrigin origin) {
exists(CfgOrigin orig |
origin = orig.fix(def.getDefiningNode())
|
exists(ModuleObjectInternal mod, string name |
PointsTo2::points_to(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
PointsTo2::ssa_variable_points_to(var, context, value,orig)
)
)
}
/** Holds if `def` is technically a definition of `var`, but the `from ... import *` does not in fact define `var`. */
cached predicate variable_not_redefined_by_import_star(EssaVariable var, PointsToContext2 context, ImportStarRefinement def) {
var = def.getInput() and
exists(ModuleObjectInternal mod |
PointsTo2::points_to(def.getDefiningNode().(ImportStarNode).getModule(), context, mod, _) |
module_exports_boolean(mod, var.getSourceVariable().getName()) = false
or
exists(Module m, string name |
m = mod.getSourceModule() and name = var.getSourceVariable().getName() |
not m.declaredInAll(_) and name.charAt(0) = "_"
)
)
}
private predicate importsByImportStar(ModuleObjectInternal mod, ModuleObjectInternal imported) {
exists(ImportStarNode isn |
PointsTo2::points_to(isn.getModule(), _, imported, _) and
isn.getScope() = mod.getSourceModule()
)
or exists(ModuleObjectInternal mid |
importsByImportStar(mod, mid) and importsByImportStar(mid, imported)
)
}
private predicate ofInterestInModule(ModuleObjectInternal mod, string name) {
exists(ImportStarNode isn, Module m |
m = mod.getSourceModule() and
isn.getScope() = m and
exists(EssaVariable var | var.getAUse() = isn and var.getName() = name)
)
}
private predicate ofInterestInExports(ModuleObjectInternal mod, string name) {
exists(ModuleObjectInternal importer |
importsByImportStar(importer, mod) and
ofInterestInModule(importer, name)
)
}
private boolean module_exports_boolean(ModuleObjectInternal mod, string name) {
ofInterestInExports(mod, name) and
exists(Module src |
src = mod.getSourceModule()
|
if exists(SsaVariable var | name = var.getId() and var.getAUse() = src.getANormalExit()) then
result = true
else (
exists(ImportStarNode isn, ModuleObjectInternal imported |
isn.getScope() = src and
PointsTo2::points_to(isn.getModule(), _, imported, _) and
result = module_exports_boolean(imported, name)
)
or
not exists(ImportStarNode isn |isn.getScope() = src) and result = false
)
)
or
ofInterestInExports(mod, name) and
exists(Folder folder |
mod.(PackageObjectInternal).hasNoInitModule() and
folder = mod.(PackageObjectInternal).getFolder() |
if (exists(folder.getChildContainer(name)) or exists(folder.getFile(name + ".py"))) then
result = true
else
result = false
)
or
name = "__name__" and result = true
}
}
module InterProceduralPointsTo {
pragma [noinline]
predicate call_points_to(CallNode f, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
exists(ObjectInternal func, PointsToContext2 callee, CfgOrigin resultOrigin |
callee.fromCall(f, context) and
exists(ObjectInternal func, CfgOrigin resultOrigin |
PointsTo2::points_to(f.getFunction(), context, func, _) and
func.callResult(callee, value, resultOrigin) and
origin = resultOrigin.fix(f)
|
exists(PointsToContext2 callee |
callee.fromCall(f, context) and
func.callResult(callee, value, resultOrigin)
)
or
func.callResult(value, resultOrigin)
)
}
@@ -627,12 +768,11 @@ module InterProceduralPointsTo {
/** Model the transfer of values at scope-entry points. Transfer from `(pred_var, pred_context)` to `(succ_def, succ_context)`. */
cached predicate scope_entry_value_transfer(EssaVariable pred_var, PointsToContext2 pred_context, ScopeEntryDefinition succ_def, PointsToContext2 succ_context) {
scope_entry_value_transfer_from_earlier(pred_var, pred_context, succ_def, succ_context)
// TO DO...
//or
//callsite_entry_value_transfer(pred_var, pred_context, succ_def, succ_context)
//or
//pred_context.isImport() and pred_context = succ_context and
//class_entry_value_transfer(pred_var, succ_def)
or
callsite_entry_value_transfer(pred_var, pred_context, succ_def, succ_context)
or
pred_context.isImport() and pred_context = succ_context and
class_entry_value_transfer(pred_var, succ_def)
}
/** Helper for `scope_entry_value_transfer`. Transfer of values from a temporally earlier scope to later scope.
@@ -667,6 +807,59 @@ module InterProceduralPointsTo {
)
}
/** Helper for `scope_entry_value_transfer`.
* Transfer of values from the callsite to the callee, for enclosing variables, but not arguments/parameters. */
pragma [noinline]
private predicate callsite_entry_value_transfer(EssaVariable caller_var, PointsToContext2 caller, ScopeEntryDefinition entry_def, PointsToContext2 callee) {
entry_def.getSourceVariable() = caller_var.getSourceVariable() and
callsite_calls_function(caller_var.getAUse(), caller, entry_def.getScope(), callee, _)
}
/** Helper for `scope_entry_value_transfer`. */
private predicate class_entry_value_transfer(EssaVariable pred_var, ScopeEntryDefinition succ_def) {
exists(ImportTimeScope scope, ControlFlowNode class_def |
class_def = pred_var.getAUse() and
scope.entryEdge(class_def, succ_def.getDefiningNode()) and
pred_var.getSourceVariable() = succ_def.getSourceVariable()
)
}
/** Points-to for a variable (possibly) redefined by a call:
* `var = ...; foo(); use(var)`
* Where var may be redefined in call to `foo` if `var` escapes (is global or non-local).
*/
pragma [noinline]
predicate callsite_points_to(CallsiteRefinement def, PointsToContext2 context, ObjectInternal value, CfgOrigin origin) {
exists(SsaSourceVariable srcvar |
srcvar = def.getSourceVariable() |
if srcvar instanceof EscapingAssignmentGlobalVariable then (
/* If global variable can be reassigned, we need to track it through calls */
exists(EssaVariable var, Function func, PointsToContext2 callee |
callsite_calls_function(def.getCall(), context, func, callee, _) and
var_at_exit(srcvar, func, var) and
PointsTo2::ssa_variable_points_to(var, callee, value, origin)
)
or
exists(ObjectInternal callable |
PointsTo2::points_to(def.getCall().getFunction(), context, callable, _) and
exists(callable.getBuiltin()) and
PointsTo2::ssa_variable_points_to(def.getInput(), context, value, origin)
)
) else (
/* Otherwise we can assume its value (but not those of its attributes or members) has not changed. */
PointsTo2::ssa_variable_points_to(def.getInput(), context, value, origin)
)
)
}
/* Helper for computing ESSA variables at scoepe exit. */
private predicate var_at_exit(Variable var, Scope scope, EssaVariable evar) {
not var instanceof LocalVariable and
evar.getSourceVariable() = var and
evar.getScope() = scope and
BaseFlow::reaches_exit(evar)
}
}
/** Gets the `value, origin` that `f` would refer to if it has not been assigned some other value */