-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathPartiallyMaskedCatch.ql
More file actions
132 lines (122 loc) · 4.15 KB
/
PartiallyMaskedCatch.ql
File metadata and controls
132 lines (122 loc) · 4.15 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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/**
* @name Unreachable catch clause
* @description An unreachable 'catch' clause may indicate a mistake in exception handling or may
* be unnecessary.
* @kind problem
* @problem.severity warning
* @precision high
* @id java/unreachable-catch-clause
* @tags quality
* reliability
* correctness
* exceptions
* external/cwe/cwe-561
*/
import java
pragma[nomagic]
predicate mayThrow(Stmt s, RefType rt) {
s.(ThrowStmt).getExpr().getType() = rt
or
exists(Call call |
call.getEnclosingStmt() = s and
call.getCallee().getAnException().getType() = rt
)
}
pragma[nomagic]
predicate caughtBy(TryStmt try, Stmt s, RefType rt) {
mayThrow(s, rt) and
s.getEnclosingStmt+() = try.getBlock() and
caughtType(try, _).hasSubtype*(rt)
}
pragma[nomagic]
predicate nestedTry(TryStmt outer, TryStmt inner) { inner.getEnclosingStmt+() = outer.getBlock() }
/**
* Exceptions of type `rt` thrown from within statement `s` are caught by an inner try block
* and are therefore not propagated to the outer try block `t`.
*/
private predicate caughtInside(TryStmt t, Stmt s, RefType rt) {
exists(TryStmt innerTry |
nestedTry(t, innerTry) and
caughtBy(innerTry, s, rt)
)
}
/**
* Returns an exception type thrown from within the try block of `t`
* that is relevant to the catch clauses of `t` (i.e. not already
* caught by an inner try-catch).
*/
private RefType getAThrownExceptionType(TryStmt t) {
exists(Method m, Exception e |
(
m = t.getAResourceDecl().getAVariable().getType().(RefType).getAMethod() or
m = t.getAResourceExpr().getType().(RefType).getAMethod()
) and
m.hasName("close") and
m.hasNoParameters() and
m.getAnException() = e and
result = e.getType()
)
or
exists(Call call, Exception e |
t.getBlock() = call.getEnclosingStmt().getEnclosingStmt*() or
t.getAResourceDecl() = call.getEnclosingStmt()
|
(
call.getCallee().getAnException() = e or
call.(GenericCall).getATypeArgument(call.getCallee().getAnException().getType()) = e.getType()
) and
not caughtInside(t, call.getEnclosingStmt(), e.getType()) and
result = e.getType()
)
or
exists(ThrowStmt ts |
t.getBlock() = ts.getEnclosingStmt*() and
not caughtInside(t, ts, ts.getExpr().getType()) and
result = ts.getExpr().getType()
)
}
private RefType caughtType(TryStmt try, int index) {
exists(CatchClause cc | cc = try.getCatchClause(index) |
if cc.isMultiCatch()
then result = cc.getVariable().getTypeAccess().(UnionTypeAccess).getAnAlternative().getType()
else result = cc.getVariable().getType()
)
}
private predicate maybeUnchecked(RefType t) {
t.getAnAncestor().hasQualifiedName("java.lang", "RuntimeException") or
t.getAnAncestor().hasQualifiedName("java.lang", "Error") or
t.hasQualifiedName("java.lang", "Exception") or
t.hasQualifiedName("java.lang", "Throwable")
}
predicate overlappingExceptions(RefType e1, RefType e2) {
exists(RefType throwable | throwable.hasQualifiedName("java.lang", "Throwable") |
throwable.hasSubtype*(e1) and
throwable.hasSubtype*(e2) and
e1.getADescendant() = e2.getADescendant()
)
}
from TryStmt try, int first, int second, RefType masking, RefType masked, string multiCatchMsg
where
try.getFile().isJavaSourceFile() and
masking = caughtType(try, first) and
masking.getAStrictAncestor() = masked and
masked = caughtType(try, second) and
forall(RefType thrownType |
thrownType = getAThrownExceptionType(try) and
// If there's any overlap in the types, this catch block may be relevant.
overlappingExceptions(thrownType, masked)
|
exists(RefType priorCaughtType, int priorIdx |
priorIdx < second and
priorCaughtType = caughtType(try, priorIdx) and
thrownType.hasSupertype*(priorCaughtType)
)
) and
not maybeUnchecked(masked) and
if try.getCatchClause(second).isMultiCatch()
then multiCatchMsg = " for type " + masked.getName()
else multiCatchMsg = ""
select try.getCatchClause(second),
"This catch-clause is unreachable" + multiCatchMsg + "; it is masked $@.",
try.getCatchClause(first),
"by a previous catch-clause for exceptions of type '" + masking.getName() + "'"