Python points-to: Add property objects.

This commit is contained in:
Mark Shannon
2019-04-12 10:27:46 +01:00
parent ec080419ba
commit 62e05187af
5 changed files with 228 additions and 133 deletions

View File

@@ -407,138 +407,6 @@ class BoundMethodObjectInternal extends CallableObjectInternal, TBoundMethod {
}
class ClassMethodObjectInternal extends ObjectInternal, TClassMethod {
override string toString() {
result = "classmethod(" + this.getFunction() + ")"
}
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::classMethod() }
override boolean isComparable() { none() }
override Builtin getBuiltin() { none() }
override ControlFlowNode getOrigin() { this = TClassMethod(result, _) }
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { none() }
override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() }
override int intValue() { none() }
override string strValue() { none() }
override predicate calleeAndOffset(Function scope, int paramOffset) { none() }
override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() }
override predicate attributesUnknown() { none() }
override boolean isDescriptor() { result = true }
override predicate descriptorGet(ObjectInternal instance, ObjectInternal value, CfgOrigin origin) {
any(ObjectInternal obj).binds(instance, _, this) and
exists(ObjectInternal cls |
instance.isClass() = false and cls = instance.getClass()
or
instance.isClass() = true and cls = instance
|
value = TBoundMethod(cls, this.getFunction()) and
origin = CfgOrigin::unknown()
)
}
override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) {
descriptor = this.getFunction() and
exists(ObjectInternal instance |
any(ObjectInternal obj).binds(instance, name, this) |
instance.isClass() = false and cls = instance.getClass()
or
instance.isClass() = true and cls = instance
)
}
override int length() { 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(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { none() }
override predicate callResult(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() }
override boolean isDescriptor() { result = true }
override predicate descriptorGet(ObjectInternal instance, ObjectInternal value, CfgOrigin origin) {
any(ObjectInternal obj).binds(instance, _, this) and
value = this.getFunction() and origin = CfgOrigin::unknown()
}
override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { none() }
override int length() { none() }
}

View File

@@ -0,0 +1,218 @@
import python
private import semmle.python.objects.TObject
private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.PointsTo
private import semmle.python.pointsto.PointsToContext
private import semmle.python.pointsto.MRO
private import semmle.python.types.Builtins
class PropertyInternal extends ObjectInternal, TProperty {
override string toString() {
result = "property" + this.getName()
}
override boolean booleanValue() { result = true }
override predicate introduced(ControlFlowNode node, PointsToContext context) {
this = TProperty(node, context, _)
}
override ClassDecl getClassDeclaration() { none() }
override boolean isClass() { result = false }
override ObjectInternal getClass() { result = ObjectInternal::property() }
CallableObjectInternal getGetter() {
this = TProperty(_, _, result)
}
override boolean isComparable() { result = true }
override Builtin getBuiltin() { none() }
string getName() {
result = this.getGetter().getName()
}
override ControlFlowNode getOrigin() { this = TProperty(result, _, _) }
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { none() }
override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() }
override int intValue() { none() }
override string strValue() { none() }
override predicate calleeAndOffset(Function scope, int paramOffset) { none() }
override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() }
override predicate attributesUnknown() { none() }
override boolean isDescriptor() { result = true }
override int length() { none() }
private Context getContext() { this = TProperty(_,result, _) }
CallableObjectInternal getSetter() {
exists(CallNode call, AttrNode setter |
call.getFunction() = setter and
PointsToInternal::pointsTo(setter.getObject("setter"), this.getContext(), this, _) and
PointsToInternal::pointsTo(call.getArg(0), this.getContext(), result, _)
)
}
override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) { none() }
override predicate descriptorGet(ObjectInternal instance, ObjectInternal value, CfgOrigin origin) {
/* Just give an unknown value for now. We could improve this, but it would mean
* changing Contexts to account for property accesses.
*/
exists(AttrNode attr, ClassObjectInternal cls, string name |
name = this.getName() and
PointsToInternal::pointsTo(attr.getObject(name), _, instance, _) and
instance.getClass() = cls and
cls.lookup(name, this, _) and
origin = CfgOrigin::fromCfgNode(attr) and value = ObjectInternal::unknown()
)
}
}
class ClassMethodObjectInternal extends ObjectInternal, TClassMethod {
override string toString() {
result = "classmethod(" + this.getFunction() + ")"
}
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::classMethod() }
override boolean isComparable() { result = true }
override Builtin getBuiltin() { none() }
override ControlFlowNode getOrigin() { this = TClassMethod(result, _) }
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { none() }
override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() }
override int intValue() { none() }
override string strValue() { none() }
override predicate calleeAndOffset(Function scope, int paramOffset) { none() }
override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() }
override predicate attributesUnknown() { none() }
override boolean isDescriptor() { result = true }
override predicate descriptorGet(ObjectInternal instance, ObjectInternal value, CfgOrigin origin) {
any(ObjectInternal obj).binds(instance, _, this) and
exists(ObjectInternal cls |
instance.isClass() = false and cls = instance.getClass()
or
instance.isClass() = true and cls = instance
|
value = TBoundMethod(cls, this.getFunction()) and
origin = CfgOrigin::unknown()
)
}
override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) {
descriptor = this.getFunction() and
exists(ObjectInternal instance |
any(ObjectInternal obj).binds(instance, name, this) |
instance.isClass() = false and cls = instance.getClass()
or
instance.isClass() = true and cls = instance
)
}
override int length() { 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() { result = true }
override Builtin getBuiltin() { none() }
override ControlFlowNode getOrigin() { this = TStaticMethod(result, _) }
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { none() }
override predicate callResult(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() }
override boolean isDescriptor() { result = true }
override predicate descriptorGet(ObjectInternal instance, ObjectInternal value, CfgOrigin origin) {
any(ObjectInternal obj).binds(instance, _, this) and
value = this.getFunction() and origin = CfgOrigin::unknown()
}
override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { none() }
override int length() { none() }
}

View File

@@ -352,7 +352,7 @@ class SuperInstance extends TSuperInstance, ObjectInternal {
result = ObjectInternal::builtin("super")
}
override boolean isComparable() { none() }
override boolean isComparable() { result = false }
override Builtin getBuiltin() { none() }

View File

@@ -11,6 +11,7 @@ import semmle.python.objects.Instances
import semmle.python.objects.Callables
import semmle.python.objects.Constants
import semmle.python.objects.Sequences
import semmle.python.objects.Descriptors
class ObjectInternal extends TObject {
@@ -396,6 +397,9 @@ module ObjectInternal {
result = TBuiltinClassObject(Builtin::special("NoneType"))
}
ObjectInternal property() {
result = TBuiltinClassObject(Builtin::special("property"))
}
}
/** Helper for boolean predicates returning both `true` and `false` */

View File

@@ -125,6 +125,11 @@ newtype TObject =
}
or
TType()
or
TProperty(CallNode call, Context ctx, CallableObjectInternal getter) {
PointsToInternal::pointsTo(call.getFunction(), ctx, ObjectInternal::property(), _) and
PointsToInternal::pointsTo(call.getArg(0), ctx, getter, _)
}
private predicate is_power_2(int n) {
n = 1 or