From 84875d70ffc9ba0f8040814d7a5729820c700812 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Wed, 26 Feb 2020 21:42:52 -0800 Subject: [PATCH 01/16] Adds preliminary modernization This will overlapp with/depend on changes to CallArgs and ObjectAPI that are already in the WrongNamedArgumentInCall PR --- python/ql/src/Expressions/WrongNumberArgumentsInCall.ql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql b/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql index 9f636213a34..4101bd3a854 100644 --- a/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql +++ b/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql @@ -14,15 +14,15 @@ import python import CallArgs -from Call call, FunctionObject func, string too, string should, int limit +from Call call, FunctionValue func, string too, string should, int limit where ( - too_many_args_objectapi(call, func, limit) and too = "too many arguments" and should = "no more than " + too_many_args(call, func, limit) and too = "too many arguments" and should = "no more than " or - too_few_args_objectapi(call, func, limit) and too = "too few arguments" and should = "no fewer than " + too_few_args(call, func, limit) and too = "too few arguments" and should = "no fewer than " ) and not func.isAbstract() and -not exists(FunctionObject overridden | func.overrides(overridden) and correct_args_if_called_as_method_objectapi(call, overridden)) +not exists(FunctionValue overridden | func.overrides(overridden) and correct_args_if_called_as_method(call, overridden)) /* The semantics of `__new__` can be a bit subtle, so we simply exclude `__new__` methods */ and not func.getName() = "__new__" From 741317bd050326008c29ad076b736045c89a70ea Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Tue, 31 Mar 2020 22:56:44 -0700 Subject: [PATCH 02/16] Python: ObjectAPI to ValueAPI: Makes isAbstract a predicate in CallArgs --- python/ql/src/Expressions/CallArgs.qll | 4 ++++ python/ql/src/Expressions/WrongNumberArgumentsInCall.ql | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/python/ql/src/Expressions/CallArgs.qll b/python/ql/src/Expressions/CallArgs.qll index 5dcf50ac8f4..b071bbac372 100644 --- a/python/ql/src/Expressions/CallArgs.qll +++ b/python/ql/src/Expressions/CallArgs.qll @@ -255,3 +255,7 @@ predicate overridden_call(FunctionValue func, FunctionValue overriding, Call cal overriding.getACall().getNode() = call } +/** Holds if `func` will raise a `NotImplemented` error. */ +predicate isAbstract(FunctionValue func) { + func.getARaisedType() = ClassValue::notImplementedError() +} \ No newline at end of file diff --git a/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql b/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql index 4101bd3a854..02bc685c096 100644 --- a/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql +++ b/python/ql/src/Expressions/WrongNumberArgumentsInCall.ql @@ -21,7 +21,7 @@ where or too_few_args(call, func, limit) and too = "too few arguments" and should = "no fewer than " ) and -not func.isAbstract() and +not isAbstract(func) and not exists(FunctionValue overridden | func.overrides(overridden) and correct_args_if_called_as_method(call, overridden)) /* The semantics of `__new__` can be a bit subtle, so we simply exclude `__new__` methods */ and not func.getName() = "__new__" From 6517feda9a4931c5c6a7566c1e9ef4c4829a04f3 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Thu, 2 Apr 2020 11:56:15 -0700 Subject: [PATCH 03/16] Python: ObjectAPI to ValueAPI: WrongNumberArgumentsInCall: Adds new version of ObjectsAPI.qll --- .../src/semmle/python/objects/ObjectAPI.qll | 711 ++++++++---------- 1 file changed, 294 insertions(+), 417 deletions(-) diff --git a/python/ql/src/semmle/python/objects/ObjectAPI.qll b/python/ql/src/semmle/python/objects/ObjectAPI.qll index 4b8aaaa45b8..380f42043c7 100644 --- a/python/ql/src/semmle/python/objects/ObjectAPI.qll +++ b/python/ql/src/semmle/python/objects/ObjectAPI.qll @@ -3,9 +3,6 @@ * A `Value` is a static approximation to a set of runtime objects. */ - - - import python private import TObject private import semmle.python.objects.ObjectInternal @@ -14,54 +11,48 @@ private import semmle.python.pointsto.PointsToContext private import semmle.python.pointsto.MRO private import semmle.python.types.Builtins -/* Use the term `ObjectSource` to refer to DB entity. Either a CFG node +/* + * Use the term `ObjectSource` to refer to DB entity. Either a CFG node * for Python objects, or `@py_cobject` entity for built-in objects. */ + class ObjectSource = Object; /* Aliases for scopes */ class FunctionScope = Function; + class ClassScope = Class; + class ModuleScope = Module; -/** Class representing values in the Python program +/** + * Class representing values in the Python program * Each `Value` is a static approximation to a set of one or more real objects. */ class Value extends TObject { - Value() { this != ObjectInternal::unknown() and this != ObjectInternal::unknownClass() and this != ObjectInternal::undefined() } - string toString() { - result = this.(ObjectInternal).toString() - } + string toString() { result = this.(ObjectInternal).toString() } /** Gets a `ControlFlowNode` that refers to this object. */ - ControlFlowNode getAReference() { - PointsToInternal::pointsTo(result, _, this, _) - } + ControlFlowNode getAReference() { PointsToInternal::pointsTo(result, _, this, _) } /** Gets the origin CFG node for this value. */ - ControlFlowNode getOrigin() { - result = this.(ObjectInternal).getOrigin() - } + ControlFlowNode getOrigin() { result = this.(ObjectInternal).getOrigin() } - - /** Gets the class of this object. + /** + * Gets the class of this object. * Strictly, the `Value` representing the class of the objects * represented by this Value. */ - ClassValue getClass() { - result = this.(ObjectInternal).getClass() - } + ClassValue getClass() { result = this.(ObjectInternal).getClass() } /** Gets a call to this object */ - CallNode getACall() { - result = this.getACall(_) - } + CallNode getACall() { result = this.getACall(_) } /** Gets a call to this object with the given `caller` context. */ CallNode getACall(PointsToContext caller) { @@ -74,36 +65,34 @@ class Value extends TObject { } /** Gets a `Value` that represents the attribute `name` of this object. */ - Value attr(string name) { - this.(ObjectInternal).attribute(name, result, _) - } + Value attr(string name) { this.(ObjectInternal).attribute(name, result, _) } - /** Holds if this value is builtin. Applies to built-in functions and methods, + /** + * Holds if this value is builtin. Applies to built-in functions and methods, * but also integers and strings. */ - predicate isBuiltin() { - this.(ObjectInternal).isBuiltin() - } + predicate isBuiltin() { this.(ObjectInternal).isBuiltin() } predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) { this.(ObjectInternal).getOrigin().getLocation().hasLocationInfo(filepath, bl, bc, el, ec) or not exists(this.(ObjectInternal).getOrigin()) and - filepath = "" and bl = 0 and bc = 0 and el = 0 and ec = 0 + filepath = "" and + bl = 0 and + bc = 0 and + el = 0 and + ec = 0 } - /** Gets the name of this value, if it has one. + /** + * Gets the name of this value, if it has one. * Note this is the innate name of the * object, not necessarily all the names by which it can be called. */ - final string getName() { - result = this.(ObjectInternal).getName() - } + final string getName() { result = this.(ObjectInternal).getName() } /** Holds if this value has the attribute `name` */ - predicate hasAttribute(string name) { - this.(ObjectInternal).hasAttribute(name) - } + predicate hasAttribute(string name) { this.(ObjectInternal).hasAttribute(name) } /** Whether this value is absent from the database, but has been inferred to likely exist */ predicate isAbsent() { @@ -112,9 +101,10 @@ class Value extends TObject { this instanceof AbsentModuleAttributeObjectInternal } - /** Whether this overrides v. In this context, "overrides" means that this object - * is a named attribute of a some class C and `v` is a named attribute of another - * class S, both attributes having the same name, and S is a super class of C. + /** + * Whether this overrides v. In this context, "overrides" means that this object + * is a named attribute of a some class C and `v` is a named attribute of another + * class S, both attributes having the same name, and S is a super class of C. */ predicate overrides(Value v) { exists(ClassValue my_class, ClassValue other_class, string name | @@ -124,46 +114,42 @@ class Value extends TObject { ) } - /** Gets the boolean interpretation of this value. - * Could be both `true` and `false`, if we can't determine the result more precisely. - */ - boolean getABooleanValue() { - result = this.(ObjectInternal).booleanValue() - } + /** + * Gets the boolean interpretation of this value. + * Could be both `true` and `false`, if we can't determine the result more precisely. + */ + boolean getABooleanValue() { result = this.(ObjectInternal).booleanValue() } - /** Gets the boolean interpretation of this value, only if we can determine the result precisely. - * The result can be `none()`, but never both `true` and `false`. - */ + /** + * Gets the boolean interpretation of this value, only if we can determine the result precisely. + * The result can be `none()`, but never both `true` and `false`. + */ boolean getDefiniteBooleanValue() { result = getABooleanValue() and not (getABooleanValue() = true and getABooleanValue() = false) } } -/** Class representing modules in the Python program +/** + * Class representing modules in the Python program * Each `ModuleValue` represents a module object in the Python program. */ class ModuleValue extends Value { + ModuleValue() { this instanceof ModuleObjectInternal } - ModuleValue() { - this instanceof ModuleObjectInternal - } - - /** Holds if this module "exports" name. + /** + * Holds if this module "exports" name. * That is, does it define `name` in `__all__` or is * `__all__` not defined and `name` a global variable that does not start with "_" * This is the set of names imported by `from ... import *`. */ - predicate exports(string name) { - PointsTo::moduleExports(this, name) - } + predicate exports(string name) { PointsTo::moduleExports(this, name) } /** Gets the scope for this module, provided that it is a Python module. */ - ModuleScope getScope() { - result = this.(ModuleObjectInternal).getSourceModule() - } + ModuleScope getScope() { result = this.(ModuleObjectInternal).getSourceModule() } - /** Gets the container path for this module. Will be the file for a Python module, + /** + * Gets the container path for this module. Will be the file for a Python module, * the folder for a package and no result for a builtin module. */ Container getPath() { @@ -172,26 +158,20 @@ class ModuleValue extends Value { result = this.(PythonModuleObjectInternal).getSourceModule().getFile() } - /** 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) { - PointsToInternal::module_imported_as(this, name) - } + /** + * 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) { PointsToInternal::module_imported_as(this, name) } /** Whether this module is a package. */ - predicate isPackage() { - this instanceof PackageObjectInternal - } + predicate isPackage() { this instanceof PackageObjectInternal } /** Whether the complete set of names "exported" by this module can be accurately determined */ - predicate hasCompleteExportInfo() { - this.(ModuleObjectInternal).hasCompleteExportInfo() - } + predicate hasCompleteExportInfo() { this.(ModuleObjectInternal).hasCompleteExportInfo() } /** Get a module that this module imports */ - ModuleValue getAnImportedModule() { - result.importedAs(this.getScope().getAnImportedModuleName()) - } + ModuleValue getAnImportedModule() { result.importedAs(this.getScope().getAnImportedModuleName()) } /** When used as a normal module (for example, imported and used by other modules) */ predicate isUsedAsModule() { @@ -214,7 +194,8 @@ class ModuleValue extends Value { i.getScope() = this.getScope() and op instanceof Eq and i.getTest().(Compare).compares(name, op, main) and - name.getId() = "__name__" and main.getText() = "__main__" + name.getId() = "__name__" and + main.getText() = "__main__" ) or exists(Comment c | @@ -227,8 +208,8 @@ class ModuleValue extends Value { } module Module { - - /** Gets the `ModuleValue` named `name`. + /** + * Gets the `ModuleValue` named `name`. * * Note that the name used to refer to a module is not * necessarily its name. For example, @@ -245,25 +226,23 @@ module Module { /* Prevent runaway recursion when a module has itself as an attribute. */ private ModuleValue named(string name, int dots) { - dots = 0 and not name.charAt(_) = "." and + dots = 0 and + not name.charAt(_) = "." and result.getName() = name or dots <= 3 and - exists(string modname, string attrname | - name = modname + "." + attrname | - result = named(modname, dots-1).attr(attrname) + exists(string modname, string attrname | name = modname + "." + attrname | + result = named(modname, dots - 1).attr(attrname) ) } /** Get the `ModuleValue` for the `builtin` module. */ - ModuleValue builtinModule() { - result = TBuiltinModuleObject(Builtin::builtinModule()) - } + ModuleValue builtinModule() { result = TBuiltinModuleObject(Builtin::builtinModule()) } } module Value { - - /** Gets the `Value` named `name`. + /** + * Gets the `Value` named `name`. * If there is at least one '.' in `name`, then the part of * the name to the left of the rightmost '.' is interpreted as a module name * and the part after the rightmost '.' as an attribute of that module. @@ -274,8 +253,7 @@ module Value { * For example `Value::named("len")` is the `Value` representing the `len` built-in function. */ Value named(string name) { - exists(string modname, string attrname | - name = modname + "." + attrname | + exists(string modname, string attrname | name = modname + "." + attrname | result = Module::named(modname).attr(attrname) ) or @@ -288,34 +266,32 @@ module Value { name = "False" and result = TFalse() } - /** Gets the `NumericValue` for the integer constant `i`, if it exists. + /** + * Gets the `NumericValue` for the integer constant `i`, if it exists. * There will be no `NumericValue` for most integers, but the following are * guaranteed to exist: * * From zero to 511 inclusive. * * All powers of 2 (up to 2**30) * * Any integer explicitly mentioned in the source program. */ - NumericValue forInt(int i) { - result.(IntObjectInternal).intValue() = i - } + NumericValue forInt(int i) { result.(IntObjectInternal).intValue() = i } - /** Gets the `Value` for the bytes constant `bytes`, if it exists. + /** + * Gets the `Value` for the bytes constant `bytes`, if it exists. * There will be no `Value` for most byte strings, unless it is explicitly * declared in the source program. */ - StringValue forBytes(string bytes) { - result.(BytesObjectInternal).strValue() = bytes - } + StringValue forBytes(string bytes) { result.(BytesObjectInternal).strValue() = bytes } - /** Gets the `Value` for the unicode constant `text`, if it exists. + /** + * Gets the `Value` for the unicode constant `text`, if it exists. * There will be no `Value` for most text strings, unless it is explicitly * declared in the source program. */ - StringValue forUnicode(string text) { - result.(UnicodeObjectInternal).strValue() = text - } + StringValue forUnicode(string text) { result.(UnicodeObjectInternal).strValue() = text } - /** Gets a `Value` for the string `text`. May be a bytes or unicode string for Python 2. + /** + * Gets a `Value` for the string `text`. May be a bytes or unicode string for Python 2. * There will be no `Value` for most strings, unless it is explicitly * declared in the source program. */ @@ -334,9 +310,7 @@ module Value { } /** Gets the `Value` for `None`. */ - Value none_() { - result = ObjectInternal::none_() - } + Value none_() { result = ObjectInternal::none_() } /** * Shorcuts added by the `site` module to exit your interactive session. @@ -353,32 +327,25 @@ module Value { } } -/** Class representing callables in the Python program +/** + * Class representing callables in the Python program * Callables include Python functions, built-in functions and bound-methods, * but not classes. */ class CallableValue extends Value { + CallableValue() { this instanceof CallableObjectInternal } - CallableValue() { - this instanceof CallableObjectInternal - } - - /** Holds if this callable never returns once called. + /** + * Holds if this callable never returns once called. * For example, `sys.exit` */ - predicate neverReturns() { - this.(CallableObjectInternal).neverReturns() - } + predicate neverReturns() { this.(CallableObjectInternal).neverReturns() } /** Gets the scope for this function, provided that it is a Python function. */ - FunctionScope getScope() { - result = this.(PythonFunctionObjectInternal).getScope() - } + FunctionScope getScope() { result = this.(PythonFunctionObjectInternal).getScope() } /** Gets the `n`th parameter node of this callable. */ - NameNode getParameter(int n) { - result = this.(CallableObjectInternal).getParameter(n) - } + NameNode getParameter(int n) { result = this.(CallableObjectInternal).getParameter(n) } /** Gets the `name`d parameter node of this callable. */ NameNode getParameterByName(string name) { @@ -386,56 +353,60 @@ class CallableValue extends Value { } /** Gets the argument corresponding to the `n'th parameter node of this callable. */ - cached ControlFlowNode getArgumentForCall(CallNode call, int n) { + cached + ControlFlowNode getArgumentForCall(CallNode call, int n) { exists(ObjectInternal called, int offset | PointsToInternal::pointsTo(call.getFunction(), _, called, _) and called.functionAndOffset(this, offset) - | - call.getArg(n-offset) = result + | + call.getArg(n - offset) = result or - exists(string name | call.getArgByName(name) = result and this.(PythonFunctionObjectInternal).getScope().getArg(n+offset).getName() = name) + exists(string name | + call.getArgByName(name) = result and + this.(PythonFunctionObjectInternal).getScope().getArg(n + offset).getName() = name + ) or called instanceof BoundMethodObjectInternal and - offset = 1 and n = 0 and result = call.getFunction().(AttrNode).getObject() + offset = 1 and + n = 0 and + result = call.getFunction().(AttrNode).getObject() ) } - /** Gets the argument corresponding to the `name`d parameter node of this callable. */ - cached ControlFlowNode getNamedArgumentForCall(CallNode call, string name) { + cached + ControlFlowNode getNamedArgumentForCall(CallNode call, string name) { exists(CallableObjectInternal called, int offset | PointsToInternal::pointsTo(call.getFunction(), _, called, _) and called.functionAndOffset(this, offset) - | + | exists(int n | call.getArg(n) = result and - this.(PythonFunctionObjectInternal).getScope().getArg(n+offset).getName() = name + this.(PythonFunctionObjectInternal).getScope().getArg(n + offset).getName() = name ) or call.getArgByName(name) = result and exists(this.(PythonFunctionObjectInternal).getScope().getArgByName(name)) or called instanceof BoundMethodObjectInternal and - offset = 1 and name = "self" and result = call.getFunction().(AttrNode).getObject() + offset = 1 and + name = "self" and + result = call.getFunction().(AttrNode).getObject() ) } - } -/** Class representing classes in the Python program, both Python and built-in. +/** + * Class representing classes in the Python program, both Python and built-in. */ class ClassValue extends Value { - - ClassValue() { - this.(ObjectInternal).isClass() = true - } + ClassValue() { this.(ObjectInternal).isClass() = true } /** Gets an improper super type of this class. */ - ClassValue getASuperType() { - result = this.getABaseType*() - } + ClassValue getASuperType() { result = this.getABaseType*() } - /** Looks up the attribute `name` on this class. + /** + * Looks up the attribute `name` on this class. * Note that this may be different from `this.attr(name)`. * For example given the class: * ```class C: @@ -444,15 +415,11 @@ class ClassValue extends Value { * ``` * `this.lookup("f")` is equivalent to `C.__dict__['f']`, which is the class-method * whereas - * `this.attr("f") is equivalent to `C.f`, which is a bound-method. + * `this.attr("f")` is equivalent to `C.f`, which is a bound-method. */ - Value lookup(string name) { - this.(ClassObjectInternal).lookup(name, result, _) - } + Value lookup(string name) { this.(ClassObjectInternal).lookup(name, result, _) } - predicate isCallable() { - this.(ClassObjectInternal).lookup("__call__", _, _) - } + predicate isCallable() { this.(ClassObjectInternal).lookup("__call__", _, _) } /** Holds if this class is an iterable. */ predicate isIterable() { @@ -462,17 +429,17 @@ class ClassValue extends Value { or this.hasAttribute("__getitem__") } - - /** Holds if this class is a container(). That is, does it have a __getitem__ method.*/ - predicate isContainer() { - exists(this.lookup("__getitem__")) - } - + + /** Holds if this class is a container(). That is, does it have a __getitem__ method. */ + predicate isContainer() { exists(this.lookup("__getitem__")) } + /** Holds if this class is probably a sequence. */ predicate isSequence() { - /* To determine whether something is a sequence or a mapping is not entirely clear, + /* + * To determine whether something is a sequence or a mapping is not entirely clear, * so we need to guess a bit. */ + this.getASuperType() = ClassValue::tuple() or this.getASuperType() = ClassValue::list() @@ -494,18 +461,15 @@ class ClassValue extends Value { this.hasAttribute("__reversed__") ) } - + /** Holds if this class is a mapping. */ predicate isMapping() { - this.hasAttribute("__getitem__") - and + this.hasAttribute("__getitem__") and not this.isSequence() } /** Holds if this class is a descriptor. */ - predicate isDescriptorType() { - this.hasAttribute("__get__") - } + predicate isDescriptorType() { this.hasAttribute("__get__") } /** Holds if this class is a context manager. */ predicate isContextManager() { @@ -513,7 +477,8 @@ class ClassValue extends Value { this.hasAttribute("__exit__") } - /** Gets the qualified name for this class. + /** + * Gets the qualified name for this class. * Should return the same name as the `__qualname__` attribute on classes in Python 3. */ string getQualifiedName() { @@ -523,62 +488,52 @@ class ClassValue extends Value { } /** Gets the MRO for this class */ - MRO getMro() { - result = Types::getMro(this) - } + MRO getMro() { result = Types::getMro(this) } - predicate failedInference(string reason) { - Types::failedInference(this, reason) - } + predicate failedInference(string reason) { Types::failedInference(this, reason) } /** Gets the nth immediate base type of this class. */ - ClassValue getBaseType(int n) { - result = Types::getBase(this, n) - } + ClassValue getBaseType(int n) { result = Types::getBase(this, n) } /** Gets an immediate base type of this class. */ - ClassValue getABaseType() { - result = Types::getBase(this, _) - } + ClassValue getABaseType() { result = Types::getBase(this, _) } - /** Holds if this class is a new style class. - A new style class is one that implicitly or explicitly inherits from `object`. */ - predicate isNewStyle() { - Types::isNewStyle(this) - } + /** + * Holds if this class is a new style class. + * A new style class is one that implicitly or explicitly inherits from `object`. + */ + predicate isNewStyle() { Types::isNewStyle(this) } - /** Holds if this class is an old style class. - An old style class is one that does not inherit from `object`. */ - predicate isOldStyle() { - Types::isOldStyle(this) - } + /** + * Holds if this class is an old style class. + * An old style class is one that does not inherit from `object`. + */ + predicate isOldStyle() { Types::isOldStyle(this) } /** Gets the scope associated with this class, if it is not a builtin class */ - ClassScope getScope() { - result = this.(PythonClassObjectInternal).getScope() - } + ClassScope getScope() { result = this.(PythonClassObjectInternal).getScope() } /** Gets the attribute declared in this class */ - Value declaredAttribute(string name) { - Types::declaredAttribute(this, name, result, _) - } + Value declaredAttribute(string name) { Types::declaredAttribute(this, name, result, _) } - /** Holds if this class has the attribute `name`, including + /** + * Holds if this class has the attribute `name`, including * attributes declared by super classes. */ - predicate hasAttribute(string name) { - this.getMro().declares(name) - } + predicate hasAttribute(string name) { this.getMro().declares(name) } - /** Holds if this class declares the attribute `name`, + /** + * Holds if this class declares the attribute `name`, * *not* including attributes declared by super classes. */ predicate declaresAttribute(string name) { this.(ClassObjectInternal).getClassDeclaration().declaresAttribute(name) } - /** Whether this class is a legal exception class. - * What constitutes a legal exception class differs between major versions */ + /** + * Whether this class is a legal exception class. + * What constitutes a legal exception class differs between major versions + */ predicate isLegalExceptionType() { not this.isNewStyle() or @@ -586,30 +541,27 @@ class ClassValue extends Value { or major_version() = 2 and this = ClassValue::tuple() } - } - -/** Class representing functions in the Python program, both Python and built-in. +/** + * Class representing functions in the Python program, both Python and built-in. * Note that this does not include other callables such as bound-methods. */ abstract class FunctionValue extends CallableValue { - abstract string getQualifiedName(); + /** Gets a longer, more descriptive version of toString() */ + abstract string descriptiveString(); + /** Gets the minimum number of parameters that can be correctly passed to this function */ abstract int minParameters(); /** Gets the maximum number of parameters that can be correctly passed to this function */ abstract int maxParameters(); - predicate isOverridingMethod() { - exists(Value f | this.overrides(f)) - } + predicate isOverridingMethod() { exists(Value f | this.overrides(f)) } - predicate isOverriddenMethod() { - exists(Value f | f.overrides(this)) - } + predicate isOverriddenMethod() { exists(Value f | f.overrides(this)) } /** Whether `name` is a legal argument name for this function */ bindingset[name] @@ -621,24 +573,40 @@ abstract class FunctionValue extends CallableValue { this.getScope().hasKwArg() } - /** Whether this is a "normal" method, that is, it is exists as a class attribute - * which is not a lambda and not the __new__ method. */ + /** + * Whether this is a "normal" method, that is, it is exists as a class attribute + * which is not a lambda and not the __new__ method. + */ predicate isNormalMethod() { exists(ClassValue cls, string name | cls.declaredAttribute(name) = this and name != "__new__" and - exists(Expr expr, AstNode origin | expr.pointsTo(this, origin) | - not origin instanceof Lambda - ) + exists(Expr expr, AstNode origin | expr.pointsTo(this, origin) | not origin instanceof Lambda) ) } + + /** Gets a class that may be raised by this function */ + abstract ClassValue getARaisedType(); + + /** Gets a class that this function may return */ + ClassValue getAnInferredReturnType() { + result = TBuiltinClassObject(this.(BuiltinFunctionObjectInternal).getReturnType()) + or + result = TBuiltinClassObject(this.(BuiltinMethodObjectInternal).getReturnType()) + } } /** Class representing Python functions */ class PythonFunctionValue extends FunctionValue { + PythonFunctionValue() { this instanceof PythonFunctionObjectInternal } - PythonFunctionValue() { - this instanceof PythonFunctionObjectInternal + override string descriptiveString() { + if this.getScope().isMethod() + then + exists(Class cls | this.getScope().getScope() = cls | + result = "method " + this.getQualifiedName() + ) + else result = "function " + this.getQualifiedName() } override string getQualifiedName() { @@ -655,10 +623,9 @@ class PythonFunctionValue extends FunctionValue { override int maxParameters() { exists(Function f | f = this.getScope() and - if exists(f.getVararg()) then - result = 2147483647 // INT_MAX - else - result = count(f.getAnArg()) + if exists(f.getVararg()) + then result = 2147483647 // INT_MAX + else result = count(f.getAnArg()) ) } @@ -667,18 +634,19 @@ class PythonFunctionValue extends FunctionValue { result = this.getScope().getAReturnValueFlowNode() } + override ClassValue getARaisedType() { + scope_raises(result, this.getScope()) + } + } /** Class representing builtin functions, such as `len` or `print` */ class BuiltinFunctionValue extends FunctionValue { + BuiltinFunctionValue() { this instanceof BuiltinFunctionObjectInternal } - BuiltinFunctionValue() { - this instanceof BuiltinFunctionObjectInternal - } + override string getQualifiedName() { result = this.(BuiltinFunctionObjectInternal).getName() } - override string getQualifiedName() { - result = this.(BuiltinFunctionObjectInternal).getName() - } + override string descriptiveString() { result = "builtin-function " + this.getName() } override int minParameters() { none() @@ -687,14 +655,16 @@ class BuiltinFunctionValue extends FunctionValue { override int maxParameters() { none() } + + override ClassValue getARaisedType() { + /* Information is unavailable for C code in general */ + none() + } } /** Class representing builtin methods, such as `list.append` or `set.add` */ class BuiltinMethodValue extends FunctionValue { - - BuiltinMethodValue() { - this instanceof BuiltinMethodObjectInternal - } + BuiltinMethodValue() { this instanceof BuiltinMethodObjectInternal } override string getQualifiedName() { exists(Builtin cls | @@ -704,6 +674,8 @@ class BuiltinMethodValue extends FunctionValue { ) } + override string descriptiveString() { result = "builtin-method " + this.getQualifiedName() } + override int minParameters() { none() } @@ -712,38 +684,33 @@ class BuiltinMethodValue extends FunctionValue { none() } + override ClassValue getARaisedType() { + /* Information is unavailable for C code in general */ + none() + } + } -/** A class representing sequence objects with a length and tracked items. +/** + * A class representing sequence objects with a length and tracked items. */ class SequenceValue extends Value { + SequenceValue() { this instanceof SequenceObjectInternal } - SequenceValue() { - this instanceof SequenceObjectInternal - } - - Value getItem(int n) { - result = this.(SequenceObjectInternal).getItem(n) - } - - int length() { - result = this.(SequenceObjectInternal).length() - } + Value getItem(int n) { result = this.(SequenceObjectInternal).getItem(n) } + int length() { result = this.(SequenceObjectInternal).length() } } /** A class representing tuple objects */ class TupleValue extends SequenceValue { - - TupleValue() { - this instanceof TupleObjectInternal - } - + TupleValue() { this instanceof TupleObjectInternal } } -/** A class representing strings, either present in the source as a literal, or -in a builtin as a value. */ - +/** + * A class representing strings, either present in the source as a literal, or + * in a builtin as a value. + */ class StringValue extends Value { StringValue() { this instanceof BytesObjectInternal or @@ -757,8 +724,9 @@ class StringValue extends Value { } } -/** A class representing numbers (ints and floats), either present in the source as a literal, - * or in a builtin as a value. +/** + * A class representing numbers (ints and floats), either present in the source as a literal, + * or in a builtin as a value. */ class NumericValue extends Value { NumericValue() { @@ -767,102 +735,66 @@ class NumericValue extends Value { } /** Gets the integer-value if it is a constant integer, and it fits in a QL int */ - int getIntValue() { - result = this.(IntObjectInternal).intValue() - } + int getIntValue() { result = this.(IntObjectInternal).intValue() } /** Gets the float-value if it is a constant float */ - int getFloatValue() { - result = this.(FloatObjectInternal).floatValue() - } + int getFloatValue() { result = this.(FloatObjectInternal).floatValue() } } -/** A Python property: - * @property - * def f(): +/** + * A Python property: + * + * @property def f(): * .... * * https://docs.python.org/3/howto/descriptor.html#properties * https://docs.python.org/3/library/functions.html#property */ class PropertyValue extends Value { + PropertyValue() { this instanceof PropertyInternal } - PropertyValue() { - this instanceof PropertyInternal - } + CallableValue getGetter() { result = this.(PropertyInternal).getGetter() } - CallableValue getGetter(){ - result = this.(PropertyInternal).getGetter() - } - - CallableValue getSetter(){ - result = this.(PropertyInternal).getSetter() - } - - CallableValue getDeleter(){ - result = this.(PropertyInternal).getDeleter() - } + CallableValue getSetter() { result = this.(PropertyInternal).getSetter() } + CallableValue getDeleter() { result = this.(PropertyInternal).getDeleter() } } /** A method-resolution-order sequence of classes */ class MRO extends TClassList { - - string toString() { - result = this.(ClassList).toString() - } + string toString() { result = this.(ClassList).toString() } /** Gets the `n`th class in this MRO */ - ClassValue getItem(int n) { - result = this.(ClassList).getItem(n) - } + ClassValue getItem(int n) { result = this.(ClassList).getItem(n) } /** Holds if any class in this MRO declares the attribute `name` */ - predicate declares(string name) { - this.(ClassList).declares(name) - } + predicate declares(string name) { this.(ClassList).declares(name) } /** Gets the length of this MRO */ - int length() { - result = this.(ClassList).length() - } + int length() { result = this.(ClassList).length() } /** Holds if this MRO contains `cls` */ - predicate contains(ClassValue cls) { - this.(ClassList).contains(cls) - } + predicate contains(ClassValue cls) { this.(ClassList).contains(cls) } /** Gets the value from scanning for the attribute `name` in this MRO. */ - Value lookup(string name) { - this.(ClassList).lookup(name, result, _) - } + Value lookup(string name) { this.(ClassList).lookup(name, result, _) } - /** Gets the MRO formed by removing all classes before `cls` + /** + * Gets the MRO formed by removing all classes before `cls` * from this MRO. */ - MRO startingAt(ClassValue cls) { - result = this.(ClassList).startingAt(cls) - } - + MRO startingAt(ClassValue cls) { result = this.(ClassList).startingAt(cls) } } - module ClassValue { - /** Get the `ClassValue` for the `bool` class. */ - ClassValue bool() { - result = TBuiltinClassObject(Builtin::special("bool")) - } + ClassValue bool() { result = TBuiltinClassObject(Builtin::special("bool")) } /** Get the `ClassValue` for the `tuple` class. */ - ClassValue tuple() { - result = TBuiltinClassObject(Builtin::special("tuple")) - } + ClassValue tuple() { result = TBuiltinClassObject(Builtin::special("tuple")) } /** Get the `ClassValue` for the `list` class. */ - ClassValue list() { - result = TBuiltinClassObject(Builtin::special("list")) - } + ClassValue list() { result = TBuiltinClassObject(Builtin::special("list")) } /** Get the `ClassValue` for `xrange` (Python 2), or `range` (only Python 3) */ ClassValue range() { @@ -872,115 +804,74 @@ module ClassValue { } /** Get the `ClassValue` for the `dict` class. */ - ClassValue dict() { - result = TBuiltinClassObject(Builtin::special("dict")) - } + ClassValue dict() { result = TBuiltinClassObject(Builtin::special("dict")) } /** Get the `ClassValue` for the `set` class. */ - ClassValue set() { - result = TBuiltinClassObject(Builtin::special("set")) - } + ClassValue set() { result = TBuiltinClassObject(Builtin::special("set")) } /** Get the `ClassValue` for the `object` class. */ - ClassValue object() { - result = TBuiltinClassObject(Builtin::special("object")) - } + ClassValue object() { result = TBuiltinClassObject(Builtin::special("object")) } /** Get the `ClassValue` for the `int` class. */ - ClassValue int_() { - result = TBuiltinClassObject(Builtin::special("int")) - } + ClassValue int_() { result = TBuiltinClassObject(Builtin::special("int")) } /** Get the `ClassValue` for the `long` class. */ - ClassValue long() { - result = TBuiltinClassObject(Builtin::special("long")) - } + ClassValue long() { result = TBuiltinClassObject(Builtin::special("long")) } /** Get the `ClassValue` for the `float` class. */ - ClassValue float_() { - result = TBuiltinClassObject(Builtin::special("float")) - } + ClassValue float_() { result = TBuiltinClassObject(Builtin::special("float")) } /** Get the `ClassValue` for the `complex` class. */ - ClassValue complex() { - result = TBuiltinClassObject(Builtin::special("complex")) - } - + ClassValue complex() { result = TBuiltinClassObject(Builtin::special("complex")) } + /** Get the `ClassValue` for the `bytes` class (also called `str` in Python 2). */ - ClassValue bytes() { - result = TBuiltinClassObject(Builtin::special("bytes")) - } + ClassValue bytes() { result = TBuiltinClassObject(Builtin::special("bytes")) } - /** Get the `ClassValue` for the class of unicode strings. - * `str` in Python 3 and `unicode` in Python 2. */ - ClassValue unicode() { - result = TBuiltinClassObject(Builtin::special("unicode")) - } + /** + * Get the `ClassValue` for the class of unicode strings. + * `str` in Python 3 and `unicode` in Python 2. + */ + ClassValue unicode() { result = TBuiltinClassObject(Builtin::special("unicode")) } - /** Get the `ClassValue` for the `str` class. This is `bytes` in Python 2, - and `str` in Python 3. */ - ClassValue str() { - if major_version() = 2 then - result = bytes() - else - result = unicode() - } + /** + * Get the `ClassValue` for the `str` class. This is `bytes` in Python 2, + * and `str` in Python 3. + */ + ClassValue str() { if major_version() = 2 then result = bytes() else result = unicode() } /** Get the `ClassValue` for the `property` class. */ - ClassValue property() { - result = TBuiltinClassObject(Builtin::special("property")) - } + ClassValue property() { result = TBuiltinClassObject(Builtin::special("property")) } /** Get the `ClassValue` for the class of Python functions. */ - ClassValue functionType() { - result = TBuiltinClassObject(Builtin::special("FunctionType")) - } + ClassValue functionType() { result = TBuiltinClassObject(Builtin::special("FunctionType")) } /** Get the `ClassValue` for the class of builtin functions. */ - ClassValue builtinFunction() { - result = Value::named("len").getClass() - } + ClassValue builtinFunction() { result = Value::named("len").getClass() } /** Get the `ClassValue` for the `generatorType` class. */ - ClassValue generator() { - result = TBuiltinClassObject(Builtin::special("generator")) - } + ClassValue generator() { result = TBuiltinClassObject(Builtin::special("generator")) } /** Get the `ClassValue` for the `type` class. */ - ClassValue type() { - result = TType() - } + ClassValue type() { result = TType() } /** Get the `ClassValue` for `ClassType`. */ - ClassValue classType() { - result = TBuiltinClassObject(Builtin::special("ClassType")) - } + ClassValue classType() { result = TBuiltinClassObject(Builtin::special("ClassType")) } /** Get the `ClassValue` for `InstanceType`. */ - ClassValue instanceType() { - result = TBuiltinClassObject(Builtin::special("InstanceType")) - } + ClassValue instanceType() { result = TBuiltinClassObject(Builtin::special("InstanceType")) } /** Get the `ClassValue` for `super`. */ - ClassValue super_() { - result = TBuiltinClassObject(Builtin::special("super")) - } + ClassValue super_() { result = TBuiltinClassObject(Builtin::special("super")) } /** Get the `ClassValue` for the `classmethod` class. */ - ClassValue classmethod() { - result = TBuiltinClassObject(Builtin::special("ClassMethod")) - } + ClassValue classmethod() { result = TBuiltinClassObject(Builtin::special("ClassMethod")) } /** Get the `ClassValue` for the `staticmethod` class. */ - ClassValue staticmethod() { - result = TBuiltinClassObject(Builtin::special("StaticMethod")) - } + ClassValue staticmethod() { result = TBuiltinClassObject(Builtin::special("StaticMethod")) } /** Get the `ClassValue` for the `MethodType` class. */ - pragma [noinline] - ClassValue methodType() { - result = TBuiltinClassObject(Builtin::special("MethodType")) - } + pragma[noinline] + ClassValue methodType() { result = TBuiltinClassObject(Builtin::special("MethodType")) } /** Get the `ClassValue` for the `MethodDescriptorType` class. */ ClassValue methodDescriptorType() { @@ -993,59 +884,42 @@ module ClassValue { } /** Get the `ClassValue` for the `StopIteration` class. */ - ClassValue stopIteration() { - result = TBuiltinClassObject(Builtin::builtin("StopIteration")) - } + ClassValue stopIteration() { result = TBuiltinClassObject(Builtin::builtin("StopIteration")) } /** Get the `ClassValue` for the class of modules. */ - ClassValue module_() { - result = TBuiltinClassObject(Builtin::special("ModuleType")) - } + ClassValue module_() { result = TBuiltinClassObject(Builtin::special("ModuleType")) } /** Get the `ClassValue` for the `Exception` class. */ - ClassValue exception() { - result = TBuiltinClassObject(Builtin::special("Exception")) - } + ClassValue exception() { result = TBuiltinClassObject(Builtin::special("Exception")) } /** Get the `ClassValue` for the `BaseException` class. */ - ClassValue baseException() { - result = TBuiltinClassObject(Builtin::special("BaseException")) - } + ClassValue baseException() { result = TBuiltinClassObject(Builtin::special("BaseException")) } /** Get the `ClassValue` for the `NoneType` class. */ - ClassValue nonetype() { - result = TBuiltinClassObject(Builtin::special("NoneType")) - } + ClassValue nonetype() { result = TBuiltinClassObject(Builtin::special("NoneType")) } /** Get the `ClassValue` for the `TypeError` class */ - ClassValue typeError() { - result = TBuiltinClassObject(Builtin::special("TypeError")) - } + ClassValue typeError() { result = TBuiltinClassObject(Builtin::special("TypeError")) } /** Get the `ClassValue` for the `NameError` class. */ - ClassValue nameError() { - result = TBuiltinClassObject(Builtin::builtin("NameError")) - } + ClassValue nameError() { result = TBuiltinClassObject(Builtin::builtin("NameError")) } /** Get the `ClassValue` for the `AttributeError` class. */ - ClassValue attributeError() { - result = TBuiltinClassObject(Builtin::builtin("AttributeError")) - } + ClassValue attributeError() { result = TBuiltinClassObject(Builtin::builtin("AttributeError")) } /** Get the `ClassValue` for the `KeyError` class. */ - ClassValue keyError() { - result = TBuiltinClassObject(Builtin::builtin("KeyError")) - } + ClassValue keyError() { result = TBuiltinClassObject(Builtin::builtin("KeyError")) } /** Get the `ClassValue` for the `LookupError` class. */ - ClassValue lookupError() { - result = TBuiltinClassObject(Builtin::builtin("LookupError")) - } + ClassValue lookupError() { result = TBuiltinClassObject(Builtin::builtin("LookupError")) } - /** Get the `ClassValue` for the `IOError` class. */ - ClassValue ioError() { - result = TBuiltinClassObject(Builtin::builtin("IOError")) + /** Get the `ClassValue` for the `IndexError` class. */ + ClassValue indexError() { + result = TBuiltinClassObject(Builtin::builtin("IndexError")) } + + /** Get the `ClassValue` for the `IOError` class. */ + ClassValue ioError() { result = TBuiltinClassObject(Builtin::builtin("IOError")) } /** Get the `ClassValue` for the `NotImplementedError` class. */ ClassValue notImplementedError() { @@ -1053,9 +927,7 @@ module ClassValue { } /** Get the `ClassValue` for the `ImportError` class. */ - ClassValue importError() { - result = TBuiltinClassObject(Builtin::builtin("ImportError")) - } + ClassValue importError() { result = TBuiltinClassObject(Builtin::builtin("ImportError")) } /** Get the `ClassValue` for the `UnicodeEncodeError` class. */ ClassValue unicodeEncodeError() { @@ -1067,4 +939,9 @@ module ClassValue { result = TBuiltinClassObject(Builtin::builtin("UnicodeDecodeError")) } + /** Get the `ClassValue` for the `SystemExit` class. */ + ClassValue systemExit() { + result = TBuiltinClassObject(Builtin::builtin("SystemExit")) + } + } From 161613f59efe6b12ed1a6d35cea9545e90a6b949 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Thu, 2 Apr 2020 12:17:14 -0700 Subject: [PATCH 04/16] Python: ObjectAPI to ValueAPI: WrongNumberArgumentsInCall: Adds new version of Exceptions.qll --- .../ql/src/semmle/python/types/Exceptions.qll | 388 ++++++++++++------ 1 file changed, 258 insertions(+), 130 deletions(-) diff --git a/python/ql/src/semmle/python/types/Exceptions.qll b/python/ql/src/semmle/python/types/Exceptions.qll index 2ed034da77b..9de373f73d4 100644 --- a/python/ql/src/semmle/python/types/Exceptions.qll +++ b/python/ql/src/semmle/python/types/Exceptions.qll @@ -1,6 +1,6 @@ /** * Analysis of exception raising and handling. - * + * * In order to make this useful we make a number of assumptions. These are: * 1. Typing errors (TypeError, NameError, AttributeError) are assumed to occur only if: * a) Explicitly raised, e.g. raise TypeError() @@ -15,7 +15,6 @@ import python /** Subset of ControlFlowNodes which might raise an exception */ class RaisingNode extends ControlFlowNode { - RaisingNode() { exists(this.getAnExceptionalSuccessor()) or @@ -24,64 +23,104 @@ class RaisingNode extends ControlFlowNode { /** Gets the CFG node for the exception, if and only if this RaisingNode is an explicit raise */ ControlFlowNode getExceptionNode() { - exists(Raise r | - r = this.getNode() and result.getNode() = r.getRaised() and + exists(Raise r | + r = this.getNode() and + result.getNode() = r.getRaised() and result.getBasicBlock().dominates(this.getBasicBlock()) ) } - private predicate quits() { - this.(CallNode).getFunction().refersTo(Object::quitter(_)) + private predicate quits() { this.(CallNode).getFunction().refersTo(Object::quitter(_)) } + + /** + * Gets the type of an exception that may be raised + * at this control flow node + */ + ClassObject getARaisedType_objectapi() { + result = this.localRaisedType_objectapi() + or + exists(FunctionObject func | this = func.getACall() | result = func.getARaisedType()) + or + result = systemExitRaise_objectapi() } - /** Gets the type of an exception that may be raised - at this control flow node */ - ClassObject getARaisedType() { + /** + * Gets the type of an exception that may be raised + * at this control flow node + */ + ClassValue getARaisedType() { result = this.localRaisedType() or - exists(FunctionObject func | - this = func.getACall() | - result = func.getARaisedType() - ) + exists(FunctionValue func | this = func.getACall() | result = func.getARaisedType()) or result = systemExitRaise() } pragma[noinline] - private ClassObject systemExitRaise() { + private ClassObject systemExitRaise_objectapi() { this.quits() and result = Object::builtin("SystemExit") } - pragma [noinline, nomagic] - private ClassObject localRaisedType() { - result.isSubclassOf(theBaseExceptionType()) - and + pragma[noinline] + private ClassValue systemExitRaise() { this.quits() and result = ClassValue::systemExit() } + + pragma[noinline, nomagic] + private ClassObject localRaisedType_objectapi() { + result.isSubclassOf(theBaseExceptionType()) and ( - exists(ControlFlowNode ex | - ex = this.getExceptionNode() and - (ex.refersTo(result) or ex.refersTo(_, result, _)) - ) - or - this.getNode() instanceof ImportExpr and result = Object::builtin("ImportError") - or - this.getNode() instanceof Print and result = theIOErrorType() - or - exists(ExceptFlowNode except | - except = this.getAnExceptionalSuccessor() and - except.handles(result) and - result = this.innateException() - ) - or - not exists(ExceptFlowNode except | except = this.getAnExceptionalSuccessor()) - and - sequence_or_mapping(this) and result = theLookupErrorType() - or - this.read_write_call() and result = theIOErrorType() + exists(ControlFlowNode ex | + ex = this.getExceptionNode() and + (ex.refersTo(result) or ex.refersTo(_, result, _)) + ) + or + this.getNode() instanceof ImportExpr and result = Object::builtin("ImportError") + or + this.getNode() instanceof Print and result = theIOErrorType() + or + exists(ExceptFlowNode except | + except = this.getAnExceptionalSuccessor() and + except.handles_objectapi(result) and + result = this.innateException_objectapi() + ) + or + not exists(ExceptFlowNode except | except = this.getAnExceptionalSuccessor()) and + sequence_or_mapping(this) and + result = theLookupErrorType() + or + this.read_write_call() and result = theIOErrorType() ) } - pragma [noinline] - ClassObject innateException() { + pragma[noinline, nomagic] + private ClassValue localRaisedType() { + result.getASuperType() = ClassValue::baseException() and + ( + exists(ControlFlowNode ex | + ex = this.getExceptionNode() and + (ex.pointsTo(result) or ex.pointsTo(_, result, _)) + ) + or + this.getNode() instanceof ImportExpr and result = ClassValue::importError() + or + this.getNode() instanceof Print and result = ClassValue::ioError() + or + exists(ExceptFlowNode except | + except = this.getAnExceptionalSuccessor() and + except.handles(result) and + result = this.innateException() + ) + or + not exists(ExceptFlowNode except | except = this.getAnExceptionalSuccessor()) and + sequence_or_mapping(this) and + result = ClassValue::lookupError() + or + this.read_write_call() and result = ClassValue::ioError() + ) + } + + /** Holds if this is an innate exception (AttributeError, NameError, IndexError, or KeyError). */ + pragma[noinline] + ClassObject innateException_objectapi() { this.getNode() instanceof Attribute and result = theAttributeErrorType() or this.getNode() instanceof Name and result = theNameErrorType() @@ -91,38 +130,48 @@ class RaisingNode extends ControlFlowNode { this.getNode() instanceof Subscript and result = theKeyErrorType() } - /** Whether this control flow node raises an exception, - * but the type of the exception it raises cannot be inferred. */ + /** Holds if this is an innate exception (AttributeError, NameError, IndexError, or KeyError). */ + pragma[noinline] + ClassValue innateException() { + this.getNode() instanceof Attribute and result = ClassValue::attributeError() + or + this.getNode() instanceof Name and result = ClassValue::nameError() + or + this.getNode() instanceof Subscript and result = ClassValue::indexError() + or + this.getNode() instanceof Subscript and result = ClassValue::keyError() + } + + /** + * Whether this control flow node raises an exception, + * but the type of the exception it raises cannot be inferred. + */ predicate raisesUnknownType() { /* read/write calls are assumed to raise IOError (OSError for Py3) */ - not this.read_write_call() - and + not this.read_write_call() and ( - /* Call to an unknown object */ - this.getNode() instanceof Call and not exists(FunctionObject func | this = func.getACall()) - and not exists(ClassObject known | this.(CallNode).getFunction().refersTo(known)) - or - this.getNode() instanceof Exec - or - /* Call to a function raising an unknown type */ - exists(FunctionObject func | - this = func.getACall() | - func.raisesUnknownType() - ) + /* Call to an unknown object */ + this.getNode() instanceof Call and + not exists(FunctionObject func | this = func.getACall()) and + not exists(ClassObject known | this.(CallNode).getFunction().refersTo(known)) + or + this.getNode() instanceof Exec + or + /* Call to a function raising an unknown type */ + exists(FunctionObject func | this = func.getACall() | func.raisesUnknownType()) ) } private predicate read_write_call() { exists(string mname | mname = this.(CallNode).getFunction().(AttrNode).getName() | - mname = "read" or mname = "write" + mname = "read" or mname = "write" ) } - /** Whether (as inferred by type inference) it is highly unlikely (or impossible) for control to flow from this to succ. - */ + /** Whether (as inferred by type inference) it is highly unlikely (or impossible) for control to flow from this to succ. */ predicate unlikelySuccessor(ControlFlowNode succ) { succ = this.getAnExceptionalSuccessor() and - not this.viableExceptionEdge(succ, _) and + not this.viableExceptionEdge_objectapi(succ, _) and not this.raisesUnknownType() or exists(FunctionObject func | @@ -140,84 +189,159 @@ class RaisingNode extends ControlFlowNode { } /** Whether it is considered plausible that 'raised' can be raised across the edge this-succ */ - predicate viableExceptionEdge(ControlFlowNode succ, ClassObject raised) { + predicate viableExceptionEdge_objectapi(ControlFlowNode succ, ClassObject raised) { raised.isLegalExceptionType() and - raised = this.getARaisedType() and + raised = this.getARaisedType_objectapi() and succ = this.getAnExceptionalSuccessor() and - ( + ( /* An 'except' that handles raised and there is no more previous handler */ - ((ExceptFlowNode)succ).handles(raised) and - not exists(ExceptFlowNode other, StmtList s, int i, int j | - not other = succ and other.handles(raised) and - s.getItem(i) = succ.getNode() and s.getItem(j) = other.getNode() - | + succ.(ExceptFlowNode).handles_objectapi(raised) and + not exists(ExceptFlowNode other, StmtList s, int i, int j | + not other = succ and + other.handles_objectapi(raised) and + s.getItem(i) = succ.getNode() and + s.getItem(j) = other.getNode() + | j < i ) or /* Any successor that is not an 'except', provided that 'raised' is not handled by a different successor. */ - (not ((ExceptFlowNode)this.getAnExceptionalSuccessor()).handles(raised) and - not succ instanceof ExceptFlowNode) + not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles_objectapi(raised) and + not succ instanceof ExceptFlowNode ) } - /** Whether this exceptional exit is viable. That is, is it + /** Whether it is considered plausible that 'raised' can be raised across the edge this-succ */ + predicate viableExceptionEdge(ControlFlowNode succ, ClassValue raised) { + raised.isLegalExceptionType() and + raised = this.getARaisedType() and + succ = this.getAnExceptionalSuccessor() and + ( + /* An 'except' that handles raised and there is no more previous handler */ + succ.(ExceptFlowNode).handles(raised) and + not exists(ExceptFlowNode other, StmtList s, int i, int j | + not other = succ and + other.handles(raised) and + s.getItem(i) = succ.getNode() and + s.getItem(j) = other.getNode() + | + j < i + ) + or + /* Any successor that is not an 'except', provided that 'raised' is not handled by a different successor. */ + not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles(raised) and + not succ instanceof ExceptFlowNode + ) + } + + /** + * Whether this exceptional exit is viable. That is, is it * plausible that the scope `s` can be exited with exception `raised` * at this point. */ - predicate viableExceptionalExit(Scope s, ClassObject raised) { + predicate viableExceptionalExit_objectapi(Scope s, ClassObject raised) { + raised.isLegalExceptionType() and + raised = this.getARaisedType_objectapi() and + this.isExceptionalExit(s) and + not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles_objectapi(raised) + } + + /** + * Whether this exceptional exit is viable. That is, is it + * plausible that the scope `s` can be exited with exception `raised` + * at this point. + */ + predicate viableExceptionalExit(Scope s, ClassValue raised) { raised.isLegalExceptionType() and raised = this.getARaisedType() and this.isExceptionalExit(s) and - not ((ExceptFlowNode)this.getAnExceptionalSuccessor()).handles(raised) + not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles(raised) } - } /** Is this a sequence or mapping subscript x[i]? */ -private predicate sequence_or_mapping(RaisingNode r) { - r.getNode() instanceof Subscript +private predicate sequence_or_mapping(RaisingNode r) { r.getNode() instanceof Subscript } + +private predicate current_exception_objectapi(ClassObject ex, BasicBlock b) { + exists(RaisingNode r | + r.viableExceptionEdge_objectapi(b.getNode(0), ex) and not b.getNode(0) instanceof ExceptFlowNode + ) + or + exists(BasicBlock prev | + current_exception_objectapi(ex, prev) and + exists(ControlFlowNode pred, ControlFlowNode succ | + pred = prev.getLastNode() and succ = b.getNode(0) + | + pred.getASuccessor() = succ and + ( + /* Normal control flow */ + not pred.getAnExceptionalSuccessor() = succ + or + /* Re-raise the current exception, propagating to the successor */ + pred instanceof ReraisingNode + ) + ) + ) } -private predicate current_exception(ClassObject ex, BasicBlock b) { +private predicate current_exception(ClassValue ex, BasicBlock b) { exists(RaisingNode r | r.viableExceptionEdge(b.getNode(0), ex) and not b.getNode(0) instanceof ExceptFlowNode ) or - exists(BasicBlock prev | + exists(BasicBlock prev | current_exception(ex, prev) and exists(ControlFlowNode pred, ControlFlowNode succ | - pred = prev.getLastNode() and succ = b.getNode(0) | - pred.getASuccessor() = succ and - (/* Normal control flow */ - not pred.getAnExceptionalSuccessor() = succ or - /* Re-raise the current exception, propagating to the successor */ - pred instanceof ReraisingNode) + pred = prev.getLastNode() and succ = b.getNode(0) + | + pred.getASuccessor() = succ and + ( + /* Normal control flow */ + not pred.getAnExceptionalSuccessor() = succ + or + /* Re-raise the current exception, propagating to the successor */ + pred instanceof ReraisingNode + ) ) ) } private predicate unknown_current_exception(BasicBlock b) { exists(RaisingNode r | - r.raisesUnknownType() and + r.raisesUnknownType() and r.getAnExceptionalSuccessor() = b.getNode(0) and not b.getNode(0) instanceof ExceptFlowNode ) or - exists(BasicBlock prev | + exists(BasicBlock prev | unknown_current_exception(prev) and exists(ControlFlowNode pred, ControlFlowNode succ | - pred = prev.getLastNode() and succ = b.getNode(0) | - pred.getASuccessor() = succ and + pred = prev.getLastNode() and succ = b.getNode(0) + | + pred.getASuccessor() = succ and (not pred.getAnExceptionalSuccessor() = succ or pred instanceof ReraisingNode) ) ) } /** INTERNAL -- Use FunctionObject.getARaisedType() instead */ -predicate scope_raises(ClassObject ex, Scope s) { - exists(BasicBlock b | +predicate scope_raises_objectapi(ClassObject ex, Scope s) { + exists(BasicBlock b | + current_exception_objectapi(ex, b) and + b.getLastNode().isExceptionalExit(s) + | + b.getLastNode() instanceof ReraisingNode + ) + or + exists(RaisingNode r | r.viableExceptionalExit_objectapi(s, ex)) +} + +/** INTERNAL -- Use FunctionObject.getARaisedType() instead */ +predicate scope_raises(ClassValue ex, Scope s) { + exists(BasicBlock b | current_exception(ex, b) and - b.getLastNode().isExceptionalExit(s) | + b.getLastNode().isExceptionalExit(s) + | b.getLastNode() instanceof ReraisingNode ) or @@ -226,9 +350,10 @@ predicate scope_raises(ClassObject ex, Scope s) { /** INTERNAL -- Use FunctionObject.raisesUnknownType() instead */ predicate scope_raises_unknown(Scope s) { - exists(BasicBlock b | - b.getLastNode() instanceof ReraisingNode - and b.getLastNode().isExceptionalExit(s) | + exists(BasicBlock b | + b.getLastNode() instanceof ReraisingNode and + b.getLastNode().isExceptionalExit(s) + | unknown_current_exception(b) ) or @@ -238,117 +363,120 @@ predicate scope_raises_unknown(Scope s) { ) } - /** ControlFlowNode for an 'except' statement. */ class ExceptFlowNode extends ControlFlowNode { - - ExceptFlowNode() { - this.getNode() instanceof ExceptStmt - } + ExceptFlowNode() { this.getNode() instanceof ExceptStmt } ControlFlowNode getType() { exists(ExceptStmt ex | this.getBasicBlock().dominates(result.getBasicBlock()) and - ex = this.getNode() and result = ex.getType().getAFlowNode() + ex = this.getNode() and + result = ex.getType().getAFlowNode() ) } ControlFlowNode getName() { exists(ExceptStmt ex | this.getBasicBlock().dominates(result.getBasicBlock()) and - ex = this.getNode() and result = ex.getName().getAFlowNode() + ex = this.getNode() and + result = ex.getName().getAFlowNode() ) } private predicate handledObject_objectapi(Object obj, ClassObject cls, ControlFlowNode origin) { this.getType().refersTo(obj, cls, origin) or - exists(Object tup | - this.handledObject_objectapi(tup, theTupleType(), _) | + exists(Object tup | this.handledObject_objectapi(tup, theTupleType(), _) | element_from_tuple_objectapi(tup).refersTo(obj, cls, origin) ) } - + private predicate handledObject(Value val, ClassValue cls, ControlFlowNode origin) { val.getClass() = cls and ( this.getType().pointsTo(val, origin) or - exists(TupleValue tup | - this.handledObject(tup, ClassValue::tuple(), _) | + exists(TupleValue tup | this.handledObject(tup, ClassValue::tuple(), _) | val = tup.getItem(_) and origin = val.getOrigin() ) ) } /** Gets the inferred type(s) that are handled by this node, splitting tuples if possible. */ - pragma [noinline] + pragma[noinline] predicate handledException_objectapi(Object obj, ClassObject cls, ControlFlowNode origin) { this.handledObject_objectapi(obj, cls, origin) and not cls = theTupleType() or - not exists(this.getNode().(ExceptStmt).getType()) and obj = theBaseExceptionType() and cls = theTypeType() and + not exists(this.getNode().(ExceptStmt).getType()) and + obj = theBaseExceptionType() and + cls = theTypeType() and origin = this } - + /** Gets the inferred type(s) that are handled by this node, splitting tuples if possible. */ - pragma [noinline] + pragma[noinline] predicate handledException(Value val, ClassValue cls, ControlFlowNode origin) { this.handledObject(val, cls, origin) and not cls = ClassValue::tuple() or - not exists(this.getNode().(ExceptStmt).getType()) and val = ClassValue::baseException() and cls = ClassValue::type() and + not exists(this.getNode().(ExceptStmt).getType()) and + val = ClassValue::baseException() and + cls = ClassValue::type() and origin = this } - - /** Whether this `except` handles `cls` */ - predicate handles(ClassObject cls) { - exists(ClassObject handled | - this.handledException_objectapi(handled, _, _) | + predicate handles_objectapi(ClassObject cls) { + exists(ClassObject handled | this.handledException_objectapi(handled, _, _) | cls.getAnImproperSuperType() = handled ) } + /** Whether this `except` handles `cls` */ + predicate handles(ClassValue cls) { + exists(ClassValue handled | this.handledException(handled, _, _) | + cls.getASuperType() = handled + ) + } } private ControlFlowNode element_from_tuple_objectapi(Object tuple) { - exists(Tuple t | - t = tuple.getOrigin() and result = t.getAnElt().getAFlowNode() - ) + exists(Tuple t | t = tuple.getOrigin() and result = t.getAnElt().getAFlowNode()) } -/** A Reraising node is the node at the end of a finally block (on the exceptional branch) +/** + * A Reraising node is the node at the end of a finally block (on the exceptional branch) * that reraises the current exception. */ class ReraisingNode extends RaisingNode { - ReraisingNode() { not this.getNode() instanceof Raise and in_finally(this) and - forall(ControlFlowNode succ | - succ = this.getASuccessor() | + forall(ControlFlowNode succ | succ = this.getASuccessor() | succ = this.getAnExceptionalSuccessor() ) } /** Gets a class that may be raised by this node */ - override ClassObject getARaisedType() { + override ClassObject getARaisedType_objectapi() { + exists(BasicBlock b | + current_exception_objectapi(result, b) and + b.getNode(_) = this + ) + } + + /** Gets a class that may be raised by this node */ + override ClassValue getARaisedType() { exists(BasicBlock b | current_exception(result, b) and b.getNode(_) = this ) } - } private predicate in_finally(ControlFlowNode n) { - exists(Stmt f | - exists(Try t | f = t.getAFinalstmt()) | - f = n.getNode() + exists(Stmt f | exists(Try t | f = t.getAFinalstmt()) | + f = n.getNode() or f.containsInScope(n.getNode()) ) } - - - From 2a7b77c0e174a63411bb395535a5af23af8b8885 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Thu, 2 Apr 2020 12:18:07 -0700 Subject: [PATCH 05/16] Python: ObjectAPI to ValueAPI: WrongNumberArgumentsInCall: Adds new version of FunctionObject.qll --- .../semmle/python/types/FunctionObject.qll | 248 ++++++------------ 1 file changed, 80 insertions(+), 168 deletions(-) diff --git a/python/ql/src/semmle/python/types/FunctionObject.qll b/python/ql/src/semmle/python/types/FunctionObject.qll index 4e03764933d..68e97901c6f 100644 --- a/python/ql/src/semmle/python/types/FunctionObject.qll +++ b/python/ql/src/semmle/python/types/FunctionObject.qll @@ -9,22 +9,13 @@ private import semmle.python.types.Builtins /** A function object, whether written in Python or builtin */ abstract class FunctionObject extends Object { + CallableValue theCallable() { result.(ObjectInternal).getSource() = this } - CallableValue theCallable() { - result.(ObjectInternal).getSource() = this - } + predicate isOverridingMethod() { exists(Object f | this.overrides(f)) } - predicate isOverridingMethod() { - exists(Object f | this.overrides(f)) - } + predicate isOverriddenMethod() { exists(Object f | f.overrides(this)) } - predicate isOverriddenMethod() { - exists(Object f | f.overrides(this)) - } - - Function getFunction() { - result = ((CallableExpr)this.getOrigin()).getInnerScope() - } + Function getFunction() { result = this.getOrigin().(CallableExpr).getInnerScope() } /** This function always returns None, meaning that its return value should be disregarded */ abstract predicate isProcedure(); @@ -39,17 +30,13 @@ abstract class FunctionObject extends Object { abstract predicate raisesUnknownType(); /** Use descriptiveString() instead. */ - deprecated string prettyString() { - result = this.descriptiveString() - } + deprecated string prettyString() { result = this.descriptiveString() } /** Gets a longer, more descriptive version of toString() */ abstract string descriptiveString(); /** Gets a call-site from where this function is called as a function */ - CallNode getAFunctionCall() { - result.getFunction().inferredValue() = theCallable() - } + CallNode getAFunctionCall() { result.getFunction().inferredValue() = theCallable() } /** Gets a call-site from where this function is called as a method */ CallNode getAMethodCall() { @@ -60,37 +47,38 @@ abstract class FunctionObject extends Object { } /** Gets a call-site from where this function is called */ - ControlFlowNode getACall() { - result = theCallable().getACall() - } + ControlFlowNode getACall() { result = theCallable().getACall() } /** Gets a call-site from where this function is called, given the `context` */ ControlFlowNode getACall(Context caller_context) { result = theCallable().getACall(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. - */ + /** + * 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 = 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. - */ + /** + * 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 = theCallable().getNamedArgumentForCall(call, name) } - /** Whether this function never returns. This is an approximation. + /** + * Whether this function never returns. This is an approximation. */ - predicate neverReturns() { - theCallable().neverReturns() - } + predicate neverReturns() { theCallable().neverReturns() } - /** Whether this is a "normal" method, that is, it is exists as a class attribute - * which is not wrapped and not the __new__ method. */ + /** + * Whether this is a "normal" method, that is, it is exists as a class attribute + * which is not wrapped and not the __new__ method. + */ predicate isNormalMethod() { exists(ClassObject cls, string name | cls.declaredAttribute(name) = this and @@ -113,7 +101,8 @@ abstract class FunctionObject extends Object { ) } - /** Gets the qualified name for this function object. + /** + * Gets the qualified name for this function object. * Should return the same name as the `__qualname__` attribute on functions in Python 3. */ abstract string getQualifiedName(); @@ -129,59 +118,39 @@ abstract class FunctionObject extends Object { } /** Gets a class that this function may return */ - ClassObject getAnInferredReturnType() { - result = this.(BuiltinCallable).getAReturnType() - } - - predicate isAbstract() { - this.getARaisedType() = theNotImplementedErrorType() - } + ClassObject getAnInferredReturnType() { result = this.(BuiltinCallable).getAReturnType() } + predicate isAbstract() { this.getARaisedType() = theNotImplementedErrorType() } } class PyFunctionObject extends FunctionObject { + PyFunctionObject() { any(PythonFunctionObjectInternal f).getOrigin() = this } - PyFunctionObject() { - any(PythonFunctionObjectInternal f).getOrigin() = this - } - - override string toString() { - result = "Function " + this.getName() - } + override string toString() { result = "Function " + this.getName() } override string getName() { - result = ((FunctionExpr)this.getOrigin()).getName() + result = this.getOrigin().(FunctionExpr).getName() or this.getOrigin() instanceof Lambda and result = "lambda" } /** Whether this function is a procedure, that is, it has no explicit return statement and is not a generator function */ - override predicate isProcedure() { - this.getFunction().isProcedure() - } + override predicate isProcedure() { this.getFunction().isProcedure() } - override ClassObject getARaisedType() { - scope_raises(result, this.getFunction()) - } + override ClassObject getARaisedType() { scope_raises_objectapi(result, this.getFunction()) } - override predicate raisesUnknownType() { - scope_raises_unknown(this.getFunction()) - } + override predicate raisesUnknownType() { scope_raises_unknown(this.getFunction()) } /** Gets a control flow node corresponding to the value of a return statement */ - ControlFlowNode getAReturnedNode() { - result = this.getFunction().getAReturnValueFlowNode() - } + ControlFlowNode getAReturnedNode() { result = this.getFunction().getAReturnValueFlowNode() } override string descriptiveString() { - if this.getFunction().isMethod() then ( - exists(Class cls | - this.getFunction().getScope() = cls | + if this.getFunction().isMethod() + then + exists(Class cls | this.getFunction().getScope() = cls | result = "method " + this.getQualifiedName() ) - ) else ( - result = "function " + this.getQualifiedName() - ) + else result = "function " + this.getQualifiedName() } override int minParameters() { @@ -194,16 +163,13 @@ class PyFunctionObject extends FunctionObject { override int maxParameters() { exists(Function f | f = this.getFunction() and - if exists(f.getVararg()) then - result = 2147483647 // INT_MAX - else - result = count(f.getAnArg()) + if exists(f.getVararg()) + then result = 2147483647 // INT_MAX + else result = count(f.getAnArg()) ) } - override string getQualifiedName() { - result = this.getFunction().getQualifiedName() - } + override string getQualifiedName() { result = this.getFunction().getQualifiedName() } predicate unconditionallyReturnsParameter(int n) { exists(SsaVariable pvar | @@ -220,7 +186,9 @@ class PyFunctionObject extends FunctionObject { /** Factored out to help join ordering */ private predicate implicitlyReturns(Object none_, ClassObject noneType) { - noneType = theNoneType() and not this.getFunction().isGenerator() and none_ = theNoneObject() and + noneType = theNoneType() and + not this.getFunction().isGenerator() and + none_ = theNoneObject() and ( not exists(this.getAReturnedNode()) and exists(this.getFunction().getANormalExit()) or @@ -232,9 +200,10 @@ class PyFunctionObject extends FunctionObject { override ClassObject getAnInferredReturnType() { this.getFunction().isGenerator() and result = theGeneratorType() or - not this.neverReturns() and not this.getFunction().isGenerator() and + not this.neverReturns() and + not this.getFunction().isGenerator() and ( - this.(PyFunctionObject).getAReturnedNode().refersTo( _, result, _) + this.(PyFunctionObject).getAReturnedNode().refersTo(_, result, _) or this.implicitlyReturns(_, result) ) @@ -243,18 +212,13 @@ class PyFunctionObject extends FunctionObject { ParameterDefinition getParameter(int n) { result.getDefiningNode().getNode() = this.getFunction().getArg(n) } - } abstract class BuiltinCallable extends FunctionObject { - abstract ClassObject getAReturnType(); override predicate isProcedure() { - forex(ClassObject rt | - rt = this.getAReturnType() | - rt = theNoneType() - ) + forex(ClassObject rt | rt = this.getAReturnType() | rt = theNoneType()) } abstract override string getQualifiedName(); @@ -262,18 +226,13 @@ abstract class BuiltinCallable extends FunctionObject { override ControlFlowNode getArgumentForCall(CallNode call, int n) { call = this.getACall() and result = call.getArg(n) } - } class BuiltinMethodObject extends BuiltinCallable { - - BuiltinMethodObject() { - any(BuiltinMethodObjectInternal m).getBuiltin() = this - } + BuiltinMethodObject() { any(BuiltinMethodObjectInternal m).getBuiltin() = this } override string getQualifiedName() { - exists(ClassObject cls | - cls.asBuiltin().getMember(_) = this.asBuiltin() | + exists(ClassObject cls | cls.asBuiltin().getMember(_) = this.asBuiltin() | result = cls.getName() + "." + this.getName() ) or @@ -281,17 +240,11 @@ class BuiltinMethodObject extends BuiltinCallable { result = this.getName() } - override string descriptiveString() { - result = "builtin-method " + this.getQualifiedName() - } + override string descriptiveString() { result = "builtin-method " + this.getQualifiedName() } - override string getName() { - result = this.asBuiltin().getName() - } + override string getName() { result = this.asBuiltin().getName() } - override string toString() { - result = "Builtin-method " + this.getName() - } + override string toString() { result = "Builtin-method " + this.getName() } override ClassObject getARaisedType() { /* Information is unavailable for C code in general */ @@ -303,41 +256,23 @@ class BuiltinMethodObject extends BuiltinCallable { any() } - override int minParameters() { - none() - } + override int minParameters() { none() } - override int maxParameters() { - none() - } - - override ClassObject getAReturnType() { - ext_rettype(this.asBuiltin(), result.asBuiltin()) - } + override int maxParameters() { none() } + override ClassObject getAReturnType() { ext_rettype(this.asBuiltin(), result.asBuiltin()) } } class BuiltinFunctionObject extends BuiltinCallable { + BuiltinFunctionObject() { any(BuiltinFunctionObjectInternal f).getBuiltin() = this } - BuiltinFunctionObject() { - any(BuiltinFunctionObjectInternal f).getBuiltin() = this - } + override string getName() { result = this.asBuiltin().getName() } - override string getName() { - result = this.asBuiltin().getName() - } + override string getQualifiedName() { result = this.getName() } - override string getQualifiedName() { - result = this.getName() - } + override string toString() { result = "Builtin-function " + this.getName() } - override string toString() { - result = "Builtin-function " + this.getName() - } - - override string descriptiveString() { - result = "builtin-function " + this.getName() - } + override string descriptiveString() { result = "builtin-function " + this.getName() } override ClassObject getARaisedType() { /* Information is unavailable for C code in general */ @@ -350,16 +285,19 @@ class BuiltinFunctionObject extends BuiltinCallable { } override ClassObject getAReturnType() { - /* Enumerate the types of a few builtin functions, that the CPython analysis misses. - */ + /* + * Enumerate the types of a few builtin functions, that the CPython analysis misses. + */ + this = Object::builtin("hex") and result = theStrType() or this = Object::builtin("oct") and result = theStrType() or this = Object::builtin("intern") and result = theStrType() or - /* Fix a few minor inaccuracies in the CPython analysis */ - ext_rettype(this.asBuiltin(), result.asBuiltin()) and not ( + /* Fix a few minor inaccuracies in the CPython analysis */ + ext_rettype(this.asBuiltin(), result.asBuiltin()) and + not ( this = Object::builtin("__import__") and result = theNoneType() or this = Object::builtin("compile") and result = theNoneType() @@ -370,63 +308,37 @@ class BuiltinFunctionObject extends BuiltinCallable { ) } - override int minParameters() { - none() - } - - override int maxParameters() { - none() - } + override int minParameters() { none() } + override int maxParameters() { none() } } /** DEPRECATED -- Use `Object::builtin("apply")` instead. */ -deprecated Object theApplyFunction() { - result = Object::builtin("apply") -} +deprecated Object theApplyFunction() { result = Object::builtin("apply") } /** DEPRECATED -- Use `Object::builtin("hasattr")` instead. */ -deprecated Object theHasattrFunction() { - result = Object::builtin("hasattr") -} +deprecated Object theHasattrFunction() { result = Object::builtin("hasattr") } /** DEPRECATED -- Use `Object::builtin("len")` instead. */ -deprecated Object theLenFunction() { - result = Object::builtin("len") -} +deprecated Object theLenFunction() { result = Object::builtin("len") } /** DEPRECATED -- Use `Object::builtin("format")` instead. */ -deprecated Object theFormatFunction() { - result = Object::builtin("format") -} +deprecated Object theFormatFunction() { result = Object::builtin("format") } /** DEPRECATED -- Use `Object::builtin("open")` instead. */ -deprecated Object theOpenFunction() { - result = Object::builtin("open") -} +deprecated Object theOpenFunction() { result = Object::builtin("open") } /** DEPRECATED -- Use `Object::builtin("print")` instead. */ -deprecated Object thePrintFunction() { - result = Object::builtin("print") -} +deprecated Object thePrintFunction() { result = Object::builtin("print") } /** DEPRECATED -- Use `Object::builtin("input")` instead. */ -deprecated Object theInputFunction() { - result = Object::builtin("input") -} +deprecated Object theInputFunction() { result = Object::builtin("input") } /** DEPRECATED -- Use `Object::builtin("locals")` instead. */ -deprecated Object theLocalsFunction() { - result = Object::builtin("locals") -} +deprecated Object theLocalsFunction() { result = Object::builtin("locals") } /** DEPRECATED -- Use `Object::builtin("globals")()` instead. */ -deprecated Object theGlobalsFunction() { - result = Object::builtin("globals") -} +deprecated Object theGlobalsFunction() { result = Object::builtin("globals") } /** DEPRECATED -- Use `Object::builtin("sysExit()` instead. */ -deprecated Object theExitFunctionObject() { - result = ModuleObject::named("sys").attr("exit") -} - +deprecated Object theExitFunctionObject() { result = ModuleObject::named("sys").attr("exit") } From 2ad0f5af2df14ca6848e5d32b2a79ab4c4f0efa1 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Fri, 3 Apr 2020 20:04:32 -0700 Subject: [PATCH 06/16] Python: ObjectAPI to ValueAPI: WrongNumberArgumentsInCall: ObjectAPI.qll: Adds getAFunctionCall and getAMethodCall predicates to FunctionValue --- python/ql/src/semmle/python/objects/ObjectAPI.qll | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/python/ql/src/semmle/python/objects/ObjectAPI.qll b/python/ql/src/semmle/python/objects/ObjectAPI.qll index 380f42043c7..d24976be013 100644 --- a/python/ql/src/semmle/python/objects/ObjectAPI.qll +++ b/python/ql/src/semmle/python/objects/ObjectAPI.qll @@ -594,6 +594,17 @@ abstract class FunctionValue extends CallableValue { or result = TBuiltinClassObject(this.(BuiltinMethodObjectInternal).getReturnType()) } + + /** Gets a call-site from where this function is called as a function */ + CallNode getAFunctionCall() { result.getFunction().pointsTo() = this } + + /** Gets a call-site from where this function is called as a method */ + CallNode getAMethodCall() { + exists(BoundMethodObjectInternal bm | + result.getFunction().pointsTo() = bm and + bm.getFunction() = this + ) + } } /** Class representing Python functions */ From c6fbbb1cd19fca003d0a8b5ba2bb3f23fd377cac Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Fri, 3 Apr 2020 20:06:43 -0700 Subject: [PATCH 07/16] Python: ObjectAPI to ValueAPI: WrongNumberArgumentsInCall: CallArgs.qll: Fixes too_*_args refs to getA*Call --- python/ql/src/Expressions/CallArgs.qll | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/ql/src/Expressions/CallArgs.qll b/python/ql/src/Expressions/CallArgs.qll index b071bbac372..c455a3428d9 100644 --- a/python/ql/src/Expressions/CallArgs.qll +++ b/python/ql/src/Expressions/CallArgs.qll @@ -159,14 +159,14 @@ predicate too_few_args(Call call, Value callable, int limit) { not exists(call.getStarargs()) and not exists(call.getKwargs()) and arg_count(call) < limit and exists(FunctionValue func | func = get_function_or_initializer(callable) | - call = func.getACall().getNode() and limit = func.minParameters() and + call = func.getAFunctionCall().getNode() and limit = func.minParameters() and /* The combination of misuse of `mox.Mox().StubOutWithMock()` * and a bug in mox's implementation of methods results in having to * pass 1 too few arguments to the mocked function. */ not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod()) or - call = func.getACall().getNode() and limit = func.minParameters() - 1 + call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1 or callable instanceof ClassValue and call.getAFlowNode() = get_a_call(callable) and limit = func.minParameters() - 1 @@ -199,9 +199,9 @@ predicate too_many_args(Call call, Value callable, int limit) { func = get_function_or_initializer(callable) and not func.getScope().hasVarArg() and limit >= 0 | - call = func.getACall().getNode() and limit = func.maxParameters() + call = func.getAFunctionCall().getNode() and limit = func.maxParameters() or - call = func.getACall().getNode() and limit = func.maxParameters() - 1 + call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1 or callable instanceof ClassValue and call.getAFlowNode() = get_a_call(callable) and limit = func.maxParameters() - 1 From f25428b7a9f8e2435a935cca64df53053fdb865c Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Fri, 3 Apr 2020 20:08:00 -0700 Subject: [PATCH 08/16] Python: ObjectAPI to ValueAPI: WrongNumberArgumentsInCall: Exceptions.qll: Fixes incorrect implementation of localRaisedType --- python/ql/src/semmle/python/types/Exceptions.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/semmle/python/types/Exceptions.qll b/python/ql/src/semmle/python/types/Exceptions.qll index 9de373f73d4..b194807e4a1 100644 --- a/python/ql/src/semmle/python/types/Exceptions.qll +++ b/python/ql/src/semmle/python/types/Exceptions.qll @@ -97,7 +97,7 @@ class RaisingNode extends ControlFlowNode { ( exists(ControlFlowNode ex | ex = this.getExceptionNode() and - (ex.pointsTo(result) or ex.pointsTo(_, result, _)) + (ex.pointsTo(result) or ex.pointsTo().getClass() = result) ) or this.getNode() instanceof ImportExpr and result = ClassValue::importError() From e0f26d4f7e8b71f06e41e526df07d4cb300aabbc Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Fri, 3 Apr 2020 20:11:42 -0700 Subject: [PATCH 09/16] Python: ObjectAPI to ValueAPI: WrongNumberArgumentsInCall: Updates expected results --- .../Functions/overriding/WrongNumberArgumentsInCall.expected | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/test/query-tests/Functions/overriding/WrongNumberArgumentsInCall.expected b/python/ql/test/query-tests/Functions/overriding/WrongNumberArgumentsInCall.expected index 4f29401ce5b..86dd08abf16 100644 --- a/python/ql/test/query-tests/Functions/overriding/WrongNumberArgumentsInCall.expected +++ b/python/ql/test/query-tests/Functions/overriding/WrongNumberArgumentsInCall.expected @@ -1,2 +1,2 @@ -| test.py:16:9:16:21 | Attribute() | Call to $@ with too many arguments; should be no more than 0. | test.py:5:5:5:20 | Function meth1 | method Base.meth1 | -| test.py:17:9:17:20 | Attribute() | Call to $@ with too few arguments; should be no fewer than 1. | test.py:8:5:8:26 | Function meth2 | method Base.meth2 | +| test.py:16:9:16:21 | Attribute() | Call to $@ with too many arguments; should be no more than 0. | test.py:5:5:5:20 | Function Base.meth1 | method Base.meth1 | +| test.py:17:9:17:20 | Attribute() | Call to $@ with too few arguments; should be no fewer than 1. | test.py:8:5:8:26 | Function Base.meth2 | method Base.meth2 | From 7615452b3179939b19ef27c28e9f43a1f09d566a Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Fri, 3 Apr 2020 20:28:19 -0700 Subject: [PATCH 10/16] Python: ObjAPI to ValAPI: WrongNumArgsInCall: Autoformat CallArgs.qll --- python/ql/src/Expressions/CallArgs.qll | 39 +++++++++++++++----------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/python/ql/src/Expressions/CallArgs.qll b/python/ql/src/Expressions/CallArgs.qll index 1686195f12b..61ce5c7dc8c 100644 --- a/python/ql/src/Expressions/CallArgs.qll +++ b/python/ql/src/Expressions/CallArgs.qll @@ -154,17 +154,21 @@ predicate too_few_args(Call call, Value callable, int limit) { not exists(call.getKwargs()) and arg_count(call) < limit and exists(FunctionValue func | func = get_function_or_initializer(callable) | - call = func.getAFunctionCall().getNode() and limit = func.minParameters() and - /* The combination of misuse of `mox.Mox().StubOutWithMock()` - * and a bug in mox's implementation of methods results in having to - * pass 1 too few arguments to the mocked function. - */ - not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod()) - or - call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1 - or - callable instanceof ClassValue and - call.getAFlowNode() = get_a_call(callable) and limit = func.minParameters() - 1 + call = func.getAFunctionCall().getNode() and + limit = func.minParameters() and + /* + * The combination of misuse of `mox.Mox().StubOutWithMock()` + * and a bug in mox's implementation of methods results in having to + * pass 1 too few arguments to the mocked function. + */ + + not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod()) + or + call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1 + or + callable instanceof ClassValue and + call.getAFlowNode() = get_a_call(callable) and + limit = func.minParameters() - 1 ) } @@ -192,14 +196,15 @@ predicate too_many_args_objectapi(Call call, Object callable, int limit) { predicate too_many_args(Call call, Value callable, int limit) { // Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call' not illegally_named_parameter(call, callable, _) and - exists(FunctionValue func | - func = get_function_or_initializer(callable) and - not func.getScope().hasVarArg() and limit >= 0 - | + exists(FunctionValue func | + func = get_function_or_initializer(callable) and + not func.getScope().hasVarArg() and + limit >= 0 + | call = func.getAFunctionCall().getNode() and limit = func.maxParameters() - or + or call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1 - or + or callable instanceof ClassValue and call.getAFlowNode() = get_a_call(callable) and limit = func.maxParameters() - 1 From bbe7314c1799f90c76716f2bcec42d0d0b4ac8ac Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Fri, 3 Apr 2020 20:30:24 -0700 Subject: [PATCH 11/16] Python: ObjAPI to ValAPI: WrongNumArgsInCall: Autoformats ObjectAPI.qll --- .../src/semmle/python/objects/ObjectAPI.qll | 39 ++++++------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/python/ql/src/semmle/python/objects/ObjectAPI.qll b/python/ql/src/semmle/python/objects/ObjectAPI.qll index 7a8e3080fb9..331bc283594 100644 --- a/python/ql/src/semmle/python/objects/ObjectAPI.qll +++ b/python/ql/src/semmle/python/objects/ObjectAPI.qll @@ -614,9 +614,9 @@ class PythonFunctionValue extends FunctionValue { override string descriptiveString() { if this.getScope().isMethod() then - exists(Class cls | this.getScope().getScope() = cls | - result = "method " + this.getQualifiedName() - ) + exists(Class cls | this.getScope().getScope() = cls | + result = "method " + this.getQualifiedName() + ) else result = "function " + this.getQualifiedName() } @@ -641,14 +641,9 @@ class PythonFunctionValue extends FunctionValue { } /** Gets a control flow node corresponding to a return statement in this function */ - ControlFlowNode getAReturnedNode() { - result = this.getScope().getAReturnValueFlowNode() - } - - override ClassValue getARaisedType() { - scope_raises(result, this.getScope()) - } + ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() } + override ClassValue getARaisedType() { scope_raises(result, this.getScope()) } } /** Class representing builtin functions, such as `len` or `print` */ @@ -661,9 +656,7 @@ class BuiltinFunctionValue extends FunctionValue { override int minParameters() { none() } - override int maxParameters() { - none() - } + override int maxParameters() { none() } override ClassValue getARaisedType() { /* Information is unavailable for C code in general */ @@ -685,19 +678,14 @@ class BuiltinMethodValue extends FunctionValue { override string descriptiveString() { result = "builtin-method " + this.getQualifiedName() } - override int minParameters() { - none() - } + override int minParameters() { none() } - override int maxParameters() { - none() - } + override int maxParameters() { none() } override ClassValue getARaisedType() { /* Information is unavailable for C code in general */ none() } - } /** @@ -923,10 +911,8 @@ module ClassValue { ClassValue lookupError() { result = TBuiltinClassObject(Builtin::builtin("LookupError")) } /** Get the `ClassValue` for the `IndexError` class. */ - ClassValue indexError() { - result = TBuiltinClassObject(Builtin::builtin("IndexError")) - } - + ClassValue indexError() { result = TBuiltinClassObject(Builtin::builtin("IndexError")) } + /** Get the `ClassValue` for the `IOError` class. */ ClassValue ioError() { result = TBuiltinClassObject(Builtin::builtin("IOError")) } @@ -949,8 +935,5 @@ module ClassValue { } /** Get the `ClassValue` for the `SystemExit` class. */ - ClassValue systemExit() { - result = TBuiltinClassObject(Builtin::builtin("SystemExit")) - } - + ClassValue systemExit() { result = TBuiltinClassObject(Builtin::builtin("SystemExit")) } } From 26bdb9ab04163fc8ec052c8c469e89e4a04a3c77 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Fri, 3 Apr 2020 20:33:17 -0700 Subject: [PATCH 12/16] Python: ObjAPI to ValAPI: WrongNumArgsInCall: Exceptions.qll: Removes extraneous spaces in comments --- python/ql/src/semmle/python/types/Exceptions.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/src/semmle/python/types/Exceptions.qll b/python/ql/src/semmle/python/types/Exceptions.qll index b194807e4a1..7fe1b274664 100644 --- a/python/ql/src/semmle/python/types/Exceptions.qll +++ b/python/ql/src/semmle/python/types/Exceptions.qll @@ -34,7 +34,7 @@ class RaisingNode extends ControlFlowNode { /** * Gets the type of an exception that may be raised - * at this control flow node + * at this control flow node */ ClassObject getARaisedType_objectapi() { result = this.localRaisedType_objectapi() @@ -46,7 +46,7 @@ class RaisingNode extends ControlFlowNode { /** * Gets the type of an exception that may be raised - * at this control flow node + * at this control flow node */ ClassValue getARaisedType() { result = this.localRaisedType() From 88be3359ac10c8975bae07cb2087dffdc4330de9 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Sat, 4 Apr 2020 00:15:10 -0700 Subject: [PATCH 13/16] Python: ObjAPI to ValAPI: WrongNumForArgsInCall: Update affected queries to use objectapi --- python/ql/test/library-tests/types/exceptions/ExitRaises.ql | 2 +- python/ql/test/library-tests/types/exceptions/Handles.ql | 2 +- python/ql/test/library-tests/types/exceptions/Viable.ql | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/python/ql/test/library-tests/types/exceptions/ExitRaises.ql b/python/ql/test/library-tests/types/exceptions/ExitRaises.ql index 62be45dce8e..32ef268332c 100644 --- a/python/ql/test/library-tests/types/exceptions/ExitRaises.ql +++ b/python/ql/test/library-tests/types/exceptions/ExitRaises.ql @@ -1,5 +1,5 @@ import python from RaisingNode r, Scope s, ClassObject cls -where r.viableExceptionalExit(s, cls) +where r.viableExceptionalExit_objectapi(s, cls) select r.getLocation().getStartLine(), r, s.toString(), cls diff --git a/python/ql/test/library-tests/types/exceptions/Handles.ql b/python/ql/test/library-tests/types/exceptions/Handles.ql index 601f2632392..dfdf1f9d7b2 100644 --- a/python/ql/test/library-tests/types/exceptions/Handles.ql +++ b/python/ql/test/library-tests/types/exceptions/Handles.ql @@ -1,5 +1,5 @@ import python from ExceptFlowNode n, ClassObject cls -where n.handles(cls) +where n.handles_objectapi(cls) select n.getLocation().getStartLine(), cls.toString() diff --git a/python/ql/test/library-tests/types/exceptions/Viable.ql b/python/ql/test/library-tests/types/exceptions/Viable.ql index e28fa1a907c..ed388e2faf2 100644 --- a/python/ql/test/library-tests/types/exceptions/Viable.ql +++ b/python/ql/test/library-tests/types/exceptions/Viable.ql @@ -1,6 +1,6 @@ import python from RaisingNode r, ControlFlowNode n, ClassObject ex -where r.viableExceptionEdge(n, ex) +where r.viableExceptionEdge_objectapi(n, ex) select r.getLocation().getStartLine(), n.getLocation().getStartLine(), r.getNode().toString(), n.getNode().toString(), ex.toString() From 8c1aeb24cb3b678843455a1e5bbe16755e734894 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Sat, 4 Apr 2020 00:57:09 -0700 Subject: [PATCH 14/16] Python: ObjAPI to ValAPI: WrongNumArgsInCall: Updates query expected results --- .../Arguments/WrongNumberArgumentsInCall.expected | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/ql/test/query-tests/Expressions/Arguments/WrongNumberArgumentsInCall.expected b/python/ql/test/query-tests/Expressions/Arguments/WrongNumberArgumentsInCall.expected index c23418acd45..31adabb9e05 100644 --- a/python/ql/test/query-tests/Expressions/Arguments/WrongNumberArgumentsInCall.expected +++ b/python/ql/test/query-tests/Expressions/Arguments/WrongNumberArgumentsInCall.expected @@ -1,7 +1,7 @@ | use_mox.py:28:1:28:4 | f0() | Call to $@ with too few arguments; should be no fewer than 1. | use_mox.py:7:1:7:10 | Function f0 | function f0 | | use_mox.py:29:1:29:5 | f1() | Call to $@ with too few arguments; should be no fewer than 2. | use_mox.py:10:1:10:13 | Function f1 | function f1 | -| use_mox.py:32:1:32:8 | Attribute() | Call to $@ with too few arguments; should be no fewer than 1. | use_mox.py:15:5:15:20 | Function m0 | method C.m0 | -| use_mox.py:33:1:33:9 | Attribute() | Call to $@ with too few arguments; should be no fewer than 2. | use_mox.py:18:5:18:23 | Function m1 | method C.m1 | +| use_mox.py:32:1:32:8 | Attribute() | Call to $@ with too few arguments; should be no fewer than 1. | use_mox.py:15:5:15:20 | Function C.m0 | method C.m0 | +| use_mox.py:33:1:33:9 | Attribute() | Call to $@ with too few arguments; should be no fewer than 2. | use_mox.py:18:5:18:23 | Function C.m1 | method C.m1 | | wrong_arguments.py:29:1:29:4 | f0() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:3:1:3:10 | Function f0 | function f0 | | wrong_arguments.py:30:1:30:4 | f1() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:6:1:6:20 | Function f1 | function f1 | | wrong_arguments.py:31:1:31:4 | f2() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:9:1:9:14 | Function f2 | function f2 | @@ -21,5 +21,5 @@ | wrong_arguments.py:86:1:86:4 | l1() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:71:6:71:21 | Function lambda | function lambda | | wrong_arguments.py:96:1:96:12 | f6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:21:1:21:13 | Function f6 | function f6 | | wrong_arguments.py:97:1:97:7 | f6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:21:1:21:13 | Function f6 | function f6 | -| wrong_arguments.py:130:1:130:9 | Attribute() | Call to $@ with too few arguments; should be no fewer than 2. | wrong_arguments.py:126:5:126:31 | Function spam | method Eggs2.spam | -| wrong_arguments.py:130:1:130:9 | Attribute() | Call to $@ with too many arguments; should be no more than 0. | wrong_arguments.py:121:5:121:19 | Function spam | method Eggs1.spam | +| wrong_arguments.py:130:1:130:9 | Attribute() | Call to $@ with too few arguments; should be no fewer than 2. | wrong_arguments.py:126:5:126:31 | Function Eggs2.spam | method Eggs2.spam | +| wrong_arguments.py:130:1:130:9 | Attribute() | Call to $@ with too many arguments; should be no more than 0. | wrong_arguments.py:121:5:121:19 | Function Eggs1.spam | method Eggs1.spam | From 01aac8273cb7b84fbf21c5bc80e1dd455fb0710d Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Sat, 4 Apr 2020 03:11:25 -0700 Subject: [PATCH 15/16] Python: ObjAPI to ValAPI: WrongNumArgsInCall: Update queries to use objectapi --- python/ql/test/2/library-tests/types/exceptions/ExitRaises.ql | 2 +- python/ql/test/3/library-tests/types/exceptions/Viable.ql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/test/2/library-tests/types/exceptions/ExitRaises.ql b/python/ql/test/2/library-tests/types/exceptions/ExitRaises.ql index 8e4c47a3e74..415db290a05 100644 --- a/python/ql/test/2/library-tests/types/exceptions/ExitRaises.ql +++ b/python/ql/test/2/library-tests/types/exceptions/ExitRaises.ql @@ -1,5 +1,5 @@ import python from RaisingNode r, Scope s, ClassObject cls -where r.viableExceptionalExit(s, cls) +where r.viableExceptionalExit_objectapi(s, cls) select r.getLocation().getStartLine(), r.toString(), s.toString(), cls.toString() diff --git a/python/ql/test/3/library-tests/types/exceptions/Viable.ql b/python/ql/test/3/library-tests/types/exceptions/Viable.ql index e28fa1a907c..ed388e2faf2 100644 --- a/python/ql/test/3/library-tests/types/exceptions/Viable.ql +++ b/python/ql/test/3/library-tests/types/exceptions/Viable.ql @@ -1,6 +1,6 @@ import python from RaisingNode r, ControlFlowNode n, ClassObject ex -where r.viableExceptionEdge(n, ex) +where r.viableExceptionEdge_objectapi(n, ex) select r.getLocation().getStartLine(), n.getLocation().getStartLine(), r.getNode().toString(), n.getNode().toString(), ex.toString() From c1ba1345df23925e1d88d07ff789d0fd1f75f0f1 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Mon, 6 Apr 2020 11:03:42 -0700 Subject: [PATCH 16/16] Python: ObjAPI to ValAPI: WrongNumArgsInCall: ObjectAPI.qll: Adds doc for getAnInferredType --- python/ql/src/semmle/python/objects/ObjectAPI.qll | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/ql/src/semmle/python/objects/ObjectAPI.qll b/python/ql/src/semmle/python/objects/ObjectAPI.qll index 5a09f0c8893..d870e19e9c6 100644 --- a/python/ql/src/semmle/python/objects/ObjectAPI.qll +++ b/python/ql/src/semmle/python/objects/ObjectAPI.qll @@ -621,6 +621,8 @@ abstract class FunctionValue extends CallableValue { bm.getFunction() = this ) } + + /** Gets a class that this function may return */ abstract ClassValue getAnInferredReturnType(); }