forked from github/codeql
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathVirtualDispatch.qll
More file actions
307 lines (274 loc) · 9.79 KB
/
VirtualDispatch.qll
File metadata and controls
307 lines (274 loc) · 9.79 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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
/**
* Provides predicates for reasoning about runtime call targets through virtual
* dispatch.
*/
overlay[local?]
module;
import java
import semmle.code.java.dataflow.TypeFlow
private import DispatchFlow as DispatchFlow
private import ObjFlow as ObjFlow
private import semmle.code.java.dataflow.internal.BaseSSA
private import semmle.code.java.controlflow.Guards
private import semmle.code.java.dispatch.internal.Unification
/**
* A conservative analysis that returns a single method - if we can establish
* one - that will be the target of the virtual dispatch.
*/
Method exactVirtualMethod(MethodCall c) {
// If there are multiple potential implementations, return nothing.
implCount(c, 1) and
result = viableImpl(c)
}
/**
* A conservative analysis that returns a single callable - if we can establish
* one - that will be the target of the call.
*/
Callable exactCallable(Call c) {
result = exactVirtualMethod(c)
or
c instanceof ConstructorCall and result = c.getCallee()
}
private predicate implCount(MethodCall m, int c) { strictcount(viableImpl(m)) = c }
/** Gets a viable implementation of the target of the given `Call`. */
overlay[local]
Callable viableCallable(Call c) {
result = viableImpl(c)
or
c instanceof ConstructorCall and result = c.getCallee().getSourceDeclaration()
}
/** The source declaration of a method that is the target of a virtual call. */
class VirtCalledSrcMethod extends SrcMethod {
pragma[nomagic]
VirtCalledSrcMethod() {
exists(VirtualMethodCall ma | ma.getMethod().getSourceDeclaration() = this)
}
}
private module Dispatch {
/** Gets a viable implementation of the method called in the given method access. */
cached
Method viableImpl(MethodCall ma) { result = ObjFlow::viableImpl_out(ma) }
/**
* Holds if `m` is a viable implementation of the method called in `ma` for
* which we only have imprecise open-world type-based dispatch resolution, and
* the dispatch type is likely to yield implausible dispatch targets.
*/
cached
predicate lowConfidenceDispatchTarget(MethodCall ma, Method m) {
m = viableImpl(ma) and lowConfidenceDispatch(ma)
}
/**
* INTERNAL: Use `viableImpl` instead.
*
* Gets a viable implementation of the method called in the given method access.
*/
cached
Method viableImpl_v3(MethodCall ma) { result = DispatchFlow::viableImpl_out(ma) }
/**
* Holds if the best type bounds for the qualifier of `ma` are likely to
* contain implausible dispatch targets.
*/
private predicate lowConfidenceDispatch(VirtualMethodCall ma) {
exists(RefType t | hasQualifierType(ma, t, false) |
lowConfidenceDispatchType(t.getSourceDeclaration())
) and
(
not qualType(ma, _, _)
or
exists(RefType t | qualType(ma, t, false) |
lowConfidenceDispatchType(t.getSourceDeclaration())
)
) and
(
not qualUnionType(ma, _, _)
or
exists(RefType t | qualUnionType(ma, t, false) |
lowConfidenceDispatchType(t.getSourceDeclaration())
)
) and
not ObjFlow::objectToStringCall(ma)
}
private predicate lowConfidenceDispatchType(SrcRefType t) {
t instanceof TypeObject
or
t instanceof Interface and not t.fromSource()
or
t instanceof TypeInputStream
or
t.hasQualifiedName("java.io", "Serializable")
or
t.hasQualifiedName("java.lang", "Iterable")
or
t.hasQualifiedName("java.lang", "Cloneable")
or
t.getPackage().hasName("java.util") and t instanceof Interface
or
t.hasQualifiedName("java.util", "Hashtable")
}
/**
* INTERNAL: Use `viableImpl` instead.
*
* Gets a viable implementation of the method called in the given method access.
*/
cached
Method viableImpl_v2(MethodCall ma) {
result = viableImpl_v2_cand(pragma[only_bind_into](ma)) and
exists(Method def, RefType t, boolean exact |
qualUnionType(pragma[only_bind_into](ma), pragma[only_bind_into](t),
pragma[only_bind_into](exact)) and
def = ma.getMethod().getSourceDeclaration()
|
exact = true and result = exactMethodImpl(def, t.getSourceDeclaration())
or
exact = false and
exists(RefType t2 |
result = viableMethodImpl(def, t.getSourceDeclaration(), t2) and
not Unification_v2::failsUnification(t, t2)
)
)
or
result = viableImpl_v2_cand(ma) and
not qualUnionType(ma, _, _)
}
private predicate qualUnionType(VirtualMethodCall ma, RefType t, boolean exact) {
exprUnionTypeFlow(ma.getQualifier(), t, exact)
}
private predicate unificationTargetLeft_v2(ParameterizedType t1) { qualUnionType(_, t1, _) }
private module Unification_v2 =
MkUnification<unificationTargetLeft_v2/1, unificationTargetRight/1>;
private Method viableImpl_v2_cand(MethodCall ma) {
result = viableImpl_v1(ma) and
(
exists(Method def, RefType t, boolean exact |
qualType(ma, t, exact) and
def = ma.getMethod().getSourceDeclaration()
|
exact = true and result = exactMethodImpl(def, t.getSourceDeclaration())
or
exact = false and
exists(RefType t2 |
result = viableMethodImpl(def, t.getSourceDeclaration(), t2) and
not Unification_v2_cand::failsUnification(t, t2)
)
)
or
not qualType(ma, _, _)
)
}
private predicate qualType(VirtualMethodCall ma, RefType t, boolean exact) {
exprTypeFlow(ma.getQualifier(), t, exact)
}
private predicate unificationTargetLeft_v2_cand(ParameterizedType t1) { qualType(_, t1, _) }
private module Unification_v2_cand =
MkUnification<unificationTargetLeft_v2_cand/1, unificationTargetRight/1>;
/**
* INTERNAL: Use `viableImpl` instead.
*
* Gets a viable implementation of the method called in the given method access.
*/
cached
Method viableImpl_v1(MethodCall source) {
result = viableImpl_v1_cand(source) and
not impossibleDispatchTarget(source, result)
}
/**
* Holds if `source` cannot dispatch to `tgt` due to a negative `instanceof` guard.
*/
private predicate impossibleDispatchTarget(MethodCall source, Method tgt) {
tgt = viableImpl_v1_cand(source) and
exists(Guard typeTest, BaseSsaVariable v, Expr q, RefType t |
source.getQualifier() = q and
v.getAUse() = q and
typeTest.appliesTypeTest(v.getAUse(), t, false) and
guardControls_v1(typeTest, q.getBasicBlock(), false) and
tgt.getDeclaringType().getSourceDeclaration().getASourceSupertype*() = t.getErasure()
)
}
/**
* Gets a viable implementation of the method called in the given method access.
*/
private Method viableImpl_v1_cand(MethodCall source) {
not result.isAbstract() and
if source instanceof VirtualMethodCall
then
exists(VirtCalledSrcMethod def, RefType t, boolean exact |
source.getMethod().getSourceDeclaration() = def and
hasQualifierType(source, t, exact)
|
exact = true and result = exactMethodImpl(def, t.getSourceDeclaration())
or
exact = false and
exists(RefType t2 |
result = viableMethodImpl(def, t.getSourceDeclaration(), t2) and
not Unification_v1::failsUnification(t, t2)
)
)
else result = source.getMethod().getSourceDeclaration()
}
private predicate unificationTargetLeft_v1(ParameterizedType t1) { hasQualifierType(_, t1, _) }
private predicate unificationTargetRight(ParameterizedType t2) {
exists(viableMethodImpl(_, _, t2))
}
private module Unification_v1 =
MkUnification<unificationTargetLeft_v1/1, unificationTargetRight/1>;
private RefType getPreciseType(Expr e) {
result = e.(FunctionalExpr).getConstructedType()
or
not e instanceof FunctionalExpr and result = e.getType()
}
private predicate hasQualifierType(VirtualMethodCall ma, RefType t, boolean exact) {
exists(Expr src | src = ma.getQualifier() |
// If we have a qualifier, then we take its type.
exists(RefType srctype | srctype = getPreciseType(src) |
exists(BoundedType bd | bd = srctype |
t = bd.getAnUltimateUpperBoundType()
or
not exists(bd.getAnUltimateUpperBoundType()) and t = ma.getMethod().getDeclaringType()
)
or
t = srctype and not srctype instanceof BoundedType
) and
// If we have a class instance expression, then we know the exact type.
// This is an important improvement in precision.
if src instanceof ClassInstanceExpr then exact = true else exact = false
)
or
// If the call has no qualifier then it's an implicit `this` qualifier,
// so start from the caller's declaring type or enclosing type.
not exists(ma.getQualifier()) and
exact = false and
(
ma.isOwnMethodCall() and t = ma.getEnclosingCallable().getDeclaringType()
or
ma.isEnclosingMethodCall(t)
)
}
/** Gets the implementation of `top` present on a value of precisely type `t`. */
cached
Method exactMethodImpl(VirtCalledSrcMethod top, SrcRefType t) {
hasSrcMethod(t, result) and
top.getAPossibleImplementationOfSrcMethod() = result
}
/** Gets the implementations of `top` present on viable subtypes of `t`. */
cached
Method viableMethodImpl(VirtCalledSrcMethod top, SrcRefType tsrc, RefType t) {
exists(SrcRefType sub |
result = exactMethodImpl(top, sub) and
tsrc = t.getSourceDeclaration() and
hasViableSubtype(t, sub)
)
}
pragma[noinline]
private predicate hasSrcMethod(SrcRefType t, Method impl) {
exists(Method m | t.hasMethod(m, _, _) and impl = m.getSourceDeclaration())
}
private predicate isAbstractWithSubclass(SrcRefType t) {
t.isAbstract() and exists(Class c | c.getASourceSupertype() = t)
}
private predicate hasViableSubtype(RefType t, SrcRefType sub) {
sub.extendsOrImplements*(t) and
not sub instanceof Interface and
not isAbstractWithSubclass(sub)
}
}
import Dispatch