mirror of
https://github.com/github/codeql.git
synced 2025-12-19 10:23:15 +01:00
Will need subsequent PRs fixing up test failures (due to deprecated methods moving around), but other than that everything should be straight-forward.
76 lines
2.8 KiB
Plaintext
76 lines
2.8 KiB
Plaintext
import python
|
|
|
|
// Helper predicates for multiple call to __init__/__del__ queries.
|
|
pragma[noinline]
|
|
private predicate multiple_invocation_paths_helper(
|
|
FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2, FunctionObject multi
|
|
) {
|
|
i1 != i2 and
|
|
i1 = top.getACallee+() and
|
|
i2 = top.getACallee+() and
|
|
i1.getFunction() = multi
|
|
}
|
|
|
|
pragma[noinline]
|
|
private predicate multiple_invocation_paths(
|
|
FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2, FunctionObject multi
|
|
) {
|
|
multiple_invocation_paths_helper(top, i1, i2, multi) and
|
|
i2.getFunction() = multi
|
|
}
|
|
|
|
/** Holds if `self.name` calls `multi` by multiple paths, and thus calls it more than once. */
|
|
predicate multiple_calls_to_superclass_method(ClassObject self, FunctionObject multi, string name) {
|
|
exists(FunctionInvocation top, FunctionInvocation i1, FunctionInvocation i2 |
|
|
multiple_invocation_paths(top, i1, i2, multi) and
|
|
top.runtime(self.declaredAttribute(name)) and
|
|
self.getASuperType().declaredAttribute(name) = multi
|
|
|
|
|
// Only called twice if called from different functions,
|
|
// or if one call-site can reach the other.
|
|
i1.getCall().getScope() != i2.getCall().getScope()
|
|
or
|
|
i1.getCall().strictlyReaches(i2.getCall())
|
|
)
|
|
}
|
|
|
|
/** Holds if all attributes called `name` can be inferred to be methods. */
|
|
private predicate named_attributes_not_method(ClassObject cls, string name) {
|
|
cls.declaresAttribute(name) and not cls.declaredAttribute(name) instanceof FunctionObject
|
|
}
|
|
|
|
/** Holds if `f` actually does something. */
|
|
private predicate does_something(FunctionObject f) {
|
|
f.isBuiltin() and not f = theObjectType().lookupAttribute("__init__")
|
|
or
|
|
exists(Stmt s | s = f.getFunction().getAStmt() and not s instanceof Pass)
|
|
}
|
|
|
|
/** Holds if `meth` looks like it should have a call to `name`, but does not */
|
|
private predicate missing_call(FunctionObject meth, string name) {
|
|
exists(CallNode call, AttrNode attr |
|
|
call.getScope() = meth.getFunction() and
|
|
call.getFunction() = attr and
|
|
attr.getName() = name and
|
|
not exists(FunctionObject f | f.getACall() = call)
|
|
)
|
|
}
|
|
|
|
/** Holds if `self.name` does not call `missing`, even though it is expected to. */
|
|
predicate missing_call_to_superclass_method(
|
|
ClassObject self, FunctionObject top, FunctionObject missing, string name
|
|
) {
|
|
missing = self.getASuperType().declaredAttribute(name) and
|
|
top = self.lookupAttribute(name) and
|
|
/* There is no call to missing originating from top */
|
|
not top.getACallee*() = missing and
|
|
/* Make sure that all named 'methods' are objects that we can understand. */
|
|
not exists(ClassObject sup |
|
|
sup = self.getAnImproperSuperType() and
|
|
named_attributes_not_method(sup, name)
|
|
) and
|
|
not self.isAbstract() and
|
|
does_something(missing) and
|
|
not missing_call(top, name)
|
|
}
|