Files
codeql/python/ql/lib/semmle/python/objects/Descriptors.qll
2022-03-07 18:59:49 +00:00

386 lines
12 KiB
Plaintext

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
/** A property object. */
class PropertyInternal extends ObjectInternal, TProperty {
/** Gets the name of this property */
override string getName() { result = this.getGetter().getName() }
/** Gets the getter function of this property */
CallableObjectInternal getGetter() { this = TProperty(_, _, result) }
private CallNode getCallNode() { this = TProperty(result, _, _) }
/** Gets the setter function of this property */
CallableObjectInternal getSetter() {
// @x.setter
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, _)
)
or
// x = property(getter, setter, deleter)
exists(ControlFlowNode setter_arg |
setter_arg = this.getCallNode().getArg(1) or
setter_arg = this.getCallNode().getArgByName("fset")
|
PointsToInternal::pointsTo(setter_arg, this.getContext(), result, _)
)
}
/** Gets the setter function of this property */
CallableObjectInternal getDeleter() {
exists(CallNode call, AttrNode setter |
call.getFunction() = setter and
PointsToInternal::pointsTo(setter.getObject("deleter"), this.getContext(), this, _) and
PointsToInternal::pointsTo(call.getArg(0), this.getContext(), result, _)
)
or
// x = property(getter, setter, deleter)
exists(ControlFlowNode deleter_arg |
deleter_arg = this.getCallNode().getArg(2) or
deleter_arg = this.getCallNode().getArgByName("fdel")
|
PointsToInternal::pointsTo(deleter_arg, this.getContext(), result, _)
)
}
private Context getContext() { this = TProperty(_, result, _) }
override string toString() { result = "property " + this.getName() }
override boolean booleanValue() { result = true }
override predicate introducedAt(ControlFlowNode node, PointsToContext context) {
this = TProperty(node, context, _)
}
override ClassDecl getClassDeclaration() { none() }
override boolean isClass() { result = false }
override ObjectInternal getClass() { result = ObjectInternal::property() }
override predicate notTestableForEquality() { none() }
override Builtin getBuiltin() { none() }
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() }
pragma[noinline]
override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) {
value = TPropertySetterOrDeleter(this, name) and origin = CfgOrigin::unknown()
}
override predicate attributesUnknown() { none() }
override predicate subscriptUnknown() { none() }
override boolean isDescriptor() { result = true }
override int length() { none() }
override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) { none() }
pragma[noinline]
override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) {
any(ObjectInternal obj).binds(cls, _, this) and
value = this and
origin = CfgOrigin::fromCfgNode(this.getOrigin())
}
pragma[noinline]
override predicate descriptorGetInstance(
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(ClassObjectInternal cls, string name |
name = this.getName() and
receiver_type(_, name, instance, cls) and
cls.lookup(name, this, _) and
origin = CfgOrigin::unknown() and
value = ObjectInternal::unknown()
)
}
override predicate contextSensitiveCallee() { none() }
override predicate useOriginAsLegacyObject() { none() }
/* Properties aren't iterable */
override ObjectInternal getIterNext() { none() }
override predicate isNotSubscriptedType() { any() }
}
private class PropertySetterOrDeleter extends ObjectInternal, TPropertySetterOrDeleter {
override string toString() { result = this.getProperty().toString() + "." + this.getName() }
override string getName() { this = TPropertySetterOrDeleter(_, result) }
PropertyInternal getProperty() { this = TPropertySetterOrDeleter(result, _) }
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
exists(ControlFlowNode call |
obj = this.getProperty() and
obj = TProperty(call, _, _) and
origin = CfgOrigin::fromCfgNode(call)
)
}
override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() }
override ClassDecl getClassDeclaration() { none() }
override boolean isClass() { result = false }
override ObjectInternal getClass() {
result = TBuiltinClassObject(Builtin::special("MethodType"))
}
override predicate notTestableForEquality() { none() }
override Builtin getBuiltin() { none() }
override ControlFlowNode getOrigin() { none() }
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
none()
}
override int intValue() { none() }
override string strValue() { none() }
override boolean booleanValue() { result = true }
override predicate calleeAndOffset(Function scope, int paramOffset) { none() }
override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() }
override predicate attributesUnknown() { none() }
override predicate subscriptUnknown() { none() }
override boolean isDescriptor() { result = true }
override int length() { none() }
override predicate binds(ObjectInternal cls, string name, ObjectInternal descriptor) { none() }
override predicate contextSensitiveCallee() { none() }
override ObjectInternal getIterNext() { none() }
override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) {
none()
}
override predicate descriptorGetInstance(
ObjectInternal instance, ObjectInternal value, CfgOrigin origin
) {
none()
}
override predicate useOriginAsLegacyObject() { none() }
override predicate isNotSubscriptedType() { any() }
}
/** A class representing classmethods in Python */
class ClassMethodObjectInternal extends ObjectInternal, TClassMethod {
override string toString() { result = "classmethod(" + this.getFunction() + ")" }
override boolean booleanValue() { result = true }
override predicate introducedAt(ControlFlowNode node, PointsToContext context) {
exists(CallableObjectInternal function |
this = TClassMethod(node, function) and
class_method(node, function, context)
)
}
/** Gets the function wrapped by this classmethod object */
CallableObjectInternal getFunction() { this = TClassMethod(_, result) }
override ClassDecl getClassDeclaration() { none() }
override boolean isClass() { result = false }
override ObjectInternal getClass() { result = ObjectInternal::classMethod() }
override predicate notTestableForEquality() { 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 predicate subscriptUnknown() { none() }
override boolean isDescriptor() { result = true }
pragma[noinline]
override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) {
value = TBoundMethod(cls, this.getFunction()) and
origin = CfgOrigin::unknown()
}
pragma[noinline]
override predicate descriptorGetInstance(
ObjectInternal instance, ObjectInternal value, CfgOrigin origin
) {
any(ObjectInternal obj).binds(instance, _, this) and
value = TBoundMethod(instance.getClass(), this.getFunction()) and
origin = CfgOrigin::unknown()
}
/**
* Holds if attribute lookup on this object may "bind" `cls` to `descriptor`.
* `cls` will always be a class as this is a classmethod.
* Here "bind" means that `instance` is passed to the `classmethod.__get__()` method
* at runtime. The term "bind" is used as this most likely results in a bound-method.
*/
pragma[noinline]
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() }
override string getName() { result = this.getFunction().getName() }
override predicate contextSensitiveCallee() { none() }
override predicate useOriginAsLegacyObject() { none() }
/* Classmethods aren't iterable */
override ObjectInternal getIterNext() { none() }
override predicate isNotSubscriptedType() { any() }
}
class StaticMethodObjectInternal extends ObjectInternal, TStaticMethod {
override string toString() { result = "staticmethod()" }
override boolean booleanValue() { result = true }
override predicate introducedAt(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 predicate notTestableForEquality() { 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 predicate subscriptUnknown() { none() }
override boolean isDescriptor() { result = true }
pragma[noinline]
override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) {
any(ObjectInternal obj).binds(cls, _, this) and
value = this.getFunction() and
origin = CfgOrigin::unknown()
}
pragma[noinline]
override predicate descriptorGetInstance(
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() }
override string getName() { result = this.getFunction().getName() }
override predicate contextSensitiveCallee() { none() }
override predicate useOriginAsLegacyObject() { none() }
/* Staticmethods aren't iterable */
override ObjectInternal getIterNext() { none() }
override predicate isNotSubscriptedType() { any() }
}