mirror of
https://github.com/github/codeql.git
synced 2026-01-15 15:34:49 +01:00
475 lines
15 KiB
Plaintext
475 lines
15 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
|
|
|
|
abstract class CallableObjectInternal extends ObjectInternal {
|
|
/** Gets the scope of this callable if it has one */
|
|
abstract Function getScope();
|
|
|
|
/** Gets a call to this callable from the given context */
|
|
abstract CallNode getACall(PointsToContext ctx);
|
|
|
|
/** Gets a call to this callable */
|
|
CallNode getACall() { result = this.getACall(_) }
|
|
|
|
override boolean isClass() { result = false }
|
|
|
|
override boolean booleanValue() { result = true }
|
|
|
|
override ClassDecl getClassDeclaration() { none() }
|
|
|
|
pragma[noinline]
|
|
override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) {
|
|
none()
|
|
}
|
|
|
|
/** Gets the `n`th parameter node of this callable. */
|
|
abstract NameNode getParameter(int n);
|
|
|
|
/** Gets the `name`d parameter node of this callable. */
|
|
abstract NameNode getParameterByName(string name);
|
|
|
|
abstract predicate neverReturns();
|
|
|
|
override int length() { none() }
|
|
|
|
pragma[noinline]
|
|
override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() }
|
|
|
|
pragma[noinline]
|
|
override predicate attributesUnknown() { none() }
|
|
|
|
override predicate subscriptUnknown() { none() }
|
|
|
|
override int intValue() { none() }
|
|
|
|
override string strValue() { none() }
|
|
|
|
/* Callables aren't iterable */
|
|
override ObjectInternal getIterNext() { none() }
|
|
}
|
|
|
|
/** A Python function. */
|
|
class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFunctionObject {
|
|
override Function getScope() {
|
|
exists(CallableExpr expr |
|
|
this = TPythonFunctionObject(expr.getAFlowNode()) and
|
|
result = expr.getInnerScope()
|
|
)
|
|
}
|
|
|
|
override string toString() { result = "Function " + this.getScope().getQualifiedName() }
|
|
|
|
override predicate introducedAt(ControlFlowNode node, PointsToContext context) {
|
|
this = TPythonFunctionObject(node) and context.appliesTo(node)
|
|
}
|
|
|
|
override ObjectInternal getClass() {
|
|
result = TBuiltinClassObject(Builtin::special("FunctionType"))
|
|
}
|
|
|
|
override predicate notTestableForEquality() { none() }
|
|
|
|
override Builtin getBuiltin() { none() }
|
|
|
|
override ControlFlowNode getOrigin() { this = TPythonFunctionObject(result) }
|
|
|
|
pragma[nomagic]
|
|
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
|
|
exists(Function func, ControlFlowNode rval, ControlFlowNode forigin |
|
|
func = this.getScope() and
|
|
callee.appliesToScope(func)
|
|
|
|
|
rval = func.getAReturnValueFlowNode() and
|
|
PointsToInternal::pointsTo(rval, callee, obj, forigin) and
|
|
origin = CfgOrigin::fromCfgNode(forigin)
|
|
)
|
|
or
|
|
this.procedureReturnsNone(callee, obj, origin)
|
|
}
|
|
|
|
private predicate procedureReturnsNone(
|
|
PointsToContext callee, ObjectInternal obj, CfgOrigin origin
|
|
) {
|
|
exists(Function func |
|
|
func = this.getScope() and
|
|
callee.appliesToScope(func)
|
|
|
|
|
PointsToInternal::reachableBlock(blockReturningNone(func), callee) and
|
|
obj = ObjectInternal::none_() and
|
|
origin = CfgOrigin::unknown()
|
|
)
|
|
}
|
|
|
|
pragma[noinline]
|
|
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
|
|
this.getScope().isProcedure() and
|
|
obj = ObjectInternal::none_() and
|
|
origin = CfgOrigin::fromCfgNode(this.getScope().getEntryNode())
|
|
}
|
|
|
|
override predicate calleeAndOffset(Function scope, int paramOffset) {
|
|
scope = this.getScope() and paramOffset = 0
|
|
}
|
|
|
|
override string getName() { result = this.getScope().getName() }
|
|
|
|
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 and
|
|
origin = CfgOrigin::fromCfgNode(this.getOrigin())
|
|
}
|
|
|
|
pragma[noinline]
|
|
override predicate descriptorGetInstance(
|
|
ObjectInternal instance, ObjectInternal value, CfgOrigin origin
|
|
) {
|
|
value = TBoundMethod(instance, this) and origin = CfgOrigin::unknown()
|
|
}
|
|
|
|
override CallNode getACall(PointsToContext ctx) {
|
|
PointsTo::pointsTo(result.getFunction(), ctx, this, _)
|
|
or
|
|
exists(BoundMethodObjectInternal bm | bm.getACall(ctx) = result and this = bm.getFunction())
|
|
}
|
|
|
|
override NameNode getParameter(int n) { result.getNode() = this.getScope().getArg(n) }
|
|
|
|
override NameNode getParameterByName(string name) {
|
|
result.getNode() = this.getScope().getArgByName(name)
|
|
}
|
|
|
|
override predicate neverReturns() { InterProceduralPointsTo::neverReturns(this.getScope()) }
|
|
|
|
override predicate functionAndOffset(CallableObjectInternal function, int offset) {
|
|
function = this and offset = 0
|
|
}
|
|
|
|
override predicate contextSensitiveCallee() { any() }
|
|
|
|
override predicate useOriginAsLegacyObject() { none() }
|
|
|
|
override predicate isNotSubscriptedType() { any() }
|
|
}
|
|
|
|
private BasicBlock blockReturningNone(Function func) {
|
|
exists(Return ret |
|
|
not exists(ret.getValue()) and
|
|
ret.getScope() = func and
|
|
result = ret.getAFlowNode().getBasicBlock()
|
|
)
|
|
}
|
|
|
|
/** A built-in function such as `len` or `print`. */
|
|
class BuiltinFunctionObjectInternal extends CallableObjectInternal, TBuiltinFunctionObject {
|
|
override Builtin getBuiltin() { this = TBuiltinFunctionObject(result) }
|
|
|
|
override string toString() { result = "Builtin-function " + this.getBuiltin().getName() }
|
|
|
|
override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() }
|
|
|
|
override ObjectInternal getClass() { result = TBuiltinClassObject(this.getBuiltin().getClass()) }
|
|
|
|
override predicate notTestableForEquality() { none() }
|
|
|
|
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
|
|
none()
|
|
}
|
|
|
|
pragma[noinline]
|
|
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
|
|
exists(Builtin func, BuiltinClassObjectInternal cls |
|
|
func = this.getBuiltin() and
|
|
func != Builtin::builtin("isinstance") and
|
|
func != Builtin::builtin("issubclass") and
|
|
func != Builtin::builtin("callable") and
|
|
cls = ObjectInternal::fromBuiltin(this.getReturnType())
|
|
|
|
|
obj = TUnknownInstance(cls)
|
|
or
|
|
cls = ObjectInternal::noneType() and obj = ObjectInternal::none_()
|
|
or
|
|
cls = ObjectInternal::builtin("bool") and obj = ObjectInternal::bool(_)
|
|
) and
|
|
origin = CfgOrigin::unknown()
|
|
or
|
|
this.returnTypeUnknown() and
|
|
obj = ObjectInternal::unknown() and
|
|
origin = CfgOrigin::unknown()
|
|
}
|
|
|
|
override ControlFlowNode getOrigin() { none() }
|
|
|
|
override predicate calleeAndOffset(Function scope, int paramOffset) { none() }
|
|
|
|
override string getName() { result = this.getBuiltin().getName() }
|
|
|
|
Builtin getReturnType() {
|
|
exists(Builtin func |
|
|
func = this.getBuiltin() and
|
|
result = getBuiltinFunctionReturnType(func)
|
|
)
|
|
}
|
|
|
|
private predicate returnTypeUnknown() {
|
|
exists(Builtin func |
|
|
func = this.getBuiltin() and
|
|
not exists(getBuiltinFunctionReturnType(func))
|
|
)
|
|
}
|
|
|
|
override Function getScope() { none() }
|
|
|
|
override boolean isDescriptor() { result = false }
|
|
|
|
pragma[noinline]
|
|
override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) {
|
|
none()
|
|
}
|
|
|
|
pragma[noinline]
|
|
override predicate descriptorGetInstance(
|
|
ObjectInternal instance, ObjectInternal value, CfgOrigin origin
|
|
) {
|
|
none()
|
|
}
|
|
|
|
override CallNode getACall(PointsToContext ctx) {
|
|
PointsTo::pointsTo(result.getFunction(), ctx, this, _)
|
|
}
|
|
|
|
override NameNode getParameter(int n) { none() }
|
|
|
|
override NameNode getParameterByName(string name) { none() }
|
|
|
|
override predicate neverReturns() {
|
|
exists(ModuleObjectInternal sys |
|
|
sys.getName() = "sys" and
|
|
sys.attribute("exit", this, _)
|
|
)
|
|
}
|
|
|
|
override predicate functionAndOffset(CallableObjectInternal function, int offset) {
|
|
function = this and offset = 0
|
|
}
|
|
|
|
override predicate contextSensitiveCallee() { none() }
|
|
|
|
override predicate useOriginAsLegacyObject() { none() }
|
|
|
|
override predicate isNotSubscriptedType() { any() }
|
|
}
|
|
|
|
private Builtin getBuiltinFunctionReturnType(Builtin func) {
|
|
/* Enumerate the types of a few builtin functions, that the CPython analysis misses. */
|
|
func = Builtin::builtin("hex") and result = Builtin::special("str")
|
|
or
|
|
func = Builtin::builtin("oct") and result = Builtin::special("str")
|
|
or
|
|
func = Builtin::builtin("intern") and result = Builtin::special("str")
|
|
or
|
|
func = Builtin::builtin("__import__") and result = Builtin::special("ModuleType")
|
|
or
|
|
/* Fix a few minor inaccuracies in the CPython analysis */
|
|
ext_rettype(func, result) and
|
|
not (
|
|
func = Builtin::builtin("__import__")
|
|
or
|
|
func = Builtin::builtin("compile") and result = Builtin::special("NoneType")
|
|
or
|
|
func = Builtin::builtin("sum")
|
|
or
|
|
func = Builtin::builtin("filter")
|
|
)
|
|
}
|
|
|
|
/** A method of a built-in class (otherwise known as method-descriptors) such as `list.append`. */
|
|
class BuiltinMethodObjectInternal extends CallableObjectInternal, TBuiltinMethodObject {
|
|
override Builtin getBuiltin() { this = TBuiltinMethodObject(result) }
|
|
|
|
override string toString() { result = "builtin method " + this.getBuiltin().getName() }
|
|
|
|
override ObjectInternal getClass() { result = TBuiltinClassObject(this.getBuiltin().getClass()) }
|
|
|
|
override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() }
|
|
|
|
override predicate notTestableForEquality() { none() }
|
|
|
|
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
|
|
none()
|
|
}
|
|
|
|
pragma[noinline]
|
|
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
|
|
exists(Builtin func, BuiltinClassObjectInternal cls |
|
|
func = this.getBuiltin() and
|
|
cls = ObjectInternal::fromBuiltin(this.getReturnType())
|
|
|
|
|
obj = TUnknownInstance(cls)
|
|
or
|
|
cls = ObjectInternal::noneType() and obj = ObjectInternal::none_()
|
|
or
|
|
cls = ObjectInternal::builtin("bool") and obj = ObjectInternal::bool(_)
|
|
) and
|
|
origin = CfgOrigin::unknown()
|
|
or
|
|
this.returnTypeUnknown() and
|
|
obj = ObjectInternal::unknown() and
|
|
origin = CfgOrigin::unknown()
|
|
}
|
|
|
|
Builtin getReturnType() {
|
|
/* If we have a record of the return type in our stubs, use that. */
|
|
exists(Builtin func | func = this.getBuiltin() | ext_rettype(func, result))
|
|
}
|
|
|
|
private predicate returnTypeUnknown() {
|
|
exists(Builtin func | func = this.getBuiltin() | not ext_rettype(func, _))
|
|
}
|
|
|
|
override ControlFlowNode getOrigin() { none() }
|
|
|
|
override predicate calleeAndOffset(Function scope, int paramOffset) { none() }
|
|
|
|
override string getName() { result = this.getBuiltin().getName() }
|
|
|
|
override Function getScope() { 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 and
|
|
origin = CfgOrigin::unknown()
|
|
}
|
|
|
|
pragma[noinline]
|
|
override predicate descriptorGetInstance(
|
|
ObjectInternal instance, ObjectInternal value, CfgOrigin origin
|
|
) {
|
|
value = TBoundMethod(instance, this) and origin = CfgOrigin::unknown()
|
|
}
|
|
|
|
override CallNode getACall(PointsToContext ctx) {
|
|
PointsTo::pointsTo(result.getFunction(), ctx, this, _)
|
|
}
|
|
|
|
override NameNode getParameter(int n) { none() }
|
|
|
|
override NameNode getParameterByName(string name) { none() }
|
|
|
|
override predicate neverReturns() { none() }
|
|
|
|
override predicate functionAndOffset(CallableObjectInternal function, int offset) {
|
|
function = this and offset = 0
|
|
}
|
|
|
|
override predicate contextSensitiveCallee() { none() }
|
|
|
|
override predicate useOriginAsLegacyObject() { none() }
|
|
|
|
override predicate isNotSubscriptedType() { any() }
|
|
}
|
|
|
|
/**
|
|
* A bound-method.
|
|
* Note that built-in methods, such as `[].append` are also represented as bound-methods.
|
|
* Although built-in methods and bound-methods are distinct classes in CPython, their behavior
|
|
* is the same and we treat them identically.
|
|
*/
|
|
class BoundMethodObjectInternal extends CallableObjectInternal, TBoundMethod {
|
|
override Builtin getBuiltin() { none() }
|
|
|
|
CallableObjectInternal getFunction() { this = TBoundMethod(_, result) }
|
|
|
|
ObjectInternal getSelf() { this = TBoundMethod(result, _) }
|
|
|
|
override string toString() {
|
|
result = "Method(" + this.getFunction() + ", " + this.getSelf() + ")"
|
|
}
|
|
|
|
override ObjectInternal getClass() {
|
|
result = TBuiltinClassObject(Builtin::special("MethodType"))
|
|
}
|
|
|
|
override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() }
|
|
|
|
override predicate notTestableForEquality() { any() }
|
|
|
|
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
|
|
this.getFunction().callResult(callee, obj, origin)
|
|
}
|
|
|
|
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
|
|
this.getFunction().callResult(obj, origin)
|
|
}
|
|
|
|
override ControlFlowNode getOrigin() { none() }
|
|
|
|
override predicate calleeAndOffset(Function scope, int paramOffset) {
|
|
this.getFunction().calleeAndOffset(scope, paramOffset - 1)
|
|
}
|
|
|
|
override string getName() { result = this.getFunction().getName() }
|
|
|
|
override Function getScope() { result = this.getFunction().getScope() }
|
|
|
|
override boolean isDescriptor() { result = false }
|
|
|
|
pragma[noinline]
|
|
override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) {
|
|
none()
|
|
}
|
|
|
|
pragma[noinline]
|
|
override predicate descriptorGetInstance(
|
|
ObjectInternal instance, ObjectInternal value, CfgOrigin origin
|
|
) {
|
|
none()
|
|
}
|
|
|
|
override CallNode getACall(PointsToContext ctx) {
|
|
PointsTo::pointsTo(result.getFunction(), ctx, this, _)
|
|
}
|
|
|
|
/** Gets the parameter node that will be used for `self`. */
|
|
NameNode getSelfParameter() { result = this.getFunction().getParameter(0) }
|
|
|
|
override NameNode getParameter(int n) {
|
|
result = this.getFunction().getParameter(n + 1) and
|
|
// don't return the parameter for `self` at `n = -1`
|
|
n >= 0
|
|
}
|
|
|
|
/**
|
|
* Gets the `name`d parameter node of this callable.
|
|
* Will not return the parameter node for `self`, instead use `getSelfParameter`.
|
|
*/
|
|
override NameNode getParameterByName(string name) {
|
|
result = this.getFunction().getParameterByName(name) and
|
|
not result = this.getSelfParameter()
|
|
}
|
|
|
|
override predicate neverReturns() { this.getFunction().neverReturns() }
|
|
|
|
override predicate functionAndOffset(CallableObjectInternal function, int offset) {
|
|
function = this.getFunction() and offset = 1
|
|
or
|
|
function = this and offset = 0
|
|
}
|
|
|
|
override predicate useOriginAsLegacyObject() { any() }
|
|
|
|
override predicate contextSensitiveCallee() { this.getFunction().contextSensitiveCallee() }
|
|
|
|
override predicate isNotSubscriptedType() { any() }
|
|
}
|