mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Modernize multple calls to init/del
This commit is contained in:
@@ -856,9 +856,14 @@ Class getNextClassInMroKnownStartingClass(Class cls, Class startingClass) {
|
||||
)
|
||||
}
|
||||
|
||||
private Function findFunctionAccordingToMroKnownStartingClass(
|
||||
Class cls, Class startingClass, string name
|
||||
) {
|
||||
/**
|
||||
* Gets a potential definition of the function `name` of the class `cls` according to our approximation of
|
||||
* MRO for the class `startingCls` (see `getNextClassInMroKnownStartingClass` for more information).
|
||||
*
|
||||
* Note: this is almost the same as `findFunctionAccordingToMro`, except we know the
|
||||
* `startingClass`, which can give slightly more precise results.
|
||||
*/
|
||||
Function findFunctionAccordingToMroKnownStartingClass(Class cls, Class startingClass, string name) {
|
||||
result = cls.getAMethod() and
|
||||
result.getName() = name and
|
||||
cls = getADirectSuperclass*(startingClass)
|
||||
@@ -871,7 +876,7 @@ private Function findFunctionAccordingToMroKnownStartingClass(
|
||||
|
||||
/**
|
||||
* Gets a potential definition of the function `name` according to our approximation of
|
||||
* MRO for the class `cls` (see `getNextClassInMroKnownStartingClass` for more information).
|
||||
* MRO for the class `startingCls` (see `getNextClassInMroKnownStartingClass` for more information).
|
||||
*
|
||||
* Note: this is almost the same as `findFunctionAccordingToMro`, except we know the
|
||||
* `startingClass`, which can give slightly more precise results.
|
||||
|
||||
@@ -4,37 +4,32 @@ import python
|
||||
import semmle.python.ApiGraphs
|
||||
import semmle.python.dataflow.new.internal.DataFlowDispatch
|
||||
|
||||
// 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
|
||||
predicate multipleCallsToSuperclassMethod(Function meth, Function calledMulti, string name) {
|
||||
exists(DataFlow::MethodCallNode call1, DataFlow::MethodCallNode call2, Class cls |
|
||||
meth.getName() = name and
|
||||
meth.getScope() = cls and
|
||||
not call1 = call2 and
|
||||
calledMulti = getASuperCallTarget(cls, meth, call1) and
|
||||
calledMulti = getASuperCallTarget(cls, meth, call2) and
|
||||
nonTrivial(calledMulti)
|
||||
)
|
||||
}
|
||||
|
||||
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
|
||||
Function getASuperCallTarget(Class mroBase, Function meth, DataFlow::MethodCallNode call) {
|
||||
meth = call.getScope() and
|
||||
getADirectSuperclass*(mroBase) = meth.getScope() and
|
||||
call.calls(_, meth.getName()) and
|
||||
exists(Function target, Class nextMroBase |
|
||||
(result = target or result = getASuperCallTarget(nextMroBase, target, _))
|
||||
|
|
||||
// Only called twice if called from different functions,
|
||||
// or if one call-site can reach the other.
|
||||
i1.getCall().getScope() != i2.getCall().getScope()
|
||||
superCall(call, _) and
|
||||
nextMroBase = mroBase and
|
||||
target =
|
||||
findFunctionAccordingToMroKnownStartingClass(getNextClassInMroKnownStartingClass(meth.getScope(),
|
||||
mroBase), mroBase, meth.getName())
|
||||
or
|
||||
i1.getCall().strictlyReaches(i2.getCall())
|
||||
callsMethodOnClassWithSelf(meth, call, nextMroBase, _) and
|
||||
target = findFunctionAccordingToMro(nextMroBase, meth.getName())
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Multiple calls to `__del__` during object destruction
|
||||
* @description A duplicated call to a super-class `__del__` method may lead to class instances not be cleaned up properly.
|
||||
* @description A duplicated call to a superclass `__del__` method may lead to class instances not be cleaned up properly.
|
||||
* @kind problem
|
||||
* @tags quality
|
||||
* reliability
|
||||
@@ -14,16 +14,17 @@
|
||||
import python
|
||||
import MethodCallOrder
|
||||
|
||||
from ClassObject self, FunctionObject multi
|
||||
predicate multipleCallsToSuperclassDel(Function meth, Function calledMulti) {
|
||||
multipleCallsToSuperclassMethod(meth, calledMulti, "__sel__")
|
||||
}
|
||||
|
||||
from Function meth, Function calledMulti
|
||||
where
|
||||
multiple_calls_to_superclass_method(self, multi, "__del__") and
|
||||
not multiple_calls_to_superclass_method(self.getABaseType(), multi, "__del__") and
|
||||
not exists(FunctionObject better |
|
||||
multiple_calls_to_superclass_method(self, better, "__del__") and
|
||||
better.overrides(multi)
|
||||
) and
|
||||
not self.failedInference()
|
||||
select self,
|
||||
"Class " + self.getName() +
|
||||
" may not be cleaned up properly as $@ may be called multiple times during destruction.", multi,
|
||||
multi.descriptiveString()
|
||||
multipleCallsToSuperclassDel(meth, calledMulti) and
|
||||
// Don't alert for multiple calls to a superclass del when a subclass will do.
|
||||
not exists(Function subMulti |
|
||||
multipleCallsToSuperclassDel(meth, subMulti) and
|
||||
calledMulti.getScope() = getADirectSuperclass+(subMulti.getScope())
|
||||
)
|
||||
select meth, "This delete method calls $@ multiple times.", calledMulti,
|
||||
calledMulti.getQualifiedName()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Multiple calls to `__init__` during object initialization
|
||||
* @description A duplicated call to a super-class `__init__` method may lead to objects of this class not being properly initialized.
|
||||
* @description A duplicated call to a superclass `__init__` method may lead to objects of this class not being properly initialized.
|
||||
* @kind problem
|
||||
* @tags quality
|
||||
* reliability
|
||||
@@ -14,17 +14,17 @@
|
||||
import python
|
||||
import MethodCallOrder
|
||||
|
||||
from ClassObject self, FunctionObject multi
|
||||
predicate multipleCallsToSuperclassInit(Function meth, Function calledMulti) {
|
||||
multipleCallsToSuperclassMethod(meth, calledMulti, "__init__")
|
||||
}
|
||||
|
||||
from Function meth, Function calledMulti
|
||||
where
|
||||
multi != theObjectType().lookupAttribute("__init__") and
|
||||
multiple_calls_to_superclass_method(self, multi, "__init__") and
|
||||
not multiple_calls_to_superclass_method(self.getABaseType(), multi, "__init__") and
|
||||
not exists(FunctionObject better |
|
||||
multiple_calls_to_superclass_method(self, better, "__init__") and
|
||||
better.overrides(multi)
|
||||
) and
|
||||
not self.failedInference()
|
||||
select self,
|
||||
"Class " + self.getName() +
|
||||
" may not be initialized properly as $@ may be called multiple times during initialization.",
|
||||
multi, multi.descriptiveString()
|
||||
multipleCallsToSuperclassInit(meth, calledMulti) and
|
||||
// Don't alert for multiple calls to a superclass init when a subclass will do.
|
||||
not exists(Function subMulti |
|
||||
multipleCallsToSuperclassInit(meth, subMulti) and
|
||||
calledMulti.getScope() = getADirectSuperclass+(subMulti.getScope())
|
||||
)
|
||||
select meth, "This initializer method calls $@ multiple times.", calledMulti,
|
||||
calledMulti.getQualifiedName()
|
||||
|
||||
Reference in New Issue
Block a user