-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathImpossibleCast.ql
More file actions
103 lines (91 loc) · 3.72 KB
/
ImpossibleCast.ql
File metadata and controls
103 lines (91 loc) · 3.72 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
/**
* @name Impossible array cast
* @description Trying to cast an array of a particular type as an array of a subtype causes a
* 'ClassCastException' at runtime.
* @kind problem
* @problem.severity error
* @precision low
* @id java/impossible-array-cast
* @tags reliability
* correctness
* logic
* external/cwe/cwe-704
*/
import java
/**
* A cast expression of the form `(T[])new S[...]`.
*/
class ArrayCast extends CastExpr {
ArrayCast() {
this.getExpr() instanceof ArrayCreationExpr and
this.getType() instanceof Array
}
/** The type of the operand expression of this cast. */
Array getSourceType() { result = this.getExpr().getType() }
/** The result type of this cast. */
Array getTargetType() { result = this.getType() }
Type getSourceComponentType() { result = this.getSourceType().getComponentType() }
Type getTargetComponentType() { result = this.getTargetType().getComponentType() }
}
predicate uncheckedCastType(RefType t) {
t instanceof BoundedType or t instanceof ParameterizedType
}
predicate castFlow(ArrayCast ce, Variable v) {
ce = v.getAnAssignedValue()
or
exists(Variable mid | castFlow(ce, mid) and mid.getAnAccess() = v.getAnAssignedValue())
}
predicate returnedFrom(ArrayCast ce, Method m) {
exists(ReturnStmt ret | ret.getEnclosingCallable() = m | ret.getExpr() = ce)
or
exists(Variable v | castFlow(ce, v) | returnedVariableFrom(v, m))
}
predicate returnedVariableFrom(Variable v, Method m) {
exists(ReturnStmt ret | ret.getExpr() = v.getAnAccess() and ret.getEnclosingCallable() = m)
}
predicate rawTypeConversion(RawType source, ParameterizedType target) {
target.getErasure() = source.getErasure()
}
from ArrayCast ce, RefType target, RefType source, string message
where
target = ce.getTargetComponentType() and
source = ce.getSourceComponentType() and
target.hasSupertype+(source) and
not rawTypeConversion(source, target) and
(
// No unchecked operations, so the cast would crash straight away.
not uncheckedCastType(target) and
message =
"Impossible downcast: the cast from " + source.getName() + "[] to " + target.getName() +
"[] will always fail with a ClassCastException."
or
// For unchecked operations, the crash would not occur at the cast site,
// but only if/when the value is assigned to a variable of different array type.
// This would require tracking the flow of values, but we focus on finding problematic
// APIs. We keep two cases:
// - An array that is actually returned from the (non-private) method, or
// - an array that is assigned to a field returned from another (non-private) method.
uncheckedCastType(target) and
returnedFrom(ce, ce.getEnclosingCallable()) and
ce.getEnclosingCallable().getReturnType().(Array).getElementType() = target and
not ce.getEnclosingCallable().isPrivate() and
message =
"Impossible downcast: this is returned by " + ce.getEnclosingCallable().getName() +
" as a value of type " + target.getName() + "[], but the array has type " + source.getName()
+ "[]. Callers of " + ce.getEnclosingCallable().getName() +
" may fail with a ClassCastException."
or
exists(Method m, Variable v |
uncheckedCastType(target) and
castFlow(ce, v) and
returnedVariableFrom(v, m) and
m.getReturnType().(Array).getElementType() = target and
not m.isPrivate() and
message =
"Impossible downcast: this is assigned to " + v.getName() + " which is returned by " + m +
" as a value of type " + target.getName() + "[], but the array has type " +
source.getName() + "[]. Callers of " + m.getName() +
" may fail with a ClassCastException."
)
)
select ce, message