-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathMissingInstanceofInEquals.ql
More file actions
87 lines (82 loc) · 2.8 KB
/
MissingInstanceofInEquals.ql
File metadata and controls
87 lines (82 loc) · 2.8 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
/**
* @name Equals method does not inspect argument type
* @description An implementation of 'equals' that does not check the type
* of its argument may lead to failing casts.
* @kind problem
* @problem.severity error
* @precision high
* @id java/unchecked-cast-in-equals
* @tags reliability
* correctness
*/
import semmle.code.java.Member
import semmle.code.java.JDK
/** A cast inside a try-catch block that catches `ClassCastException`. */
class CheckedCast extends CastExpr {
CheckedCast() {
exists(TryStmt try, RefType cce |
this.getEnclosingStmt().getParent+() = try and
try.getACatchClause().getVariable().getType() = cce and
cce.getQualifiedName() = "java.lang.ClassCastException"
)
}
Variable castVariable() { result.getAnAccess() = this.getExpr() }
}
/**
* An `equals` method with a body of either `return o == this;`
* or `return o == field;`
*/
class ReferenceEquals extends EqualsMethod {
ReferenceEquals() {
exists(Block b, ReturnStmt ret, EQExpr eq |
this.getBody() = b and
b.getStmt(0) = ret and
(
ret.getResult() = eq
or
exists(ParExpr pe | ret.getResult() = pe and pe.getExpr() = eq)
) and
eq.getAnOperand() = this.getAParameter().getAnAccess() and
(eq.getAnOperand() instanceof ThisAccess or eq.getAnOperand() instanceof FieldAccess)
)
}
}
from EqualsMethod m
where
// The parameter is accessed at least once ...
exists(VarAccess va | va.getVariable() = m.getParameter()) and
// ... but its type is not checked using `instanceof`.
not exists(InstanceOfExpr e |
e.getEnclosingCallable() = m and
e.getExpr().(VarAccess).getVariable() = m.getParameter()
) and
// Exclude cases that are probably OK.
not exists(MethodAccess ma, Method c | ma.getEnclosingCallable() = m and ma.getMethod() = c |
c.hasName("getClass")
or
c.hasName("compareTo")
or
c.hasName("equals")
or
c.hasName("isInstance")
or
c.hasName("reflectionEquals")
or
// If both `this` and the argument are passed to another method,
// or if the argument is passed to a method declared or inherited by `this` type,
// that method may do the right thing.
ma.getAnArgument() = m.getParameter().getAnAccess() and
(
ma.getAnArgument() instanceof ThisAccess
or
exists(Method delegate | delegate.getSourceDeclaration() = ma.getMethod() |
m.getDeclaringType().inherits(delegate)
)
)
) and
not m.getDeclaringType() instanceof Interface and
// Exclude checked casts (casts inside `try`-blocks).
not exists(CheckedCast cast | cast.castVariable() = m.getAParameter()) and
// Exclude `equals` methods that implement reference-equality.
not m instanceof ReferenceEquals
select m, "equals() method does not seem to check argument type."