mirror of
https://github.com/github/codeql.git
synced 2026-05-10 09:19:27 +02:00
88 lines
3.1 KiB
Plaintext
88 lines
3.1 KiB
Plaintext
/**
|
|
* @name Dispose may not be called if an exception is thrown during execution
|
|
* @description Methods that create objects of type 'IDisposable' should call 'Dispose()' on those
|
|
* objects, even during exceptional circumstances, otherwise unmanaged resources may
|
|
* not be released.
|
|
* @kind problem
|
|
* @problem.severity warning
|
|
* @precision medium
|
|
* @id cs/dispose-not-called-on-throw
|
|
* @tags efficiency
|
|
* maintainability
|
|
* security
|
|
* external/cwe/cwe-404
|
|
* external/cwe/cwe-459
|
|
* external/cwe/cwe-460
|
|
*/
|
|
|
|
import csharp
|
|
import Dispose
|
|
import semmle.code.csharp.frameworks.System
|
|
|
|
/**
|
|
* Gets an exception type that may be thrown during the execution of method `m`.
|
|
* Assumes any exception may be thrown by library types.
|
|
*/
|
|
Class getAThrownException(Method m) {
|
|
m.fromLibrary() and
|
|
result = any(SystemExceptionClass sc)
|
|
or
|
|
exists(ControlFlowElement cfe |
|
|
cfe = any(ThrowElement te | result = te.getExpr().getType()) or
|
|
cfe = any(MethodCall mc | result = getAThrownException(mc.getARuntimeTarget()))
|
|
|
|
|
cfe.getEnclosingCallable() = m and
|
|
not isTriedAgainstException(cfe, result)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if control flow element is tried against throwing an exception of type
|
|
* `ec`.
|
|
*/
|
|
pragma[noinline]
|
|
predicate isTriedAgainstException(ControlFlowElement cfe, ExceptionClass ec) {
|
|
(cfe instanceof ThrowElement or cfe instanceof MethodCall) and
|
|
exists(TryStmt ts |
|
|
ts.getATriedElement() = cfe and
|
|
exists(ts.getAnExceptionHandler(ec))
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if `disposeCall` disposes the object created by `disposableCreation`.
|
|
*/
|
|
predicate disposeReachableFromDisposableCreation(MethodCall disposeCall, Expr disposableCreation) {
|
|
// The qualifier of the Dispose call flows from something that introduced a disposable into scope
|
|
(
|
|
disposableCreation instanceof LocalScopeDisposableCreation or
|
|
disposableCreation instanceof MethodCall
|
|
) and
|
|
DataFlow::localFlowStep+(DataFlow::exprNode(disposableCreation),
|
|
DataFlow::exprNode(disposeCall.getQualifier())) and
|
|
disposeCall.getTarget() instanceof DisposeMethod
|
|
}
|
|
|
|
class MethodCallThatMayThrow extends MethodCall {
|
|
MethodCallThatMayThrow() { exists(getAThrownException(this.getARuntimeTarget())) }
|
|
}
|
|
|
|
ControlFlowElement getACatchOrFinallyClauseChild() {
|
|
exists(TryStmt ts | result = ts.getACatchClause() or result = ts.getFinally())
|
|
or
|
|
result = getACatchOrFinallyClauseChild().getAChild()
|
|
}
|
|
|
|
from MethodCall disposeCall, Expr disposableCreation, MethodCallThatMayThrow callThatThrows
|
|
where
|
|
disposeReachableFromDisposableCreation(disposeCall, disposableCreation) and
|
|
// The dispose call is not, itself, within a dispose method.
|
|
not disposeCall.getEnclosingCallable() instanceof DisposeMethod and
|
|
// Dispose call not within a finally or catch block
|
|
not getACatchOrFinallyClauseChild() = disposeCall and
|
|
// At least one method call exists between the allocation and disposal that could throw
|
|
disposableCreation.getAReachableElement() = callThatThrows and
|
|
callThatThrows.getAReachableElement() = disposeCall
|
|
select disposeCall, "Dispose missed if exception is thrown by $@.", callThatThrows,
|
|
callThatThrows.toString()
|