Python points-to: Port old API classes to use new points-to.

This commit is contained in:
Mark Shannon
2019-03-22 14:51:51 +00:00
parent aa30745492
commit dd83149cc3
25 changed files with 632 additions and 3898 deletions

View File

@@ -36,5 +36,6 @@ import semmle.dataflow.SSA
import semmle.python.pointsto.Base
import semmle.python.pointsto.Context
import semmle.python.pointsto.CallGraph
import semmle.python.objects.ObjectAPI
import site

View File

@@ -1,5 +1,5 @@
import python
private import semmle.python.pointsto.PointsTo
private import semmle.python.pointsto.PointsTo2
/** An expression */
class Expr extends Expr_, AstNode {
@@ -78,37 +78,42 @@ class Expr extends Expr_, AstNode {
* NOTE: For complex dataflow, involving multiple stages of points-to analysis, it may be more precise to use
* `ControlFlowNode.refersTo(...)` instead.
*/
predicate refersTo(Object value, ClassObject cls, AstNode origin) {
not py_special_objects(cls, "_semmle_unknown_type")
and
not value = unknownValue()
and
PointsTo::points_to(this.getAFlowNode(), _, value, cls, origin.getAFlowNode())
predicate refersTo(Object obj, ClassObject cls, AstNode origin) {
this.refersTo(_, obj, cls, origin)
}
/** Gets what this expression might "refer-to" in the given `context`.
*/
predicate refersTo(Context context, Object value, ClassObject cls, AstNode origin) {
not py_special_objects(cls, "_semmle_unknown_type")
and
PointsTo::points_to(this.getAFlowNode(), context, value, cls, origin.getAFlowNode())
predicate refersTo(Context context, Object obj, ClassObject cls, AstNode origin) {
exists(Value value, ControlFlowNode cfgorigin |
PointsTo2::points_to(this.getAFlowNode(), context, value, cfgorigin) and
origin.getAFlowNode() = cfgorigin and
cls = value.getClass().getSource() |
if exists(value.getSource()) then
obj = value.getSource()
else
obj = cfgorigin
)
}
/** Whether this expression might "refer-to" to `value` which is from `origin`
* Unlike `this.refersTo(value, _, origin)`, this predicate includes results
* where the class cannot be inferred.
*/
predicate refersTo(Object value, AstNode origin) {
PointsTo::points_to(this.getAFlowNode(), _, value, _, origin.getAFlowNode())
and
not value = unknownValue()
predicate refersTo(Object obj, AstNode origin) {
exists(Value value, ControlFlowNode cfgorigin |
PointsTo2::points_to(this.getAFlowNode(), _, value, cfgorigin) and
origin.getAFlowNode() = cfgorigin and
if exists(value.getSource()) then
obj = value.getSource()
else
obj = cfgorigin
)
}
/** Equivalent to `this.refersTo(value, _)` */
predicate refersTo(Object value) {
PointsTo::points_to(this.getAFlowNode(), _, value, _, _)
and
not value = unknownValue()
predicate refersTo(Object obj) {
this.refersTo(obj, _)
}
}

View File

@@ -1,6 +1,6 @@
import python
import semmle.python.flow.NameNode
private import semmle.python.pointsto.PointsTo
private import semmle.python.pointsto.PointsTo2
/* Note about matching parent and child nodes and CFG splitting:
@@ -212,9 +212,19 @@ class ControlFlowNode extends @py_flow_node {
py_scope_flow(this, _, -1)
}
/** Use ControlFlowNode.refersTo() instead. */
deprecated Object pointsTo() {
this.refersTo(result)
/** The value that this ControlFlowNode points-to. */
predicate pointsTo(Value value) {
this.pointsTo(_, value, _)
}
/** The value and origin that this ControlFlowNode points-to. */
predicate pointsTo(Value value, ControlFlowNode origin) {
this.pointsTo(_, value, origin)
}
/** The value and origin that this ControlFlowNode points-to, given the context. */
predicate pointsTo(Context context, Value value, ControlFlowNode origin) {
PointsTo2::points_to(this, context, value, origin)
}
/** Gets what this flow node might "refer-to". Performs a combination of localized (intra-procedural) points-to
@@ -222,37 +232,40 @@ class ControlFlowNode extends @py_flow_node {
* precise, but may not provide information for a significant number of flow-nodes.
* If the class is unimportant then use `refersTo(value)` or `refersTo(value, origin)` instead.
*/
predicate refersTo(Object value, ClassObject cls, ControlFlowNode origin) {
not py_special_objects(cls, "_semmle_unknown_type")
and
not value = unknownValue()
and
PointsTo::points_to(this, _, value, cls, origin)
predicate refersTo(Object obj, ClassObject cls, ControlFlowNode origin) {
this.refersTo(_, obj, cls, origin)
}
/** Gets what this expression might "refer-to" in the given `context`.
*/
predicate refersTo(Context context, Object value, ClassObject cls, ControlFlowNode origin) {
not py_special_objects(cls, "_semmle_unknown_type")
and
PointsTo::points_to(this, context, value, cls, origin)
predicate refersTo(Context context, Object obj, ClassObject cls, ControlFlowNode origin) {
exists(Value value |
PointsTo2::points_to(this, context, value, origin) and
cls = value.getClass().getSource() |
if exists(value.getSource().(Object)) then
obj = value.getSource()
else
obj = origin
)
}
/** Whether this flow node might "refer-to" to `value` which is from `origin`
* Unlike `this.refersTo(value, _, origin)` this predicate includes results
* where the class cannot be inferred.
*/
predicate refersTo(Object value, ControlFlowNode origin) {
PointsTo::points_to(this, _, value, _, origin)
and
not value = unknownValue()
predicate refersTo(Object obj, ControlFlowNode origin) {
exists(Value value |
PointsTo2::points_to(this, _, value, origin) |
if exists(value.getSource().(Object)) then
obj = value.getSource()
else
obj = origin
)
}
/** Equivalent to `this.refersTo(value, _)` */
predicate refersTo(Object value) {
PointsTo::points_to(this, _, value, _, _)
and
not value = unknownValue()
predicate refersTo(Object obj) {
this.refersTo(obj, _)
}
/** Gets the basic block containing this flow node */

View File

@@ -14,7 +14,7 @@ class Alias extends Alias_ {
private predicate valid_module_name(string name) {
exists(Module m | m.getName() = name)
or
exists(Builtin cmod | cmod.getClass() = theModuleType().asBuiltin() and cmod.getName() = name)
exists(Builtin cmod | cmod.getClass() = Builtin::special("ModuleType") and cmod.getName() = name)
}
/** An artificial expression representing an import */

View File

@@ -1,5 +1,6 @@
import python
private import semmle.python.pointsto.PointsTo
private import semmle.python.objects.ObjectAPI
private import semmle.python.objects.Modules
/** A module. This is the top level element in an AST, corresponding to a source file.
* It is also a Scope; the scope of global variables. */
@@ -66,7 +67,10 @@ class Module extends Module_, Scope, AstNode {
string getAnExport() {
py_exports(this, result)
or
not PointsTo::module_defines_name(this, "__all__") and PointsTo::module_defines_name(this, result)
exists(ModuleValue mod |
mod.getSource() = this.getEntryNode() |
not mod.exports(result)
)
}
/** Gets the source file for this module */

View File

@@ -3,7 +3,6 @@
*/
import python
private import semmle.python.pointsto.PointsTo
private import semmle.python.pointsto.Filters
/** An attribute access where the left hand side of the attribute expression

View File

@@ -4,7 +4,7 @@ import python
private import semmle.python.objects.TObject
private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.PointsTo2
private import semmle.python.pointsto.PointsToContext2
private import semmle.python.pointsto.PointsToContext
private import semmle.python.pointsto.MRO2
private import semmle.python.types.Builtins
@@ -54,7 +54,7 @@ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFuncti
result = this.getScope().toString()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
this = TPythonFunctionObject(node) and context.appliesTo(node)
}
@@ -72,7 +72,7 @@ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFuncti
this = TPythonFunctionObject(result)
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
exists(Function func, ControlFlowNode rval |
func = this.getScope() and
callee.appliesToScope(func) and
@@ -105,7 +105,7 @@ class BuiltinFunctionObjectInternal extends CallableObjectInternal, TBuiltinFunc
result = "Builtin-function " + this.getBuiltin().getName()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
none()
}
@@ -115,7 +115,7 @@ class BuiltinFunctionObjectInternal extends CallableObjectInternal, TBuiltinFunc
override boolean isComparable() { result = true }
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
none()
}
@@ -185,13 +185,13 @@ class BuiltinMethodObjectInternal extends CallableObjectInternal, TBuiltinMethod
result = TBuiltinClassObject(this.getBuiltin().getClass())
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
none()
}
override boolean isComparable() { result = true }
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
none()
}
@@ -236,13 +236,13 @@ class BoundMethodObjectInternal extends CallableObjectInternal, TBoundMethod {
result = TBuiltinClassObject(Builtin::special("MethodType"))
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
this = TBoundMethod(node, _, _, context)
}
override boolean isComparable() { result = false }
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
this.getFunction().callResult(callee, obj, origin)
}
@@ -264,6 +264,103 @@ class BoundMethodObjectInternal extends CallableObjectInternal, TBoundMethod {
}
class ClassMethodObjectInternal extends ObjectInternal, TClassMethod {
override string toString() {
result = "classmethod()"
}
override boolean booleanValue() { result = true }
override predicate introduced(ControlFlowNode node, PointsToContext context) {
exists(CallableObjectInternal function |
this = TClassMethod(node, function) and
class_method(node, function, context)
)
}
CallableObjectInternal getFunction() {
this = TClassMethod(_, result)
}
override ClassDecl getClassDeclaration() { none() }
override boolean isClass() { result = false }
override ObjectInternal getClass() { result = ObjectInternal::builtin("classmethod") }
override boolean isComparable() { none() }
override Builtin getBuiltin() { none() }
override ControlFlowNode getOrigin() { this = TClassMethod(result, _) }
override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() }
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { none() }
override int intValue() { none() }
override string strValue() { none() }
override predicate calleeAndOffset(Function scope, int paramOffset) {
this.getFunction().calleeAndOffset(scope, paramOffset)
}
override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() }
override predicate attributesUnknown() { none() }
}
class StaticMethodObjectInternal extends ObjectInternal, TStaticMethod {
override string toString() {
result = "staticmethod()"
}
override boolean booleanValue() { result = true }
override predicate introduced(ControlFlowNode node, PointsToContext context) {
exists(CallableObjectInternal function |
this = TStaticMethod(node, function) and
static_method(node, function, context)
)
}
CallableObjectInternal getFunction() {
this = TStaticMethod(_, result)
}
override ClassDecl getClassDeclaration() { none() }
override boolean isClass() { result = false }
override ObjectInternal getClass() { result = ObjectInternal::builtin("staticmethod") }
override boolean isComparable() { none() }
override Builtin getBuiltin() { none() }
override ControlFlowNode getOrigin() { this = TStaticMethod(result, _) }
override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() }
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { none() }
override int intValue() { none() }
override string strValue() { none() }
override predicate calleeAndOffset(Function scope, int paramOffset) {
this.getFunction().calleeAndOffset(scope, paramOffset)
}
override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() }
override predicate attributesUnknown() { none() }
}

View File

@@ -4,7 +4,7 @@ import python
private import semmle.python.objects.TObject
private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.PointsTo2
private import semmle.python.pointsto.PointsToContext2
private import semmle.python.pointsto.PointsToContext
private import semmle.python.pointsto.MRO2
private import semmle.python.types.Builtins
@@ -48,7 +48,7 @@ class PythonClassObjectInternal extends ClassObjectInternal, TPythonClassObject
result = "class " + this.getScope().getName()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
this = TPythonClassObject(node) and context.appliesTo(node)
}
@@ -85,7 +85,7 @@ class PythonClassObjectInternal extends ClassObjectInternal, TPythonClassObject
override predicate attributesUnknown() { none() }
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
none()
}
@@ -108,7 +108,7 @@ class BuiltinClassObjectInternal extends ClassObjectInternal, TBuiltinClassObjec
result = "builtin-class " + this.getBuiltin().getName()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
none()
}
@@ -135,7 +135,7 @@ class BuiltinClassObjectInternal extends ClassObjectInternal, TBuiltinClassObjec
override predicate attributesUnknown() { none() }
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
none()
}
@@ -160,10 +160,10 @@ class UnknownClassInternal extends ClassObjectInternal, TUnknownClass {
}
override ObjectInternal getClass() {
result = TUnknownClass()
result = this
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
none()
}
@@ -173,7 +173,7 @@ class UnknownClassInternal extends ClassObjectInternal, TUnknownClass {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
none()
}

View File

@@ -3,7 +3,7 @@ import python
private import semmle.python.objects.TObject
private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.PointsTo2
private import semmle.python.pointsto.PointsToContext2
private import semmle.python.pointsto.PointsToContext
private import semmle.python.types.Builtins
@@ -30,7 +30,7 @@ abstract class BooleanObjectInternal extends ObjectInternal {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
// Booleans aren't callable
none()
}
@@ -66,7 +66,7 @@ class TrueObjectInternal extends BooleanObjectInternal, TTrue {
result = true
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
node.(NameNode).getId() = "True" and context.appliesTo(node)
}
@@ -78,6 +78,10 @@ class TrueObjectInternal extends BooleanObjectInternal, TTrue {
none()
}
override @py_object getSource() {
result = Builtin::special("True")
}
}
class FalseObjectInternal extends BooleanObjectInternal, TFalse {
@@ -90,7 +94,7 @@ class FalseObjectInternal extends BooleanObjectInternal, TFalse {
result = false
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
node.(NameNode).getId() = "False" and context.appliesTo(node)
}
@@ -102,6 +106,10 @@ class FalseObjectInternal extends BooleanObjectInternal, TFalse {
none()
}
override @py_object getSource() {
result = Builtin::special("False")
}
}
class NoneObjectInternal extends ObjectInternal, TNone {
@@ -127,7 +135,7 @@ class NoneObjectInternal extends ObjectInternal, TNone {
result = TBuiltinClassObject(Builtin::special("NoneType"))
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
node.(NameNode).getId() = "None" and context.appliesTo(node)
}
@@ -135,16 +143,16 @@ class NoneObjectInternal extends ObjectInternal, TNone {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
// None isn't callable
none()
}
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
// None isn't callable
none()
}
override ControlFlowNode getOrigin() {
none()
}
@@ -167,6 +175,10 @@ class NoneObjectInternal extends ObjectInternal, TNone {
override predicate attributesUnknown() { none() }
override @py_object getSource() {
result = Builtin::special("None")
}
}
@@ -176,7 +188,7 @@ class IntObjectInternal extends ObjectInternal, TInt {
result = "int " + this.intValue().toString()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
context.appliesTo(node) and
node.getNode().(IntegerLiteral).getValue() = this.intValue()
}
@@ -198,7 +210,7 @@ class IntObjectInternal extends ObjectInternal, TInt {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
// ints aren't callable
none()
}
@@ -236,6 +248,10 @@ class IntObjectInternal extends ObjectInternal, TInt {
override predicate attributesUnknown() { none() }
override @py_object getSource() {
result.(Builtin).intValue() = this.intValue()
}
}
@@ -245,7 +261,7 @@ class StringObjectInternal extends ObjectInternal, TString {
result = "'" + this.strValue() + "'"
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
context.appliesTo(node) and
node.getNode().(StrConst).getText() = this.strValue()
}
@@ -266,7 +282,7 @@ class StringObjectInternal extends ObjectInternal, TString {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
// strings aren't callable
none()
}
@@ -304,6 +320,10 @@ class StringObjectInternal extends ObjectInternal, TString {
override predicate attributesUnknown() { none() }
override @py_object getSource() {
result.(Builtin).strValue() = this.strValue()
}
}

View File

@@ -4,7 +4,7 @@ import python
private import semmle.python.objects.TObject
private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.PointsTo2
private import semmle.python.pointsto.PointsToContext2
private import semmle.python.pointsto.PointsToContext
private import semmle.python.types.Builtins
class SpecificInstanceInternal extends TSpecificInstance, ObjectInternal {
@@ -19,7 +19,7 @@ class SpecificInstanceInternal extends TSpecificInstance, ObjectInternal {
result = maybe()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
this = TSpecificInstance(node, _, context)
}
@@ -53,7 +53,7 @@ class SpecificInstanceInternal extends TSpecificInstance, ObjectInternal {
this = TSpecificInstance(result, _, _)
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
none()
}
@@ -96,7 +96,7 @@ class UnknownInstanceInternal extends TUnknownInstance, ObjectInternal {
result = maybe()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
context.appliesTo(node) and
this.getClass() = ObjectInternal::builtin("float") and
node.getNode() instanceof FloatLiteral
@@ -132,7 +132,7 @@ class UnknownInstanceInternal extends TUnknownInstance, ObjectInternal {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
none()
}
@@ -171,7 +171,7 @@ class SuperInstance extends TSuperInstance, ObjectInternal {
override boolean booleanValue() { result = true }
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
exists(ObjectInternal self, ClassObjectInternal startclass |
super_instantiation(node, self, startclass, context) and
this = TSuperInstance(self, startclass)
@@ -190,7 +190,9 @@ class SuperInstance extends TSuperInstance, ObjectInternal {
override boolean isClass() { result = false }
override ObjectInternal getClass() { none() }
override ObjectInternal getClass() {
result = ObjectInternal::builtin("super")
}
override boolean isComparable() { none() }
@@ -202,7 +204,7 @@ class SuperInstance extends TSuperInstance, ObjectInternal {
override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() }
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) { none() }
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { none() }
override int intValue() { none() }

View File

@@ -3,7 +3,7 @@ import python
private import semmle.python.objects.TObject
private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.PointsTo2
private import semmle.python.pointsto.PointsToContext2
private import semmle.python.pointsto.PointsToContext
private import semmle.python.types.Builtins
abstract class ModuleObjectInternal extends ObjectInternal {
@@ -12,7 +12,7 @@ abstract class ModuleObjectInternal extends ObjectInternal {
abstract Module getSourceModule();
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
// Modules aren't callable
none()
}
@@ -34,6 +34,10 @@ abstract class ModuleObjectInternal extends ObjectInternal {
result = true
}
override ObjectInternal getClass() {
result = ObjectInternal::moduleType()
}
}
class BuiltinModuleObjectInternal extends ModuleObjectInternal, TBuiltinModuleObject {
@@ -50,7 +54,7 @@ class BuiltinModuleObjectInternal extends ModuleObjectInternal, TBuiltinModuleOb
result = this.getBuiltin().getName()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
none()
}
@@ -58,10 +62,6 @@ class BuiltinModuleObjectInternal extends ModuleObjectInternal, TBuiltinModuleOb
none()
}
override ObjectInternal getClass() {
result = TBuiltinClassObject(this.getBuiltin().getClass())
}
override Module getSourceModule() {
none()
}
@@ -93,19 +93,19 @@ class PackageObjectInternal extends ModuleObjectInternal, TPackageObject {
none()
}
Folder getFolder() {
this = TPackageObject(result)
}
override string toString() {
result = "Package " + this.getName()
}
Folder getFolder() {
this = TPackageObject(result)
}
override string getName() {
result = moduleNameFromFile(this.getFolder())
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
none()
}
@@ -113,10 +113,6 @@ class PackageObjectInternal extends ModuleObjectInternal, TPackageObject {
none()
}
override ObjectInternal getClass() {
result = TBuiltinClassObject(this.getBuiltin().getClass())
}
override Module getSourceModule() {
result.getFile() = this.getFolder().getFile("__init__.py")
}
@@ -191,14 +187,14 @@ class PythonModuleObjectInternal extends ModuleObjectInternal, TPythonModule {
}
override string toString() {
result = "Module " + this.getName()
result = this.getSourceModule().toString()
}
override string getName() {
result = this.getSourceModule().getName()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
none()
}
@@ -206,10 +202,6 @@ class PythonModuleObjectInternal extends ModuleObjectInternal, TPythonModule {
none()
}
override ObjectInternal getClass() {
result = TBuiltinClassObject(this.getBuiltin().getClass())
}
override Module getSourceModule() {
this = TPythonModule(result)
}
@@ -231,7 +223,7 @@ class PythonModuleObjectInternal extends ModuleObjectInternal, TPythonModule {
}
override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) {
exists(EssaVariable var, ControlFlowNode exit, PointsToContext2 imp |
exists(EssaVariable var, ControlFlowNode exit, PointsToContext imp |
exit = this.getSourceModule().getANormalExit() and var.getAUse() = exit and
var.getSourceVariable().getName() = name and
PointsTo2::ssa_variable_points_to(var, imp, value, origin) and
@@ -241,7 +233,7 @@ class PythonModuleObjectInternal extends ModuleObjectInternal, TPythonModule {
// TO DO, dollar variable...
//or
//not exists(EssaVariable var | var.getAUse() = m.getANormalExit() and var.getSourceVariable().getName() = name) and
//exists(EssaVariable var, PointsToContext2 imp |
//exists(EssaVariable var, PointsToContext imp |
// var.getAUse() = m.getANormalExit() and isModuleStateVariable(var) |
// PointsTo2::ssa_variable_named_attribute_points_to(var, imp, name, obj, origin) and
// imp.isImport() and obj != ObjectInternal::undefined()

View File

@@ -2,20 +2,28 @@ import python
private import TObject
private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.PointsTo2
private import semmle.python.pointsto.PointsToContext2
private import semmle.python.pointsto.PointsToContext
class ObjectSource = Object;
class Value extends TObject {
Value() {
not this = ObjectInternal::unknown() and
not this = ObjectInternal::unknownClass() and
not this = ObjectInternal::undefined()
}
string toString() {
result = this.(ObjectInternal).toString()
}
ControlFlowNode getAReferent() {
ControlFlowNode getAReference() {
PointsTo2::points_to(result, _, this, _)
}
predicate pointsTo(ControlFlowNode referent, PointsToContext2 context, ControlFlowNode origin) {
PointsTo2::points_to(referent, context, this, origin)
predicate pointsTo(ControlFlowNode node, PointsToContext context, ControlFlowNode origin) {
PointsTo2::points_to(node, context, this, origin)
}
Value getClass() {
@@ -35,4 +43,71 @@ class Value extends TObject {
this.(ObjectInternal).attribute(name, result, _)
}
/* For backwards compatibility with old API */
ObjectSource getSource() {
result = this.(ObjectInternal).getSource()
}
/** Gets the `ControlFlowNode` that will be passed as the nth argument to `this` when called at `call`.
This predicate will correctly handle `x.y()`, treating `x` as the zeroth argument.
*/
ControlFlowNode getArgumentForCall(CallNode call, int n) {
// TO DO..
none()
}
/** Gets the `ControlFlowNode` that will be passed as the named argument to `this` when called at `call`.
This predicate will correctly handle `x.y()`, treating `x` as the self argument.
*/
ControlFlowNode getNamedArgumentForCall(CallNode call, string name) {
// TO DO..
none()
}
}
class ModuleValue extends Value {
ModuleValue() {
this instanceof ModuleObjectInternal
}
predicate exports(string name) {
not this.(ModuleObjectInternal).attribute("__all__", _, _) and this.(ModuleObjectInternal).attribute(name, _, _)
}
string getName() {
result = this.(ModuleObjectInternal).getName()
}
}
module Module {
ModuleValue named(string name) {
result.getName() = name
}
}
class CallableValue extends Value {
CallableValue() {
this instanceof CallableObjectInternal
}
predicate neverReturns() {
// TO DO..
none()
}
}
class ClassValue extends Value {
ClassValue() {
this.(ObjectInternal).isClass() = true
}
}

View File

@@ -2,7 +2,7 @@ import python
private import semmle.python.objects.TObject
private import semmle.python.pointsto.PointsTo2
private import semmle.python.pointsto.PointsToContext2
private import semmle.python.pointsto.PointsToContext
private import semmle.python.types.Builtins
import semmle.python.objects.Modules
import semmle.python.objects.Classes
@@ -18,7 +18,7 @@ class ObjectInternal extends TObject {
* true and false if the "object" represents a set of possible objects. */
abstract boolean booleanValue();
abstract predicate introduced(ControlFlowNode node, PointsToContext2 context);
abstract predicate introduced(ControlFlowNode node, PointsToContext context);
/** Gets the class declaration for this object, if it is a declared class. */
abstract ClassDecl getClassDeclaration();
@@ -35,15 +35,13 @@ class ObjectInternal extends TObject {
abstract boolean isComparable();
/** Gets the `Builtin` for this object, if any.
* All objects (except unknown and undefined values) should return
* Objects (except unknown and undefined values) should attempt to return
* exactly one result for either this method or `getOrigin()`.
*/
abstract Builtin getBuiltin();
/** Gets a control flow node that represents the source origin of this
/** Gets a control flow node that represents the source origin of this
* objects.
* All objects (except unknown and undefined values) should return
* exactly one result for either this method or `getBuiltin()`.
*/
abstract ControlFlowNode getOrigin();
@@ -55,7 +53,7 @@ class ObjectInternal extends TObject {
/** 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);
abstract predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin);
/** The integer value of things that have integer values.
* That is, ints and bools.
@@ -77,6 +75,16 @@ class ObjectInternal extends TObject {
abstract predicate attributesUnknown();
/** For backwards compatibility shim -- Not all objects have a "source"
* Objects (except unknown and undefined values) should attempt to return
* exactly one result for either this method`.
* */
@py_object getSource() {
result = this.getOrigin()
or
result = this.getBuiltin()
}
}
@@ -87,7 +95,7 @@ class BuiltinOpaqueObjectInternal extends ObjectInternal, TBuiltinOpaqueObject {
}
override string toString() {
result = this.getBuiltin().toString()
result = this.getBuiltin().getClass().getName() + " object"
}
override boolean booleanValue() {
@@ -105,13 +113,13 @@ class BuiltinOpaqueObjectInternal extends ObjectInternal, TBuiltinOpaqueObject {
result = TBuiltinClassObject(this.getBuiltin().getClass())
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
none()
}
override boolean isComparable() { result = false }
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
none()
}
@@ -165,7 +173,7 @@ class UnknownInternal extends ObjectInternal, TUnknown {
result = TUnknownClass()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
none()
}
@@ -179,7 +187,7 @@ class UnknownInternal extends ObjectInternal, TUnknown {
obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
none()
}
@@ -229,7 +237,7 @@ class UndefinedInternal extends ObjectInternal, TUndefined {
none()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
none()
}
@@ -237,7 +245,7 @@ class UndefinedInternal extends ObjectInternal, TUndefined {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
none()
}
@@ -334,6 +342,9 @@ module ObjectInternal {
result = TBuiltinClassObject(Builtin::special("StaticMethod"))
}
ObjectInternal moduleType() {
result = TBuiltinClassObject(Builtin::special("ModuleType"))
}
}
/** Helper for boolean predicates returning both `true` and `false` */

View File

@@ -2,7 +2,7 @@ import python
private import semmle.python.types.Builtins
private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.PointsTo2
private import semmle.python.pointsto.PointsToContext2
private import semmle.python.pointsto.PointsToContext
newtype TObject =
TBuiltinClassObject(Builtin bltn) {
@@ -31,7 +31,9 @@ newtype TObject =
classexpr.getNode() instanceof ClassExpr
}
or
TPackageObject(Folder f)
TPackageObject(Folder f) {
exists(moduleNameFromFile(f))
}
or
TPythonModule(Module m) { not m.isPackage() }
or
@@ -72,7 +74,7 @@ newtype TObject =
s = "__main__"
}
or
TSpecificInstance(ControlFlowNode instantiation, ClassObjectInternal cls, PointsToContext2 context) {
TSpecificInstance(ControlFlowNode instantiation, ClassObjectInternal cls, PointsToContext context) {
PointsTo2::points_to(instantiation.(CallNode).getFunction(), context, cls, _) and
cls.isSpecial() = false
or
@@ -80,22 +82,40 @@ newtype TObject =
self_parameter(instantiation.getNode(), context, cls)
}
or
TBoundMethod(AttrNode instantiation, ObjectInternal self, CallableObjectInternal function, PointsToContext2 context) {
TBoundMethod(AttrNode instantiation, ObjectInternal self, CallableObjectInternal function, PointsToContext context) {
method_binding(instantiation, self, function, context)
}
or
TUnknownInstance(ClassObjectInternal cls) { cls != TUnknownClass() }
TUnknownInstance(BuiltinClassObjectInternal cls) { any() }
or
TSuperInstance(ObjectInternal self, ClassObjectInternal startclass) {
super_instantiation(_, self, startclass, _)
}
or
TClassMethod(CallNode instantiation, CallableObjectInternal function) {
class_method(instantiation, function, _)
}
or
TStaticMethod(CallNode instantiation, CallableObjectInternal function) {
static_method(instantiation, function, _)
}
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) {
predicate static_method(CallNode instantiation, CallableObjectInternal function, PointsToContext context) {
PointsTo2::points_to(instantiation.getFunction(), context, ObjectInternal::builtin("staticmethod"), _) and
PointsTo2::points_to(instantiation.getArg(0), context, function, _)
}
predicate class_method(CallNode instantiation, CallableObjectInternal function, PointsToContext context) {
PointsTo2::points_to(instantiation.getFunction(), context, ObjectInternal::builtin("classmethod"), _) and
PointsTo2::points_to(instantiation.getArg(0), context, function, _)
}
predicate super_instantiation(CallNode instantiation, ObjectInternal self, ClassObjectInternal startclass, PointsToContext context) {
PointsTo2::points_to(instantiation.getFunction(), context, ObjectInternal::builtin("super"), _) and
(
PointsTo2::points_to(instantiation.getArg(0), context, startclass, _) and
@@ -113,7 +133,7 @@ predicate super_instantiation(CallNode instantiation, ObjectInternal self, Class
)
}
predicate method_binding(AttrNode instantiation, ObjectInternal self, CallableObjectInternal function, PointsToContext2 context) {
predicate method_binding(AttrNode instantiation, ObjectInternal self, CallableObjectInternal function, PointsToContext context) {
exists(ObjectInternal obj, string name |
receiver(instantiation, context, obj, name) |
exists(ObjectInternal cls |
@@ -134,13 +154,13 @@ predicate method_binding(AttrNode instantiation, ObjectInternal self, CallableOb
/** Helper for method_binding */
pragma [noinline]
predicate receiver(AttrNode instantiation, PointsToContext2 context, ObjectInternal obj, string name) {
predicate receiver(AttrNode instantiation, PointsToContext 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) {
private predicate self_parameter(Parameter def, PointsToContext context, PythonClassObjectInternal cls) {
def.isSelf() and
exists(Function scope |
def.(Name).getScope() = scope and

View File

@@ -21,7 +21,7 @@ import python
private import semmle.python.objects.TObject
private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.PointsTo2
private import semmle.python.pointsto.PointsToContext2
private import semmle.python.pointsto.PointsToContext
private import semmle.python.types.Builtins
@@ -99,6 +99,10 @@ class ClassList extends TClassList {
result = this.getTail().getItem(n-1)
}
ClassObjectInternal getAnItem() {
result = this.getItem(_)
}
pragma [inline]
ClassList removeHead(ClassObjectInternal cls) {
this.getHead() = cls and result = this.getTail()

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@ import python
private import semmle.python.objects.TObject
private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.Filters
private import semmle.python.pointsto.PointsToContext2
private import semmle.python.pointsto.PointsToContext
private import semmle.python.pointsto.MRO2
private import semmle.python.types.Builtins
@@ -31,7 +31,7 @@ library class CfgOrigin extends @py_object {
pragma[inline]
CfgOrigin fix(ControlFlowNode here) {
if this = unknownValue() then
if this = Builtin::unknown() then
result = here
else
result = this
@@ -82,26 +82,26 @@ module CfgOrigin {
}
CfgOrigin unknown() {
result = unknownValue()
result = Builtin::unknown()
}
CfgOrigin fromModule(ModuleObjectInternal mod) {
mod.isBuiltin() and result = unknownValue()
mod.isBuiltin() and result = unknown()
or
result = mod.getSourceModule().getEntryNode()
}
}
module PointsTo2 {
cached module PointsTo2 {
/** INTERNAL -- Use `f.refersTo(value, origin)` instead. */
predicate points_to(ControlFlowNode f, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
cached predicate points_to(ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
points_to_candidate(f, context, value, origin) and
reachableBlock(f.getBasicBlock(), context)
}
predicate points_to_candidate(ControlFlowNode f, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
private predicate points_to_candidate(ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
use_points_to(f, context, value, origin)
or
/* Not necessary, but for backwards compatibility */
@@ -129,12 +129,12 @@ module PointsTo2 {
// f.(CustomPointsToFact).pointsTo(context, value, origin)
}
cached CallNode get_a_call(ObjectInternal func, PointsToContext2 context) {
cached CallNode get_a_call(ObjectInternal func, PointsToContext context) {
points_to(result.getFunction(), context, func, _)
}
/* Holds if BasicBlock `b` is reachable, given the context `context`. */
predicate reachableBlock(BasicBlock b, PointsToContext2 context) {
cached predicate reachableBlock(BasicBlock b, PointsToContext context) {
context.appliesToScope(b.getScope()) and not exists(ConditionBlock guard | guard.controls(b, _))
or
exists(ConditionBlock guard |
@@ -154,7 +154,7 @@ module PointsTo2 {
}
pragma [noopt]
private predicate allowsFlow(ConditionBlock guard, BasicBlock b, PointsToContext2 context) {
private predicate allowsFlow(ConditionBlock guard, BasicBlock b, PointsToContext context) {
exists(ObjectInternal value, boolean sense, ControlFlowNode test |
test = guard.getLastNode() and
points_to(test, context, value, _) and
@@ -166,7 +166,7 @@ module PointsTo2 {
/* Holds if the edge `pred` -> `succ` is reachable, given the context `context`.
*/
pragma [noopt]
predicate controlledReachableEdge(BasicBlock pred, BasicBlock succ, PointsToContext2 context) {
private predicate controlledReachableEdge(BasicBlock pred, BasicBlock succ, PointsToContext context) {
exists(ConditionBlock guard, ObjectInternal value, boolean sense, ControlFlowNode test |
test = guard.getLastNode() and
points_to(test, context, value, _) and
@@ -177,7 +177,7 @@ module PointsTo2 {
/** 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) {
private predicate use_points_to(NameNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
exists(CfgOrigin origin_or_obj |
value != ObjectInternal::undefined() and
use_points_to_maybe_origin(f, context, value, origin_or_obj) |
@@ -187,12 +187,12 @@ 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) {
private predicate def_points_to(DefinitionNode f, PointsToContext 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) {
private predicate use_points_to_maybe_origin(NameNode f, PointsToContext context, ObjectInternal value, CfgOrigin origin_or_obj) {
ssa_variable_points_to(fast_local_variable(f), context, value, origin_or_obj)
or
name_lookup_points_to_maybe_origin(f, context, value, origin_or_obj)
@@ -203,12 +203,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) {
cached predicate ssa_variable_points_to(EssaVariable var, PointsToContext 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) {
private predicate name_lookup_points_to_maybe_origin(NameNode f, PointsToContext 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)
)
@@ -218,12 +218,12 @@ module PointsTo2 {
}
pragma [noinline]
private predicate local_variable_undefined(NameNode f, PointsToContext2 context) {
private predicate local_variable_undefined(NameNode f, PointsToContext context) {
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) {
private predicate global_lookup_points_to_maybe_origin(NameNode f, PointsToContext context, ObjectInternal value, CfgOrigin origin_or_obj) {
ssa_variable_points_to(global_variable(f), context, value, origin_or_obj)
or
exists(ControlFlowNode origin |
@@ -260,7 +260,7 @@ 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) {
private predicate attribute_load_points_to(AttrNode f, PointsToContext 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
@@ -280,7 +280,7 @@ module PointsTo2 {
}
/** Holds if the ESSA definition `def` refers to `(value, origin)` given the context `context`. */
predicate ssa_definition_points_to(EssaDefinition def, PointsToContext2 context, ObjectInternal value, CfgOrigin origin) {
private predicate ssa_definition_points_to(EssaDefinition def, PointsToContext context, ObjectInternal value, CfgOrigin origin) {
ssa_phi_points_to(def, context, value, origin)
or
exists(ControlFlowNode orig |
@@ -294,13 +294,13 @@ module PointsTo2 {
}
pragma [noinline]
private predicate ssa_node_definition_points_to(EssaNodeDefinition def, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
private predicate ssa_node_definition_points_to(EssaNodeDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
reachableBlock(def.getDefiningNode().getBasicBlock(), _) and
ssa_node_definition_points_to_unpruned(def, context, value, origin)
}
pragma [nomagic]
private predicate ssa_node_definition_points_to_unpruned(EssaNodeDefinition def, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
private predicate ssa_node_definition_points_to_unpruned(EssaNodeDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
InterProceduralPointsTo::parameter_points_to(def, context, value, origin)
or
assignment_points_to(def, context, value, origin)
@@ -323,7 +323,7 @@ module PointsTo2 {
}
pragma [noinline]
private predicate ssa_node_refinement_points_to(EssaNodeRefinement def, PointsToContext2 context, ObjectInternal value, CfgOrigin origin) {
private predicate ssa_node_refinement_points_to(EssaNodeRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin) {
//method_callsite_points_to(def, context, value, origin)
//or
InterModulePointsTo::import_star_points_to(def, context, value, origin)
@@ -332,15 +332,20 @@ module PointsTo2 {
//or
InterProceduralPointsTo::callsite_points_to(def, context, value, origin)
or
//argument_points_to(def, context, value, origin)
argument_points_to(def, context, value, origin)
//or
//attribute_delete_points_to(def, context, value, origin)
//or
or
uni_edged_phi_points_to(def, context, value, origin)
}
/** Ignore the effects of calls on their arguments. PointsTo is an approximation, but attempting to improve accuracy would be very expensive for very little gain. */
private predicate argument_points_to(ArgumentRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin) {
ssa_variable_points_to(def.getInput(), context, value, origin)
}
/** Holds if ESSA edge refinement, `def`, refers to `(value, cls, origin)`. */
predicate ssa_filter_definition_points_to(PyEdgeRefinement def, PointsToContext2 context, ObjectInternal value, CfgOrigin origin) {
private predicate ssa_filter_definition_points_to(PyEdgeRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin) {
exists(ControlFlowNode test, ControlFlowNode use |
refinement_test(test, use, Conditionals::branchEvaluatesTo(test, use, context, value, origin.toCfgNode()), def)
)
@@ -348,7 +353,7 @@ module PointsTo2 {
/** Holds if ESSA definition, `uniphi`, refers to `(value, origin)`. */
pragma [noinline]
predicate uni_edged_phi_points_to(SingleSuccessorGuard uniphi, PointsToContext2 context, ObjectInternal value, CfgOrigin origin) {
private predicate uni_edged_phi_points_to(SingleSuccessorGuard uniphi, PointsToContext context, ObjectInternal value, CfgOrigin origin) {
exists(ControlFlowNode test, ControlFlowNode use |
/* 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.
@@ -361,19 +366,19 @@ module PointsTo2 {
/** Points-to for normal assignments `def = ...`. */
pragma [noinline]
private predicate assignment_points_to(AssignmentDefinition def, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
private predicate assignment_points_to(AssignmentDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
points_to(def.getValue(), context, value, origin)
}
/** Points-to for deletion: `del name`. */
pragma [noinline]
private predicate delete_points_to(DeletionDefinition def, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
private predicate delete_points_to(DeletionDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
value = ObjectInternal::undefined() and origin = def.getDefiningNode() and context.appliesToScope(def.getScope())
}
/** Implicit "definition" of `__name__` at the start of a module. */
pragma [noinline]
private predicate module_name_points_to(ScopeEntryDefinition def, PointsToContext2 context, StringObjectInternal value, ControlFlowNode origin) {
private predicate module_name_points_to(ScopeEntryDefinition def, PointsToContext context, StringObjectInternal value, ControlFlowNode origin) {
def.getVariable().getName() = "__name__" and
exists(Module m |
m = def.getScope()
@@ -397,7 +402,7 @@ module PointsTo2 {
/** Holds if the phi-function `phi` refers to `(value, origin)` given the context `context`. */
pragma [nomagic]
private predicate ssa_phi_points_to(PhiFunction phi, PointsToContext2 context, ObjectInternal value, CfgOrigin origin) {
private predicate ssa_phi_points_to(PhiFunction phi, PointsToContext context, ObjectInternal value, CfgOrigin origin) {
exists(EssaVariable input, BasicBlock pred |
input = phi.getInput(pred) and
ssa_variable_points_to(input, context, value, origin)
@@ -412,9 +417,9 @@ module PointsTo2 {
/** Points-to for implicit variable declarations at scope-entry. */
pragma [noinline]
private predicate scope_entry_points_to(ScopeEntryDefinition def, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
private predicate scope_entry_points_to(ScopeEntryDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
/* Transfer from another scope */
exists(EssaVariable var, PointsToContext2 outer, CfgOrigin orig |
exists(EssaVariable var, PointsToContext outer, CfgOrigin orig |
InterProceduralPointsTo::scope_entry_value_transfer(var, outer, def, context) and
ssa_variable_points_to(var, outer, value, orig) and
origin = orig.asCfgNodeOrHere(def.getDefiningNode())
@@ -441,7 +446,7 @@ module PointsTo2 {
)
}
private predicate subscript_points_to(SubscriptNode sub, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
private predicate subscript_points_to(SubscriptNode sub, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
points_to(sub.getValue(), context, ObjectInternal::unknown(), _) and
value = ObjectInternal::unknown() and origin = sub
}
@@ -449,34 +454,21 @@ module PointsTo2 {
/** Track bitwise expressions so we can handle integer flags and enums.
* Tracking too many binary expressions is likely to kill performance.
*/
private predicate binary_expr_points_to(BinaryExprNode b, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
private predicate binary_expr_points_to(BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
// TO DO...
// Track some integer values through `|` and the types of some objects
none()
}
pragma [noinline]
private predicate compare_expr_points_to(CompareNode cmp, PointsToContext2 context, ObjectInternal value) {
private predicate compare_expr_points_to(CompareNode cmp, PointsToContext context, ObjectInternal value) {
value = ObjectInternal::bool(Conditionals::comparisonEvaluatesTo(cmp, _, context, _, _))
// or
// value = version_tuple_compare(cmp, context)
}
/** Helper for comparisons. */
predicate inequality(CompareNode cmp, ControlFlowNode lesser, ControlFlowNode greater, boolean strict) {
exists(Cmpop op |
cmp.operands(lesser, op, greater) and op.getSymbol() = "<" and strict = true
or
cmp.operands(lesser, op, greater) and op.getSymbol() = "<=" and strict = false
or
cmp.operands(greater, op, lesser) and op.getSymbol() = ">" and strict = true
or
cmp.operands(greater, op, lesser) and op.getSymbol() = ">=" and strict = false
)
}
pragma [noinline]
private predicate unary_points_to(UnaryExprNode f, PointsToContext2 context, ObjectInternal value) {
private predicate unary_points_to(UnaryExprNode f, PointsToContext context, ObjectInternal value) {
exists(Unaryop op, ObjectInternal operand |
op = f.getNode().getOp() and
points_to(f.getOperand(), context, operand, _)
@@ -494,7 +486,7 @@ module PointsTo2 {
module InterModulePointsTo {
pragma [noinline]
predicate import_points_to(ControlFlowNode f, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
predicate import_points_to(ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
exists(string name, ImportExpr i |
i.getAFlowNode() = f and i.getImportedModuleName() = name and
module_imported_as(value, name) and
@@ -503,14 +495,14 @@ module InterModulePointsTo {
)
}
predicate from_import_points_to(ImportMemberNode f, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
predicate from_import_points_to(ImportMemberNode f, PointsToContext context, ObjectInternal value, ControlFlowNode 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) {
predicate from_self_import_points_to(ImportMemberNode f, PointsToContext 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
@@ -519,7 +511,7 @@ module InterModulePointsTo {
}
pragma [noinline]
predicate from_other_import_points_to(ImportMemberNode f, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
predicate from_other_import_points_to(ImportMemberNode f, PointsToContext 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
@@ -535,12 +527,12 @@ module InterModulePointsTo {
)
}
private predicate from_import_imports(ImportMemberNode f, PointsToContext2 context, ModuleObjectInternal mod, string name) {
private predicate from_import_imports(ImportMemberNode f, PointsToContext 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) {
private EssaVariable ssa_variable_for_module_attribute(ImportMemberNode f, PointsToContext context) {
exists(string name, ModuleObjectInternal mod, Module m |
mod.getSourceModule() = m and m = f.getEnclosingModule() and m = result.getScope() and
PointsTo2::points_to(f.getModule(name), context, mod, _) and
@@ -549,7 +541,7 @@ module InterModulePointsTo {
}
/* Holds if `import name` will import the module `m`. */
private predicate module_imported_as(ModuleObjectInternal m, string name) {
predicate module_imported_as(ModuleObjectInternal m, string name) {
/* Normal imports */
m.getName() = name
or
@@ -572,7 +564,7 @@ module InterModulePointsTo {
* PointsTo isn't exactly how the interpreter works, but is the best approximation we can manage statically.
*/
pragma [noinline]
predicate implicit_submodule_points_to(ImplicitSubModuleDefinition def, PointsToContext2 context, ModuleObjectInternal value, ControlFlowNode origin) {
predicate implicit_submodule_points_to(ImplicitSubModuleDefinition def, PointsToContext context, ModuleObjectInternal value, ControlFlowNode origin) {
exists(PackageObjectInternal package |
package.getSourceModule() = def.getDefiningNode().getScope() |
value = package.submodule(def.getSourceVariable().getName()) and
@@ -582,7 +574,7 @@ module InterModulePointsTo {
}
/** Points-to for `from ... import *`. */
predicate import_star_points_to(ImportStarRefinement def, PointsToContext2 context, ObjectInternal value, CfgOrigin origin) {
predicate import_star_points_to(ImportStarRefinement def, PointsToContext context, ObjectInternal value, CfgOrigin origin) {
exists(CfgOrigin orig |
origin = orig.fix(def.getDefiningNode())
|
@@ -604,7 +596,7 @@ module InterModulePointsTo {
/** 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) {
cached predicate variable_not_redefined_by_import_star(EssaVariable var, PointsToContext context, ImportStarRefinement def) {
var = def.getInput() and
exists(ModuleObjectInternal mod |
PointsTo2::points_to(def.getDefiningNode().(ImportStarNode).getModule(), context, mod, _) |
@@ -678,12 +670,12 @@ module InterModulePointsTo {
module InterProceduralPointsTo {
pragma [noinline]
predicate call_points_to(CallNode f, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
predicate call_points_to(CallNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
exists(ObjectInternal func, CfgOrigin resultOrigin |
PointsTo2::points_to(f.getFunction(), context, func, _) and
origin = resultOrigin.fix(f)
|
exists(PointsToContext2 callee |
exists(PointsToContext callee |
callee.fromCall(f, context) and
func.callResult(callee, value, resultOrigin)
)
@@ -694,7 +686,7 @@ module InterProceduralPointsTo {
/** Points-to for parameter. `def foo(param): ...`. */
pragma [noinline]
predicate parameter_points_to(ParameterDefinition def, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
predicate parameter_points_to(ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
positional_parameter_points_to(def, context, value, origin)
or
named_parameter_points_to(def, context, value, origin)
@@ -706,8 +698,8 @@ module InterProceduralPointsTo {
/** Helper for `parameter_points_to` */
pragma [noinline]
private predicate positional_parameter_points_to(ParameterDefinition def, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
exists(PointsToContext2 caller, ControlFlowNode arg |
private predicate positional_parameter_points_to(ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
exists(PointsToContext caller, ControlFlowNode arg |
PointsTo2::points_to(arg, caller, value, origin) and
callsite_argument_transfer(arg, caller, def, context)
)
@@ -719,8 +711,8 @@ module InterProceduralPointsTo {
/** Helper for `parameter_points_to` */
pragma [noinline]
private predicate named_parameter_points_to(ParameterDefinition def, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
exists(CallNode call, PointsToContext2 caller, PythonFunctionObjectInternal func, string name |
private predicate named_parameter_points_to(ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
exists(CallNode call, PointsToContext caller, PythonFunctionObjectInternal func, string name |
context.fromCall(call, func, caller) and
def.getParameter() = func.getScope().getArgByName(name) and
PointsTo2::points_to(call.getArgByName(name), caller, value, origin)
@@ -728,17 +720,17 @@ module InterProceduralPointsTo {
}
/** Helper for parameter_points_to */
private predicate default_parameter_points_to(ParameterDefinition def, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
exists(PointsToContext2 imp | imp.isImport() | PointsTo2::points_to(def.getDefault(), imp, value, origin)) and
private predicate default_parameter_points_to(ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
exists(PointsToContext imp | imp.isImport() | PointsTo2::points_to(def.getDefault(), imp, value, origin)) and
context_for_default_value(def, context)
}
/** Helper for default_parameter_points_to */
pragma [noinline]
private predicate context_for_default_value(ParameterDefinition def, PointsToContext2 context) {
private predicate context_for_default_value(ParameterDefinition def, PointsToContext context) {
context.isRuntime()
or
exists(PointsToContext2 caller, CallNode call, PythonFunctionObjectInternal func, int n |
exists(PointsToContext caller, CallNode call, PythonFunctionObjectInternal func, int n |
context.fromCall(call, func, caller) and
func.getScope().getArg(n) = def.getParameter() and
not exists(call.getArg(n)) and
@@ -749,7 +741,7 @@ module InterProceduralPointsTo {
}
/** Holds if the `(argument, caller)` pair matches up with `(param, callee)` pair across call. */
cached predicate callsite_argument_transfer(ControlFlowNode argument, PointsToContext2 caller, ParameterDefinition param, PointsToContext2 callee) {
cached predicate callsite_argument_transfer(ControlFlowNode argument, PointsToContext caller, ParameterDefinition param, PointsToContext callee) {
exists(CallNode call, Function func, int n, int offset |
callsite_calls_function(call, caller, func, callee, offset) and
argument = call.getArg(n) and
@@ -757,7 +749,7 @@ module InterProceduralPointsTo {
)
}
cached predicate callsite_calls_function(CallNode call, PointsToContext2 caller, Function scope, PointsToContext2 callee, int parameter_offset) {
cached predicate callsite_calls_function(CallNode call, PointsToContext caller, Function scope, PointsToContext callee, int parameter_offset) {
callee.fromCall(call, caller) and
exists(ObjectInternal func |
PointsTo2::points_to(call.getFunction(), caller, func, _) and
@@ -766,7 +758,7 @@ 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) {
cached predicate scope_entry_value_transfer(EssaVariable pred_var, PointsToContext pred_context, ScopeEntryDefinition succ_def, PointsToContext succ_context) {
scope_entry_value_transfer_from_earlier(pred_var, pred_context, succ_def, succ_context)
or
callsite_entry_value_transfer(pred_var, pred_context, succ_def, succ_context)
@@ -778,7 +770,7 @@ module InterProceduralPointsTo {
/** Helper for `scope_entry_value_transfer`. Transfer of values from a temporally earlier scope to later scope.
* Earlier and later scopes are, for example, a module and functions in that module, or an __init__ method and another method. */
pragma [noinline]
private predicate scope_entry_value_transfer_from_earlier(EssaVariable pred_var, PointsToContext2 pred_context, ScopeEntryDefinition succ_def, PointsToContext2 succ_context) {
private predicate scope_entry_value_transfer_from_earlier(EssaVariable pred_var, PointsToContext pred_context, ScopeEntryDefinition succ_def, PointsToContext succ_context) {
exists(Scope pred_scope, Scope succ_scope |
BaseFlow::scope_entry_value_transfer_from_earlier(pred_var, pred_scope, succ_def, succ_scope) and
succ_context.appliesToScope(succ_scope)
@@ -810,7 +802,7 @@ 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) {
private predicate callsite_entry_value_transfer(EssaVariable caller_var, PointsToContext caller, ScopeEntryDefinition entry_def, PointsToContext callee) {
entry_def.getSourceVariable() = caller_var.getSourceVariable() and
callsite_calls_function(caller_var.getAUse(), caller, entry_def.getScope(), callee, _)
}
@@ -829,12 +821,12 @@ module InterProceduralPointsTo {
* 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) {
predicate callsite_points_to(CallsiteRefinement def, PointsToContext 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 |
exists(EssaVariable var, Function func, PointsToContext 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)
@@ -869,11 +861,11 @@ private predicate potential_builtin_points_to(NameNode f, ObjectInternal value,
(
value = ObjectInternal::builtin(f.getId())
or
not exists(Object::builtin(f.getId())) and value = ObjectInternal::unknown()
not exists(Builtin::builtin(f.getId())) and value = ObjectInternal::unknown()
)
}
module Conditionals {
private module Conditionals {
/** Holds if `expr` is the operand of a unary `not` expression. */
private ControlFlowNode not_operand(ControlFlowNode expr) {
@@ -881,7 +873,7 @@ module Conditionals {
result = expr.(UnaryExprNode).getOperand()
}
boolean branchEvaluatesTo(ControlFlowNode expr, ControlFlowNode use, PointsToContext2 context, ObjectInternal val, ControlFlowNode origin) {
boolean branchEvaluatesTo(ControlFlowNode expr, ControlFlowNode use, PointsToContext context, ObjectInternal val, ControlFlowNode origin) {
contains_interesting_expression_within_test(expr, use) and
PointsTo2::points_to(use, context, val, origin) and
expr = use and
@@ -892,7 +884,7 @@ module Conditionals {
result = branchEvaluatesTo(not_operand(expr), use, context, val, origin).booleanNot()
}
boolean comparisonEvaluatesTo(ControlFlowNode expr, ControlFlowNode use, PointsToContext2 context, ObjectInternal val, ControlFlowNode origin) {
boolean comparisonEvaluatesTo(ControlFlowNode expr, ControlFlowNode use, PointsToContext context, ObjectInternal val, ControlFlowNode origin) {
result = equalityEvaluatesTo(expr, use, context, val, origin)
or
result = inequalityEvaluatesTo(expr, use, context, val, origin)
@@ -907,7 +899,7 @@ module Conditionals {
}
pragma [noinline]
private boolean equalityEvaluatesTo(ControlFlowNode expr, ControlFlowNode use, PointsToContext2 context, ObjectInternal val, ControlFlowNode origin) {
private boolean equalityEvaluatesTo(ControlFlowNode expr, ControlFlowNode use, PointsToContext context, ObjectInternal val, ControlFlowNode origin) {
exists(ControlFlowNode r, boolean sense |
equality_test(expr, use, sense, r) and
exists(ObjectInternal other |
@@ -928,13 +920,13 @@ module Conditionals {
}
pragma [noinline]
private boolean inequalityEvaluatesTo(ControlFlowNode expr, ControlFlowNode use, PointsToContext2 context, ObjectInternal val, ControlFlowNode origin) {
private boolean inequalityEvaluatesTo(ControlFlowNode expr, ControlFlowNode use, PointsToContext context, ObjectInternal val, ControlFlowNode origin) {
exists(ControlFlowNode r, boolean sense |
exists(boolean strict, ObjectInternal other |
(
PointsTo2::inequality(expr, use, r, strict) and sense = true
inequality(expr, use, r, strict) and sense = true
or
PointsTo2::inequality(expr, r, use, strict) and sense = false
inequality(expr, r, use, strict) and sense = false
) and
PointsTo2::points_to(use, context, val, origin) and
PointsTo2::points_to(r, context, other, _)
@@ -959,11 +951,24 @@ module Conditionals {
)
}
/** Helper for comparisons. */
private predicate inequality(CompareNode cmp, ControlFlowNode lesser, ControlFlowNode greater, boolean strict) {
exists(Cmpop op |
cmp.operands(lesser, op, greater) and op.getSymbol() = "<" and strict = true
or
cmp.operands(lesser, op, greater) and op.getSymbol() = "<=" and strict = false
or
cmp.operands(greater, op, lesser) and op.getSymbol() = ">" and strict = true
or
cmp.operands(greater, op, lesser) and op.getSymbol() = ">=" and strict = false
)
}
}
module Types {
cached module Types {
int base_count(ClassObjectInternal cls) {
cached int base_count(ClassObjectInternal cls) {
cls = ObjectInternal::builtin("object") and result = 0
or
exists(cls.getBuiltin()) and cls != ObjectInternal::builtin("object") and result = 1
@@ -978,7 +983,7 @@ module Types {
)
}
ClassObjectInternal getBase(ClassObjectInternal cls, int n) {
cached ClassObjectInternal getBase(ClassObjectInternal cls, int n) {
result.getBuiltin() = cls.getBuiltin().getBaseClass() and n = 0
or
exists(Class pycls |
@@ -993,17 +998,17 @@ module Types {
result = ObjectInternal::builtin("object")
}
predicate isOldStyle(ClassObjectInternal cls) {
cached predicate isOldStyle(ClassObjectInternal cls) {
//To do...
none()
}
predicate isNewStyle(ClassObjectInternal cls) {
cached predicate isNewStyle(ClassObjectInternal cls) {
//To do...
any()
}
ClassList getMro(ClassObjectInternal cls) {
cached ClassList getMro(ClassObjectInternal cls) {
isNewStyle(cls) and
result = Mro::newStyleMro(cls)
or
@@ -1011,7 +1016,7 @@ module Types {
none()
}
predicate declaredAttribute(ClassObjectInternal cls, string name, ObjectInternal value, CfgOrigin origin) {
cached predicate declaredAttribute(ClassObjectInternal cls, string name, ObjectInternal value, CfgOrigin origin) {
value = ObjectInternal::fromBuiltin(cls.getBuiltin().getMember(name)) and origin = CfgOrigin::unknown()
or
value != ObjectInternal::undefined() and
@@ -1022,7 +1027,7 @@ module Types {
)
}
ClassObjectInternal getMetaClass(PythonClassObjectInternal cls) {
cached ClassObjectInternal getMetaClass(PythonClassObjectInternal cls) {
result = declaredMetaClass(cls)
or
hasDeclaredMetaclass(cls) = false and result = getInheritedMetaclass(cls)
@@ -1048,7 +1053,7 @@ module Types {
private boolean has_six_add_metaclass(PythonClassObjectInternal cls) {
// TO DO...
none()
result = false
}
private boolean has_metaclass_var_metaclass(PythonClassObjectInternal cls) {
@@ -1120,7 +1125,7 @@ module Types {
)
or
exists(ClassObjectInternal meta1, ClassObjectInternal meta2 |
meta1 = getMetaClass(getBase(cls, n)) and
meta1 = getBase(cls, n).getClass() and
meta2 = getInheritedMetaclass(cls, n+1)
|
/* Choose sub-class */
@@ -1141,6 +1146,32 @@ module Types {
result = improperSuperType(getBase(cls, _))
}
/* Holds if type inference failed to compute the full class hierarchy for this class for the reason given. */
cached predicate failedInference(ClassObjectInternal cls, string reason) {
strictcount(cls.(PythonClassObjectInternal).getScope().getADecorator()) > 1 and reason = "Multiple decorators"
or
exists(cls.(PythonClassObjectInternal).getScope().getADecorator()) and not six_add_metaclass(_, cls, _) and reason = "Decorator not understood"
or
exists(int i |
exists(cls.(PythonClassObjectInternal).getScope().getBase(i)) and reason = "Missing base " + i
|
not exists(getBase(cls, i))
)
or
exists(cls.(PythonClassObjectInternal).getScope().getMetaClass()) and not exists(cls.getClass()) and reason = "Failed to infer metaclass"
or
exists(int i | failedInference(getBase(cls, i), _) and reason = "Failed inference for base class at position " + i)
or
exists(int i, ObjectInternal base1, ObjectInternal base2 |
base1 = getBase(cls, i) and
base2 = getBase(cls, i) and
base1 != base2 and
reason = "Multiple bases at position " + i
)
or
exists(int i, int j | getBase(cls, i) = getBase(cls, j) and i != j and reason = "Duplicate bases classes")
}
}

View File

@@ -1,6 +1,6 @@
import python
private import semmle.python.pointsto.PointsTo
private import semmle.python.pointsto.PointsTo2
private import semmle.python.objects.ObjectInternal
/*
* A note on 'cost'. Cost doesn't represent the cost to compute,
* but (a vague estimate of) the cost to compute per value gained.
@@ -8,7 +8,7 @@ private import semmle.python.pointsto.PointsTo
*/
private int given_cost() {
exists(string depth |
exists(string depth |
py_flags_versioned("context.cost", depth, _) and
result = depth.toInt()
)
@@ -148,8 +148,8 @@ class PointsToContext extends TPointsToContext {
}
/** Holds if `call` is the call-site from which this context was entered and `caller` is the caller's context. */
predicate fromCall(CallNode call, FunctionObject callee, PointsToContext caller) {
call = PointsTo::get_a_call(callee, caller) and
predicate fromCall(CallNode call, PythonFunctionObjectInternal callee, PointsToContext caller) {
call = PointsTo2::get_a_call(callee, caller) and
this = TCallContext(call, caller, _)
}
@@ -169,16 +169,13 @@ class PointsToContext extends TPointsToContext {
this = TRuntimeContext() and executes_in_runtime_context(s)
or
/* Called functions, regardless of their name */
exists(FunctionObject func, ControlFlowNode call, TPointsToContext outerContext |
call = PointsTo::get_a_call(func, outerContext) and
exists(PythonFunctionObjectInternal func, ControlFlowNode call, TPointsToContext outerContext |
call = PointsTo2::get_a_call(func, outerContext) and
this = TCallContext(call, outerContext, _) and
s = func.getFunction()
s = func.getScope()
)
or
exists(FunctionObject func |
PointsTo::Flow::callsite_calls_function(_, _, func, this, _) and
s = func.getFunction()
)
InterProceduralPointsTo::callsite_calls_function(_, _, s, this, _)
}
/** Holds if this context can apply to the CFG node `n`. */
@@ -225,6 +222,10 @@ class PointsToContext extends TPointsToContext {
result = context_cost(this)
}
CallNode getCall() {
this = TCallContext(result, _, _)
}
/** Holds if a call would be too expensive to create a new context for */
predicate untrackableCall(CallNode call) {
total_cost(call, this) > max_context_cost()
@@ -269,8 +270,3 @@ private predicate maybe_main(Module m) {
)
}
/* For backwards compatibility */
/** DEPRECATED: Use `PointsToContext` instead */
deprecated class FinalContext = PointsToContext;

View File

@@ -1,272 +0,0 @@
import python
private import semmle.python.pointsto.PointsTo2
private import semmle.python.objects.ObjectInternal
/*
* A note on 'cost'. Cost doesn't represent the cost to compute,
* but (a vague estimate of) the cost to compute per value gained.
* This is constantly evolving, so see the various cost functions below for more details.
*/
private int given_cost() {
exists(string depth |
py_flags_versioned("context.cost", depth, _) and
result = depth.toInt()
)
}
private int max_context_cost() {
not py_flags_versioned("context.cost", _, _) and result = 7
or
result = max(int cost | cost = given_cost() | cost)
}
private int syntactic_call_count(Scope s) {
exists(Function f |
f = s and f.getName() != "__init__" |
result = count(CallNode call |
call.getFunction().(NameNode).getId() = f.getName()
or
call.getFunction().(AttrNode).getName() = f.getName()
)
)
or
s.getName() = "__init__" and result = 1
or
not s instanceof Function and result = 0
}
private int incoming_call_cost(Scope s) {
/* Syntactic call count will often be a considerable overestimate
* of the actual number of calls, so we use the square root.
* Cost = log(sqrt(call-count))
*/
result = ((syntactic_call_count(s)+1).log(2)*0.5).floor()
}
private int context_cost(TPointsToContext ctx) {
ctx = TMainContext() and result = 0
or
ctx = TRuntimeContext() and result = 0
or
ctx = TImportContext() and result = 0
or
ctx = TCallContext(_, _, result)
}
private int call_cost(CallNode call) {
if call.getScope().inSource() then
result = 2
else
result = 3
}
private int outgoing_calls(Scope s) {
result = strictcount(CallNode call | call.getScope() = s)
}
predicate super_method_call(CallNode call) {
call.getFunction().(AttrNode).getObject().(CallNode).getFunction().(NameNode).getId() = "super"
}
private int outgoing_call_cost(CallNode c) {
/* Cost = log(outgoing-call-count) */
result = outgoing_calls(c.getScope()).log(2).floor()
}
/** Cost of contexts for a call, the more callers the
* callee of call has the more expensive it is to add contexts for it.
* This seems to be an effective heuristics for preventing an explosion
* in the number of contexts while retaining good results.
*/
private int splay_cost(CallNode c) {
if super_method_call(c) then
result = 0
else
result = outgoing_call_cost(c) + incoming_call_cost(c.getScope())
}
private predicate call_to_init_or_del(CallNode call) {
exists(string mname |
mname = "__init__" or mname = "__del__" |
mname = call.getFunction().(AttrNode).getName()
)
}
/** Total cost estimate */
private int total_call_cost(CallNode call) {
/* We want to always follow __init__ and __del__ calls as they tell us about object construction,
* but we need to be aware of cycles, so they must have a non-zero cost.
*/
if call_to_init_or_del(call) then
result = 1
else
result = call_cost(call) + splay_cost(call)
}
private int total_cost(CallNode call, PointsToContext2 ctx) {
ctx.appliesTo(call) and
result = total_call_cost(call) + context_cost(ctx)
}
private cached newtype TPointsToContext =
TMainContext()
or
TRuntimeContext()
or
TImportContext()
or
TCallContext(ControlFlowNode call, PointsToContext2 outerContext, int cost) {
total_cost(call, outerContext) = cost and
cost <= max_context_cost()
}
/** Points-to context. Context can be one of:
* * "main": Used for scripts.
* * "import": Use for non-script modules.
* * "default": Use for functions and methods without caller context.
* * All other contexts are call contexts and consist of a pair of call-site and caller context.
*/
class PointsToContext2 extends TPointsToContext {
cached string toString() {
this = TMainContext() and result = "main"
or
this = TRuntimeContext() and result = "runtime"
or
this = TImportContext() and result = "import"
or
exists(CallNode callsite, PointsToContext2 outerContext |
this = TCallContext(callsite, outerContext, _) and
result = callsite.getLocation() + " from " + outerContext.toString()
)
}
/** Holds if `call` is the call-site from which this context was entered and `outer` is the caller's context. */
predicate fromCall(CallNode call, PointsToContext2 caller) {
caller.appliesTo(call) and
this = TCallContext(call, caller, _)
}
/** Holds if `call` is the call-site from which this context was entered and `caller` is the caller's context. */
predicate fromCall(CallNode call, PythonFunctionObjectInternal callee, PointsToContext2 caller) {
call = PointsTo2::get_a_call(callee, caller) and
this = TCallContext(call, caller, _)
}
/** Gets the caller context for this callee context. */
PointsToContext2 getOuter() {
this = TCallContext(_, result, _)
}
/** Holds if this context is relevant to the given scope. */
predicate appliesToScope(Scope s) {
/* Scripts */
this = TMainContext() and maybe_main(s)
or
/* Modules and classes evaluated at import */
s instanceof ImportTimeScope and this = TImportContext()
or
this = TRuntimeContext() and executes_in_runtime_context(s)
or
/* Called functions, regardless of their name */
exists(PythonFunctionObjectInternal func, ControlFlowNode call, TPointsToContext outerContext |
call = PointsTo2::get_a_call(func, outerContext) and
this = TCallContext(call, outerContext, _) and
s = func.getScope()
)
or
InterProceduralPointsTo::callsite_calls_function(_, _, s, this, _)
}
/** Holds if this context can apply to the CFG node `n`. */
pragma [inline]
predicate appliesTo(ControlFlowNode n) {
this.appliesToScope(n.getScope())
}
/** Holds if this context is a call context. */
predicate isCall() {
this = TCallContext(_, _, _)
}
/** Holds if this is the "main" context. */
predicate isMain() {
this = TMainContext()
}
/** Holds if this is the "import" context. */
predicate isImport() {
this = TImportContext()
}
/** Holds if this is the "default" context. */
predicate isRuntime() {
this = TRuntimeContext()
}
/** Holds if this context or one of its caller contexts is the default context. */
predicate fromRuntime() {
this.isRuntime()
or
this.getOuter().fromRuntime()
}
/** Gets the depth (number of calls) for this context. */
int getDepth() {
not exists(this.getOuter()) and result = 0
or
result = this.getOuter().getDepth() + 1
}
int getCost() {
result = context_cost(this)
}
CallNode getCall() {
this = TCallContext(result, _, _)
}
/** Holds if a call would be too expensive to create a new context for */
predicate untrackableCall(CallNode call) {
total_cost(call, this) > max_context_cost()
}
CallNode getRootCall() {
this = TCallContext(result, TImportContext(), _)
or
result = this.getOuter().getRootCall()
}
/** Gets a version of Python that this context includes */
pragma [inline]
Version getAVersion() {
/* Currently contexts do not include any version information, but may do in the future */
result = major_version()
}
}
private predicate in_source(Scope s) {
exists(s.getEnclosingModule().getFile().getRelativePath())
}
/** Holds if this scope can be executed in the default context.
* All modules and classes executed at import time and
* all "public" functions and methods, including those invoked by the VM.
*/
predicate executes_in_runtime_context(Function f) {
/* "Public" scope, i.e. functions whose name starts not with an underscore, or special methods */
(f.getName().charAt(0) != "_" or f.isSpecialMethod() or f.isInitMethod())
and
in_source(f)
}
private predicate maybe_main(Module m) {
exists(If i, Compare cmp, Name name, StrConst main |
m.getAStmt() = i and i.getTest() = cmp |
cmp.compares(name, any(Eq eq), main) and
name.getId() = "__name__" and
main.getText() = "__main__"
)
}

View File

@@ -1,7 +1,8 @@
import python
private import semmle.python.pointsto.PointsTo
private import semmle.python.pointsto.Base
private import semmle.python.pointsto.MRO as MRO
private import semmle.python.objects.Classes
private import semmle.python.objects.Instances
private import semmle.python.pointsto.PointsTo2
private import semmle.python.pointsto.MRO2
private import semmle.python.types.Builtins
@@ -20,38 +21,34 @@ private import semmle.python.types.Builtins
*/
class ClassObject extends Object {
private ClassObjectInternal theClass() {
result.getOrigin() = this
or
result.getBuiltin() = this
}
ClassObject() {
this.getOrigin() instanceof ClassExpr or
this.asBuiltin().isClass()
}
private predicate isStr() {
this.asBuiltin() = Builtin::special("bytes") and major_version() = 2
or
this.asBuiltin() = Builtin::special("unicode") and major_version() = 3
}
/** Gets the short (unqualified) name of this class */
string getName() {
this.isStr() and result = "str"
or
not this.isStr() and result = this.asBuiltin().getName() and not this = theUnknownType()
or
result = this.getPyClass().getName()
result = theClass().getName()
}
/** Gets the qualified name for this class.
* Should return the same name as the `__qualname__` attribute on classes in Python 3.
*/
string getQualifiedName() {
this.isBuiltin() and result = this.getName()
result = theClass().getBuiltin().getName()
or
result = this.getPyClass().getQualifiedName()
result = theClass().(PythonClassObjectInternal).getScope().getQualifiedName()
}
/** Gets the nth base class of this class */
Object getBaseType(int n) {
result = PointsTo::Types::class_base_type(this, n)
result = Types::getBase(theClass(), n).getSource()
}
/** Gets a base class of this class */
@@ -61,25 +58,23 @@ class ClassObject extends Object {
/** Whether this class has a base class */
predicate hasABase() {
exists(ClassExpr cls | this.getOrigin() = cls | exists(cls.getABase()))
or
exists(this.asBuiltin().getBaseClass())
exists(Types::getBase(theClass(), _))
}
/** Gets a super class of this class (includes transitive super classes) */
ClassObject getASuperType() {
result = PointsTo::Types::get_a_super_type(this)
result = Types::getMro(theClass()).getTail().getAnItem().getSource()
}
/** Gets a super class of this class (includes transitive super classes) or this class */
ClassObject getAnImproperSuperType() {
result = PointsTo::Types::get_an_improper_super_type(this)
result = Types::getMro(theClass()).getAnItem().getSource()
}
/** Whether this class is a new style class.
A new style class is one that implicitly or explicitly inherits from `object`. */
predicate isNewStyle() {
PointsTo::Types::is_new_style(this)
Types::isNewStyle(theClass())
}
/** Whether this class is a legal exception class.
@@ -98,52 +93,57 @@ class ClassObject extends Object {
/** Returns an attribute declared on this class (not on a super-class) */
Object declaredAttribute(string name) {
PointsTo::Types::class_declared_attribute(this, name, result, _, _)
exists(Value val |
Types::declaredAttribute(theClass(), name, val, _) and
result = val.getSource()
)
}
/** Returns an attribute declared on this class (not on a super-class) */
predicate declaresAttribute(string name) {
class_declares_attribute(this, name)
theClass().getClassDeclaration().declaresAttribute(name)
}
/** Returns an attribute as it would be when looked up at runtime on this class.
Will include attributes of super-classes */
Object lookupAttribute(string name) {
result = this.getMro().lookup(name)
exists(Value val |
theClass().attribute(name, val, _) and
result = val.getSource()
)
}
MRO::ClassList getMro() {
PointsTo::Types::is_new_style_bool(this) = true and
result = MRO::new_style_mro(this)
or
result = MRO::old_style_mro(this)
ClassList getMro() {
result = Types::getMro(theClass())
}
/** Looks up an attribute by searching this class' MRO starting at `start` */
Object lookupMro(ClassObject start, string name) {
result = this.getMro().startingAt(start).lookup(name)
exists(ClassObjectInternal other, ClassObjectInternal decl, Value val |
other.getSource() = start and
decl = Types::getMro(theClass()).startingAt(other).findDeclaringClass(name) and
Types::declaredAttribute(decl, name, val, _) and
result = val.getSource()
)
}
/** Whether the named attribute refers to the object and origin */
predicate attributeRefersTo(string name, Object obj, ControlFlowNode origin) {
exists(CfgOrigin orig |
origin = orig.toCfgNode() and
PointsTo::Types::class_attribute_lookup(this, name, obj, _, orig)
)
this.attributeRefersTo(name, obj, _, origin)
}
/** Whether the named attribute refers to the object, class and origin */
predicate attributeRefersTo(string name, Object obj, ClassObject cls, ControlFlowNode origin) {
not obj = unknownValue() and
exists(CfgOrigin orig |
origin = orig.toCfgNode() and
PointsTo::Types::class_attribute_lookup(this, name, obj, cls, orig)
exists(Value val |
theClass().attribute(name, val, origin) and
obj = val.getSource() and
cls = val.getClass().getSource()
)
}
/** Whether this class has a attribute named `name`, either declared or inherited.*/
predicate hasAttribute(string name) {
PointsTo::Types::class_has_attribute(this, name)
Types::getMro(theClass()).getAnItem().getClassDeclaration().declaresAttribute(name)
}
/** Whether it is impossible to know all the attributes of this class. Usually because it is
@@ -164,7 +164,7 @@ class ClassObject extends Object {
/** Gets the metaclass for this class */
ClassObject getMetaClass() {
result = PointsTo::Types::class_get_meta_class(this)
result = theClass().getClass().getSource()
and
not this.failedInference()
}
@@ -187,7 +187,7 @@ class ClassObject extends Object {
/** Has type inference failed to compute the full class hierarchy for this class for the reason given. */
predicate failedInference(string reason) {
PointsTo::Types::failed_inference(this, reason)
Types::failedInference(theClass(), reason)
}
/** Has type inference failed to compute the full class hierarchy for this class */
@@ -213,8 +213,8 @@ class ClassObject extends Object {
}
/** This class is only instantiated at one place in the code */
private predicate hasStaticallyUniqueInstance() {
strictcount(Object instances | PointsTo::points_to(_, _, instances, this, _)) = 1
private predicate hasStaticallyUniqueInstance() {
strictcount(SpecificInstanceInternal inst | inst.getClass() = theClass()) = 1
}
ImportTimeScope getImportTimeScope() {
@@ -231,10 +231,9 @@ class ClassObject extends Object {
/** Returns the next class in the MRO of 'this' after 'sup' */
ClassObject nextInMro(ClassObject sup) {
exists(MRO::ClassList mro, int i |
mro = this.getMro() and
sup = mro.getItem(i) and
result = mro.getItem(i+1)
exists(ClassObjectInternal other |
other.getSource() = sup and
result = Types::getMro(theClass()).startingAt(other).getTail().getHead().getSource()
) and
not this.failedInference()
}
@@ -243,7 +242,7 @@ class ClassObject extends Object {
* `this` has an index of `1`, the next class in the MRO has an index of `2`, and so on.
*/
ClassObject getMroItem(int index) {
result = this.getMro().getItem(index)
result = this.getMro().getItem(index).getSource()
}
/** Holds if this class has duplicate base classes */

View File

@@ -1,45 +1,20 @@
import python
private import semmle.python.pointsto.PointsTo
private import semmle.python.pointsto.PointsTo2
private import semmle.python.objects.ObjectInternal
/** A bound method object, x.f where type(x) has a method f */
class BoundMethod extends Object {
BoundMethod() {
bound_method(this, _)
}
/* Gets the method 'f' in 'x.f' */
FunctionObject getMethod() {
bound_method(this, result)
}
}
private predicate bound_method(AttrNode binding, FunctionObject method) {
binding = method.getAMethodCall().getFunction()
}
private predicate decorator_call(Object method, ClassObject decorator, FunctionObject func) {
exists(CallNode f |
method = f and
f.getFunction().refersTo(decorator) and
PointsTo::points_to(f.getArg(0), _, func, _, _)
)
}
/** A class method object. Either a decorated function or an explicit call to classmethod(f) */
class ClassMethodObject extends Object {
ClassMethodObject() {
PointsTo::class_method(this, _)
any(ClassMethodObjectInternal cm).getOrigin() = this
}
FunctionObject getFunction() {
PointsTo::class_method(this, result)
}
CallNode getACall() {
PointsTo::class_method_call(this, _, _, _, result)
exists(ClassMethodObjectInternal cm |
cm.getOrigin() = this and
result = cm.getFunction().getSource()
)
}
}
@@ -48,11 +23,14 @@ class ClassMethodObject extends Object {
class StaticMethodObject extends Object {
StaticMethodObject() {
decorator_call(this, theStaticMethodType(), _)
any(StaticMethodObjectInternal sm).getOrigin() = this
}
FunctionObject getFunction() {
decorator_call(this, theStaticMethodType(), result)
exists(StaticMethodObjectInternal sm |
sm.getOrigin() = this and
result = sm.getFunction().getSource()
)
}
}

View File

@@ -1,6 +1,7 @@
import python
import semmle.python.types.Exceptions
private import semmle.python.pointsto.PointsTo
private import semmle.python.pointsto.PointsTo2
private import semmle.python.objects.Callables
private import semmle.python.libraries.Zope
private import semmle.python.pointsto.Base
private import semmle.python.types.Builtins
@@ -8,6 +9,10 @@ private import semmle.python.types.Builtins
/** A function object, whether written in Python or builtin */
abstract class FunctionObject extends Object {
CallableValue theCallable() {
result.getSource() = this
}
predicate isOverridingMethod() {
exists(Object f | this.overrides(f))
}
@@ -42,42 +47,44 @@ abstract class FunctionObject extends Object {
/** Gets a call-site from where this function is called as a function */
CallNode getAFunctionCall() {
PointsTo::function_call(this, _, result)
result.getFunction() instanceof NameNode and
result = this.getACall()
}
/** Gets a call-site from where this function is called as a method */
CallNode getAMethodCall() {
PointsTo::method_call(this, _, result)
result.getFunction() instanceof AttrNode and
result = this.getACall()
}
/** Gets a call-site from where this function is called */
ControlFlowNode getACall() {
result = PointsTo::get_a_call(this, _)
result = PointsTo2::get_a_call(theCallable(), _)
}
/** Gets a call-site from where this function is called, given the `context` */
ControlFlowNode getACall(Context caller_context) {
result = PointsTo::get_a_call(this, caller_context)
result = PointsTo2::get_a_call(theCallable(), caller_context)
}
/** Gets the `ControlFlowNode` that will be passed as the nth argument to `this` when called at `call`.
This predicate will correctly handle `x.y()`, treating `x` as the zeroth argument.
*/
ControlFlowNode getArgumentForCall(CallNode call, int n) {
result = PointsTo::get_positional_argument_for_call(this, _, call, n)
result = theCallable().getArgumentForCall(call, n)
}
/** Gets the `ControlFlowNode` that will be passed as the named argument to `this` when called at `call`.
This predicate will correctly handle `x.y()`, treating `x` as the self argument.
*/
ControlFlowNode getNamedArgumentForCall(CallNode call, string name) {
result = PointsTo::get_named_argument_for_call(this, _, call, name)
result = theCallable().getNamedArgumentForCall(call, name)
}
/** Whether this function never returns. This is an approximation.
*/
predicate neverReturns() {
PointsTo::function_never_returns(this)
theCallable().neverReturns()
}
/** Whether this is a "normal" method, that is, it is exists as a class attribute
@@ -133,7 +140,7 @@ abstract class FunctionObject extends Object {
class PyFunctionObject extends FunctionObject {
PyFunctionObject() {
this.getOrigin() instanceof CallableExpr
any(PythonFunctionObjectInternal f).getOrigin() = this
}
override string toString() {
@@ -255,12 +262,7 @@ abstract class BuiltinCallable extends FunctionObject {
class BuiltinMethodObject extends BuiltinCallable {
BuiltinMethodObject() {
this.asBuiltin().getClass() = theMethodDescriptorType().asBuiltin()
or
this.asBuiltin().getClass() = theBuiltinFunctionType().asBuiltin() and
exists(Builtin cls | cls.isClass() and cls.getMember(_) = this.asBuiltin())
or
this.asBuiltin().getClass().getName() = "wrapper_descriptor"
any(BuiltinMethodObjectInternal m).getBuiltin() = this
}
override string getQualifiedName() {
@@ -312,8 +314,7 @@ class BuiltinMethodObject extends BuiltinCallable {
class BuiltinFunctionObject extends BuiltinCallable {
BuiltinFunctionObject() {
this.asBuiltin().getClass() = theBuiltinFunctionType().asBuiltin() and
exists(ModuleObject m | m.asBuiltin().getMember(_) = this.asBuiltin())
any(BuiltinFunctionObjectInternal f).getBuiltin() = this
}
override string getName() {

View File

@@ -1,14 +1,12 @@
import python
private import semmle.python.pointsto.PointsTo
private import semmle.python.pointsto.Base
private import semmle.python.pointsto.PointsTo2
private import semmle.python.objects.Modules
private import semmle.python.types.ModuleKind
abstract class ModuleObject extends Object {
ModuleObject () {
exists(Module m | m.getEntryNode() = this)
or
this.asBuiltin().getClass() = theModuleType().asBuiltin()
ModuleValue theModule() {
result.getSource() = this
}
/** Gets the scope corresponding to this module, if this is a Python module */
@@ -35,8 +33,9 @@ abstract class ModuleObject extends Object {
/** Gets the named attribute of this module. Using attributeRefersTo() instead
* may provide better results for presentation.
* */
pragma [noinline]
abstract Object getAttribute(string name);
Object getAttribute(string name) {
this.attributeRefersTo(name, result, _)
}
/** Gets the named attribute of this module.
* Synonym for `getAttribute(name)` */
@@ -45,13 +44,17 @@ abstract class ModuleObject extends Object {
result = this.getAttribute(name)
}
/** Whether the named attribute of this module "refers-to" value, with a known origin.
*/
abstract predicate attributeRefersTo(string name, Object value, ControlFlowNode origin);
/** Whether the named attribute of this module "refers-to" value, with known class and a known origin.
*/
abstract predicate attributeRefersTo(string name, Object value, ClassObject cls, ControlFlowNode origin);
predicate hasAttribute(string name) {
exists(theModule().attr(name))
}
predicate attributeRefersTo(string name, Object obj, ControlFlowNode origin) {
exists(Value val |
theModule().(ModuleObjectInternal).attribute(name, val, origin) and
obj = val.getSource()
)
}
/** Gets the package for this module. */
PackageObject getPackage() {
@@ -62,7 +65,7 @@ abstract class ModuleObject extends Object {
/** Whether this module "exports" `name`. That is, whether using `import *` on this module
will result in `name` being added to the namespace. */
predicate exports(string name) {
PointsTo::module_exports(this, name)
theModule().exports(name)
}
/** Whether the complete set of names "exported" by this module can be accurately determined */
@@ -78,11 +81,9 @@ abstract class ModuleObject extends Object {
/** Whether this module is imported by 'import name'. For example on a linux system,
* the module 'posixpath' is imported as 'os.path' or as 'posixpath' */
predicate importedAs(string name) {
PointsTo::module_imported_as(this, name)
InterModulePointsTo::module_imported_as(theModule(), name)
}
abstract predicate hasAttribute(string name);
ModuleObject getAnImportedModule() {
result.importedAs(this.getModule().getAnImportedModuleName())
}
@@ -118,14 +119,6 @@ class BuiltinModuleObject extends ModuleObject {
exists(this.asBuiltin().getMember(name))
}
override predicate attributeRefersTo(string name, Object value, ControlFlowNode origin) {
none()
}
override predicate attributeRefersTo(string name, Object value, ClassObject cls, ControlFlowNode origin) {
none()
}
override predicate exportsComplete() {
any()
}
@@ -157,10 +150,6 @@ class PythonModuleObject extends ModuleObject {
result = this.getModule().getFile()
}
override Object getAttribute(string name) {
this.attributeRefersTo(name, result, _, _)
}
override predicate exportsComplete() {
exists(Module m |
m = this.getModule() |
@@ -172,29 +161,6 @@ class PythonModuleObject extends ModuleObject {
)
}
override predicate hasAttribute(string name) {
PointsTo::module_defines_name(this.getModule(), name)
or
this.attributeRefersTo(name, _, _, _)
or
/* The interpreter always adds the __name__ and __package__ attributes */
name = "__name__" or name = "__package__"
}
override predicate attributeRefersTo(string name, Object value, ControlFlowNode origin) {
exists(CfgOrigin orig |
origin = orig.toCfgNode() and
PointsTo::py_module_attributes(this.getModule(), name, value, _, orig)
)
}
override predicate attributeRefersTo(string name, Object value, ClassObject cls, ControlFlowNode origin) {
exists(CfgOrigin orig |
origin = orig.toCfgNode() and
PointsTo::py_module_attributes(this.getModule(), name, value, cls, orig)
)
}
}
/** Primarily for internal use.
@@ -246,7 +212,10 @@ class PackageObject extends ModuleObject {
}
override Object getAttribute(string name) {
PointsTo::package_attribute_points_to(this, name, result, _, _)
exists(Value val |
theModule().(PackageObjectInternal).attribute(name, _, _) and
result = val.getSource()
)
}
PythonModuleObject getInitModule() {
@@ -273,20 +242,6 @@ class PackageObject extends ModuleObject {
this.getInitModule().hasAttribute(name)
}
override predicate attributeRefersTo(string name, Object value, ControlFlowNode origin) {
exists(CfgOrigin orig |
origin = orig.toCfgNode() and
PointsTo::package_attribute_points_to(this, name, value, _, orig)
)
}
override predicate attributeRefersTo(string name, Object value, ClassObject cls, ControlFlowNode origin) {
exists(CfgOrigin orig |
origin = orig.toCfgNode() and
PointsTo::package_attribute_points_to(this, name, value, cls, orig)
)
}
Location getLocation() {
none()
}

View File

@@ -1,5 +1,6 @@
import python
private import semmle.python.pointsto.Base
private import semmle.python.objects.ObjectAPI
private import semmle.python.objects.ObjectInternal
private import semmle.python.types.Builtins
private cached predicate is_an_object(@py_object obj) {
@@ -121,40 +122,25 @@ class Object extends @py_object {
)
}
private boolean booleanFromValue() {
exists(ObjectInternal obj |
obj.getSource() = this |
result = obj.booleanValue()
)
}
/** The Boolean value of this object if it always evaluates to true or false.
* For example:
* false for None, true for 7 and no result for int(x)
*/
boolean booleanValue() {
this = theNoneObject() and result = false
or
this = theTrueObject() and result = true
or
this = theFalseObject() and result = false
or
this = TupleObject::empty() and result = false
or
exists(Tuple t | t = this.getOrigin() |
exists(t.getAnElt()) and result = true
or
not exists(t.getAnElt()) and result = false
)
or
exists(Unicode s | s.getLiteralObject() = this |
s.getS() = "" and result = false
or
s.getS() != "" and result = true
)
or
exists(Bytes s | s.getLiteralObject() = this |
s.getS() = "" and result = false
or
s.getS() != "" and result = true
)
result = booleanFromValue()
and not booleanFromValue() = result.booleanNot()
}
final predicate maybe() {
not exists(this.booleanValue())
booleanFromValue() = true and
booleanFromValue() = false
}
predicate notClass() {

View File

@@ -1,6 +1,4 @@
import python
import semmle.python.GuardedControlFlow
private import semmle.python.pointsto.PointsTo
/** A Version of the Python interpreter.
* Currently only 2.7 or 3.x but may include different sets of versions in the future. */
@@ -18,152 +16,3 @@ class Version extends int {
}
}
Object theSysVersionInfoTuple() {
py_cmembers_versioned(theSysModuleObject(), "version_info", result, major_version().toString())
}
Object theSysHexVersionNumber() {
py_cmembers_versioned(theSysModuleObject(), "hexversion", result, major_version().toString())
}
Object theSysVersionString() {
py_cmembers_versioned(theSysModuleObject(), "version", result, major_version().toString())
}
string reversed(Cmpop op) {
op instanceof Lt and result = ">"
or
op instanceof Gt and result = "<"
or
op instanceof GtE and result = "<="
or
op instanceof LtE and result = ">="
or
op instanceof Eq and result = "=="
or
op instanceof NotEq and result = "!="
}
/** DEPRECATED:
* A test on the major version of the Python interpreter
* */
class VersionTest extends @py_flow_node {
string toString() {
result = "VersionTest"
}
VersionTest() {
PointsTo::version_const(this, _, _)
}
predicate isTrue() {
PointsTo::version_const(this, _, true)
}
AstNode getNode() {
result = this.(ControlFlowNode).getNode()
}
}
/** A guard on the major version of the Python interpreter */
class VersionGuard extends ConditionBlock {
VersionGuard() {
exists(VersionTest v |
PointsTo::points_to(this.getLastNode(), _, v, _, _) or
PointsTo::points_to(this.getLastNode(), _, _, _, v)
)
}
predicate isTrue() {
exists(VersionTest v |
v.isTrue() |
PointsTo::points_to(this.getLastNode(), _, v, _, _) or
PointsTo::points_to(this.getLastNode(), _, _, _, v)
)
}
}
string os_name(StrConst s) {
exists(string t |
t = s.getText() |
(t = "linux" or t = "linux2") and result = "Linux"
or
t = "win32" and result = "Windows"
or
t = "darwin" and result = "Darwin"
or
not t = "linux" and not t = "linux2" and not t = "win32" and not t = "darwin" and result = t
)
}
predicate get_platform_name(Expr e) {
exists(Attribute a, Name n | a = e and n = a.getObject() |
n.getId() = "sys" and a.getName() = "platform"
)
or
exists(Call c, Attribute a, Name n |
c = e and a = c.getFunc() and n = a.getObject() |
a.getName() = "system" and n.getId() = "platform"
)
}
predicate os_compare(ControlFlowNode f, string name) {
exists(Compare c, Expr l, Expr r, Cmpop op |
c = f.getNode() and
l = c.getLeft() and
r = c.getComparator(0) and
op = c.getOp(0) |
(op instanceof Eq or op instanceof Is)
and
( get_platform_name(l) and name = os_name(r)
or
get_platform_name(r) and name = os_name(l)
)
)
}
class OsTest extends @py_flow_node {
OsTest() {
os_compare(this, _)
}
string getOs() {
os_compare(this, result)
}
string toString() {
result = "OsTest"
}
AstNode getNode() {
result = this.(ControlFlowNode).getNode()
}
}
class OsGuard extends ConditionBlock {
OsGuard() {
exists(OsTest t |
PointsTo::points_to(this.getLastNode(), _, _, theBoolType(), t)
)
}
string getOs() {
exists(OsTest t |
PointsTo::points_to(this.getLastNode(), _, _, theBoolType(), t) and result = t.getOs()
)
}
}