Skip to content

Commit e9fcd98

Browse files
author
Alexander Eyers-Taylor
authored
Merge pull request #20357 from github/alexet/java-global-virtual-dispatch
Java: Make Virtual Dispatch Global, but keep SSA local.
2 parents d638ee9 + d5ee91b commit e9fcd98

File tree

7 files changed

+40
-12
lines changed

7 files changed

+40
-12
lines changed

config/add-overlay-annotations.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,12 @@ def insert_overlay_caller_annotations(lines):
177177
out_lines.append(line)
178178
return out_lines
179179

180+
explicitly_global = set([
181+
"java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll",
182+
"java/ql/lib/semmle/code/java/dispatch/DispatchFlow.qll",
183+
"java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll",
184+
"java/ql/lib/semmle/code/java/dispatch/internal/Unification.qll",
185+
])
180186

181187
def annotate_as_appropriate(filename, lines):
182188
'''
@@ -196,6 +202,9 @@ def annotate_as_appropriate(filename, lines):
196202
((filename.endswith("Query.qll") or filename.endswith("Config.qll")) and
197203
any("implements DataFlow::ConfigSig" in line for line in lines))):
198204
return None
205+
elif filename in explicitly_global:
206+
# These files are explicitly global and should not be annotated.
207+
return None
199208
elif not any(line for line in lines if line.strip()):
200209
return None
201210

java/ql/lib/semmle/code/java/dataflow/SSA.qll

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,17 +253,18 @@ class SsaImplicitUpdate extends SsaUpdate {
253253
or
254254
if this.hasImplicitQualifierUpdate()
255255
then
256-
if exists(this.getANonLocalUpdate())
256+
if isNonLocal(this)
257257
then result = "nonlocal + nonlocal qualifier"
258258
else result = "nonlocal qualifier"
259259
else (
260-
exists(this.getANonLocalUpdate()) and result = "nonlocal"
260+
isNonLocal(this) and result = "nonlocal"
261261
)
262262
}
263263

264264
/**
265265
* Gets a reachable `FieldWrite` that might represent this ssa update, if any.
266266
*/
267+
overlay[global]
267268
FieldWrite getANonLocalUpdate() {
268269
exists(SsaSourceField f, Callable setter |
269270
relevantFieldUpdate(setter, f.getField(), result) and
@@ -287,6 +288,11 @@ class SsaImplicitUpdate extends SsaUpdate {
287288
}
288289
}
289290

291+
overlay[global]
292+
private predicate isNonLocalImpl(SsaImplicitUpdate su) { exists(su.getANonLocalUpdate()) }
293+
294+
private predicate isNonLocal(SsaImplicitUpdate su) = forceLocal(isNonLocalImpl/1)(su)
295+
290296
/**
291297
* An SSA variable that represents an uncertain implicit update of the value.
292298
* This is a `Call` that might reach a non-local update of the field or one of

java/ql/lib/semmle/code/java/dataflow/internal/SsaImpl.qll

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,15 +157,20 @@ private predicate hasEntryDef(TrackedVar v, BasicBlock b) {
157157
}
158158

159159
/** Holds if `n` might update the locally tracked variable `v`. */
160+
overlay[global]
160161
pragma[nomagic]
161-
private predicate uncertainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) {
162+
private predicate uncertainVariableUpdateImpl(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) {
162163
exists(Call c | c = n.asCall() | updatesNamedField(c, v, _)) and
163164
b.getNode(i) = n and
164165
hasDominanceInformation(b)
165166
or
166-
uncertainVariableUpdate(v.getQualifier(), n, b, i)
167+
uncertainVariableUpdateImpl(v.getQualifier(), n, b, i)
167168
}
168169

170+
/** Holds if `n` might update the locally tracked variable `v`. */
171+
predicate uncertainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) =
172+
forceLocal(uncertainVariableUpdateImpl/4)(v, n, b, i)
173+
169174
private module SsaInput implements SsaImplCommon::InputSig<Location, BasicBlock> {
170175
class SourceVariable = SsaSourceVariable;
171176

@@ -335,6 +340,7 @@ private module Cached {
335340
* Constructor --(intraInstanceCallEdge)-->+ Method(setter of this.f)
336341
* ```
337342
*/
343+
overlay[global]
338344
private predicate intraInstanceCallEdge(Callable c1, Method m2) {
339345
exists(MethodCall ma, RefType t1 |
340346
ma.getCaller() = c1 and
@@ -355,6 +361,7 @@ private module Cached {
355361
)
356362
}
357363

364+
overlay[global]
358365
private Callable tgt(Call c) {
359366
result = viableImpl_v2(c)
360367
or
@@ -364,11 +371,13 @@ private module Cached {
364371
}
365372

366373
/** Holds if `(c1,c2)` is an edge in the call graph. */
374+
overlay[global]
367375
private predicate callEdge(Callable c1, Callable c2) {
368376
exists(Call c | c.getCaller() = c1 and c2 = tgt(c))
369377
}
370378

371379
/** Holds if `(c1,c2)` is an edge in the call graph excluding `intraInstanceCallEdge`. */
380+
overlay[global]
372381
private predicate crossInstanceCallEdge(Callable c1, Callable c2) {
373382
callEdge(c1, c2) and not intraInstanceCallEdge(c1, c2)
374383
}
@@ -382,6 +391,7 @@ private module Cached {
382391
relevantFieldUpdate(_, f.getField(), _)
383392
}
384393

394+
overlay[global]
385395
private predicate source(Call call, TrackedField f, Field field, Callable c, boolean fresh) {
386396
relevantCall(call, f) and
387397
field = f.getField() and
@@ -395,9 +405,11 @@ private module Cached {
395405
* `fresh` indicates whether the instance `this` in `c` has been freshly
396406
* allocated along the call-chain.
397407
*/
408+
overlay[global]
398409
private newtype TCallableNode =
399410
MkCallableNode(Callable c, boolean fresh) { source(_, _, _, c, fresh) or edge(_, c, fresh) }
400411

412+
overlay[global]
401413
private predicate edge(TCallableNode n, Callable c2, boolean f2) {
402414
exists(Callable c1, boolean f1 | n = MkCallableNode(c1, f1) |
403415
intraInstanceCallEdge(c1, c2) and f2 = f1
@@ -407,13 +419,15 @@ private module Cached {
407419
)
408420
}
409421

422+
overlay[global]
410423
private predicate edge(TCallableNode n1, TCallableNode n2) {
411424
exists(Callable c2, boolean f2 |
412425
edge(n1, c2, f2) and
413426
n2 = MkCallableNode(c2, f2)
414427
)
415428
}
416429

430+
overlay[global]
417431
pragma[noinline]
418432
private predicate source(Call call, TrackedField f, Field field, TCallableNode n) {
419433
exists(Callable c, boolean fresh |
@@ -422,31 +436,36 @@ private module Cached {
422436
)
423437
}
424438

439+
overlay[global]
425440
private predicate sink(Callable c, Field f, TCallableNode n) {
426441
setsOwnField(c, f) and n = MkCallableNode(c, false)
427442
or
428443
setsOtherField(c, f) and n = MkCallableNode(c, _)
429444
}
430445

446+
overlay[global]
431447
private predicate prunedNode(TCallableNode n) {
432448
sink(_, _, n)
433449
or
434450
exists(TCallableNode mid | edge(n, mid) and prunedNode(mid))
435451
}
436452

453+
overlay[global]
437454
private predicate prunedEdge(TCallableNode n1, TCallableNode n2) {
438455
prunedNode(n1) and
439456
prunedNode(n2) and
440457
edge(n1, n2)
441458
}
442459

460+
overlay[global]
443461
private predicate edgePlus(TCallableNode c1, TCallableNode c2) = fastTC(prunedEdge/2)(c1, c2)
444462

445463
/**
446464
* Holds if there exists a call-chain originating in `call` that can update `f` on some instance
447465
* where `f` and `call` share the same enclosing callable in which a
448466
* `FieldRead` of `f` is reachable from `call`.
449467
*/
468+
overlay[global]
450469
pragma[noopt]
451470
private predicate updatesNamedFieldImpl(Call call, TrackedField f, Callable setter) {
452471
exists(TCallableNode src, TCallableNode sink, Field field |
@@ -457,11 +476,13 @@ private module Cached {
457476
}
458477

459478
bindingset[call, f]
479+
overlay[global]
460480
pragma[inline_late]
461481
private predicate updatesNamedField0(Call call, TrackedField f, Callable setter) {
462482
updatesNamedField(call, f, setter)
463483
}
464484

485+
overlay[global]
465486
cached
466487
predicate defUpdatesNamedField(SsaImplicitUpdate def, TrackedField f, Callable setter) {
467488
f = def.getSourceVariable() and

java/ql/lib/semmle/code/java/dispatch/DispatchFlow.qll

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
* data flow check for lambdas, anonymous classes, and other sufficiently
66
* private classes where all object instantiations are accounted for.
77
*/
8-
overlay[local?]
9-
module;
108

119
import java
1210
private import VirtualDispatch

java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
* The set of dispatch targets for `Object.toString()` calls are reduced based
77
* on possible data flow from objects of more specific types to the qualifier.
88
*/
9-
overlay[local?]
10-
module;
119

1210
import java
1311
private import VirtualDispatch

java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
* Provides predicates for reasoning about runtime call targets through virtual
33
* dispatch.
44
*/
5-
overlay[local?]
6-
module;
75

86
import java
97
import semmle.code.java.dataflow.TypeFlow

java/ql/lib/semmle/code/java/dispatch/internal/Unification.qll

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
/**
22
* Provides a module to check whether two `ParameterizedType`s are unifiable.
33
*/
4-
overlay[local?]
5-
module;
64

75
import java
86

0 commit comments

Comments
 (0)