-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathDisposeNotCalledOnException.ql
More file actions
74 lines (68 loc) · 2.92 KB
/
DisposeNotCalledOnException.ql
File metadata and controls
74 lines (68 loc) · 2.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/**
* @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.
*/
ExceptionClass 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
}
from MethodCall disposeCall, Expr disposableCreation, MethodCall callThatThrows
where
disposeReachableFromDisposableCreation(disposeCall, disposableCreation)
// The dispose call is not, itself, within a dispose method.
and not disposeCall.getEnclosingCallable() instanceof DisposeMethod
// Dispose call not within a finally or catch block
and not exists(TryStmt ts |
ts.getACatchClause().getAChild*() = disposeCall or ts.getFinally().getAChild*() = disposeCall)
// At least one method call exists between the allocation and disposal that could throw
and disposableCreation.getAReachableElement() = callThatThrows
and callThatThrows.getAReachableElement() = disposeCall
and exists(getAThrownException(callThatThrows.getARuntimeTarget()))
select disposeCall, "Dispose missed if exception is thrown by $@.", callThatThrows, callThatThrows.toString()