mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Python points-to: Port old API classes to use new points-to.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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, _)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() }
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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` */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
30
python/ql/src/semmle/python/pointsto/PointsToContext.qll
Executable file → Normal file
30
python/ql/src/semmle/python/pointsto/PointsToContext.qll
Executable file → Normal 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;
|
||||
|
||||
|
||||
@@ -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__"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user