Skip to content

Commit 03f2d8f

Browse files
authored
Merge pull request #247 from aschackmull/java/dispatchflow-typepruning
Approved by yh-semmle
2 parents d6f6d67 + 1c2807e commit 03f2d8f

File tree

1 file changed

+96
-80
lines changed

1 file changed

+96
-80
lines changed

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

Lines changed: 96 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,98 @@ private Callable dispatchCand(Call c) {
1818
result = viableImpl_inp(c)
1919
}
2020

21+
/**
22+
* Holds if `t` and all its enclosing types are public.
23+
*/
24+
private predicate veryPublic(RefType t) {
25+
t.isPublic() and
26+
(
27+
not t instanceof NestedType or
28+
veryPublic(t.(NestedType).getEnclosingType())
29+
)
30+
}
31+
32+
/**
33+
* Holds if `cie` occurs as the initializer of a public static field.
34+
*/
35+
private predicate publicStaticFieldInit(ClassInstanceExpr cie) {
36+
exists(Field f |
37+
f.isStatic() and
38+
f.isPublic() and
39+
veryPublic(f.getDeclaringType()) and
40+
f.getInitializer() = cie
41+
)
42+
}
43+
44+
/**
45+
* Holds if a `ClassInstanceExpr` constructing `t` occurs as the initializer of
46+
* a public static field.
47+
*/
48+
private predicate publicThroughField(RefType t) {
49+
exists(ClassInstanceExpr cie |
50+
cie.getConstructedType() = t and
51+
publicStaticFieldInit(cie)
52+
)
53+
}
54+
55+
/**
56+
* Holds if `t` and its subtypes are private or anonymous.
57+
*/
58+
private predicate privateConstruction(RefType t) {
59+
(t.isPrivate() or t instanceof AnonymousClass) and
60+
not publicThroughField(t) and
61+
forall(SrcRefType sub | sub.getASourceSupertype+() = t.getSourceDeclaration() |
62+
(sub.isPrivate() or sub instanceof AnonymousClass) and
63+
not publicThroughField(sub)
64+
)
65+
}
66+
67+
/**
68+
* Holds if `m` is declared on a type that we will track all instantiations of
69+
* for the purpose of virtual dispatch to `m`. This holds in particular for
70+
* lambda methods and methods on other anonymous classes.
71+
*/
72+
private predicate trackedMethod(Method m) {
73+
privateConstruction(m.getDeclaringType().getSourceDeclaration())
74+
}
75+
76+
/**
77+
* Holds if `t` declares or inherits the tracked method `m`.
78+
*/
79+
private predicate trackedMethodOnType(Method m, SrcRefType t) {
80+
exists(Method m0 |
81+
t.hasMethod(m0, _, _) and
82+
m = m0.getSourceDeclaration() and
83+
trackedMethod(m)
84+
)
85+
}
86+
87+
/**
88+
* Holds if `ma` may dispatch to the tracked method `m` declared or inherited
89+
* by the type constructed by `cie`. Thus the dispatch from `ma` to `m` will
90+
* only be included if `cie` flows to the qualifier of `ma`.
91+
*/
92+
private predicate dispatchOrigin(ClassInstanceExpr cie, MethodAccess ma, Method m) {
93+
m = viableImpl_inp(ma) and
94+
not m = ma.getMethod().getSourceDeclaration() and
95+
trackedMethodOnType(m, cie.getConstructedType().getSourceDeclaration())
96+
}
97+
98+
/** Holds if `t` is a type that is relevant for dispatch flow. */
99+
private predicate relevant(RefType t) {
100+
exists(ClassInstanceExpr cie | dispatchOrigin(cie, _, _) and t = cie.getConstructedType().getSourceDeclaration()) or
101+
relevant(t.getErasure()) or
102+
exists(RefType r | relevant(r) and t = r.getASourceSupertype()) or
103+
relevant(t.(Array).getComponentType()) or
104+
t instanceof MapType or
105+
t instanceof CollectionType
106+
}
107+
108+
/** A node with a type that is relevant for dispatch flow. */
109+
private class RelevantNode extends Node {
110+
RelevantNode() { relevant(this.getType()) }
111+
}
112+
21113
/**
22114
* Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
23115
* The instance parameter is considered to have index `-1`.
@@ -26,7 +118,8 @@ pragma[nomagic]
26118
private predicate viableParamCand(Call call, int i, ParameterNode p) {
27119
exists(Callable callable |
28120
callable = dispatchCand(call) and
29-
p.isParameterOf(callable, i)
121+
p.isParameterOf(callable, i) and
122+
p instanceof RelevantNode
30123
)
31124
}
32125

@@ -43,7 +136,7 @@ private predicate viableArgParamCand(ArgumentNode arg, ParameterNode p) {
43136
/**
44137
* Holds if data may flow from `n1` to `n2` in a single step through a call or a return.
45138
*/
46-
private predicate callFlowStepCand(Node n1, Node n2) {
139+
private predicate callFlowStepCand(RelevantNode n1, RelevantNode n2) {
47140
exists(ReturnStmt ret, Method m |
48141
ret.getEnclosingCallable() = m and
49142
ret.getResult() = n1.asExpr() and
@@ -56,7 +149,7 @@ private predicate callFlowStepCand(Node n1, Node n2) {
56149
* Holds if data may flow from `n1` to `n2` in a single step that does not go
57150
* through a call or a return.
58151
*/
59-
private predicate flowStep(Node n1, Node n2) {
152+
private predicate flowStep(RelevantNode n1, RelevantNode n2) {
60153
exists(BaseSsaVariable v, BaseSsaVariable def |
61154
def.(BaseSsaUpdate).getDefiningExpr().(VariableAssign).getSource() = n1.asExpr() or
62155
def.(BaseSsaImplicitInit).isParameterDefinition(n1.asParameter()) or
@@ -126,83 +219,6 @@ private predicate flowStep(Node n1, Node n2) {
126219
)
127220
}
128221

129-
/**
130-
* Holds if `t` and all its enclosing types are public.
131-
*/
132-
private predicate veryPublic(RefType t) {
133-
t.isPublic() and
134-
(
135-
not t instanceof NestedType or
136-
veryPublic(t.(NestedType).getEnclosingType())
137-
)
138-
}
139-
140-
/**
141-
* Holds if `cie` occurs as the initializer of a public static field.
142-
*/
143-
private predicate publicStaticFieldInit(ClassInstanceExpr cie) {
144-
exists(Field f |
145-
f.isStatic() and
146-
f.isPublic() and
147-
veryPublic(f.getDeclaringType()) and
148-
f.getInitializer() = cie
149-
)
150-
}
151-
152-
/**
153-
* Holds if a `ClassInstanceExpr` constructing `t` occurs as the initializer of
154-
* a public static field.
155-
*/
156-
private predicate publicThroughField(RefType t) {
157-
exists(ClassInstanceExpr cie |
158-
cie.getConstructedType() = t and
159-
publicStaticFieldInit(cie)
160-
)
161-
}
162-
163-
/**
164-
* Holds if `t` and its subtypes are private or anonymous.
165-
*/
166-
private predicate privateConstruction(RefType t) {
167-
(t.isPrivate() or t instanceof AnonymousClass) and
168-
not publicThroughField(t) and
169-
forall(SrcRefType sub | sub.getASourceSupertype+() = t.getSourceDeclaration() |
170-
(sub.isPrivate() or sub instanceof AnonymousClass) and
171-
not publicThroughField(sub)
172-
)
173-
}
174-
175-
/**
176-
* Holds if `m` is declared on a type that we will track all instantiations of
177-
* for the purpose of virtual dispatch to `m`. This holds in particular for
178-
* lambda methods and methods on other anonymous classes.
179-
*/
180-
private predicate trackedMethod(Method m) {
181-
privateConstruction(m.getDeclaringType().getSourceDeclaration())
182-
}
183-
184-
/**
185-
* Holds if `t` declares or inherits the tracked method `m`.
186-
*/
187-
private predicate trackedMethodOnType(Method m, SrcRefType t) {
188-
exists(Method m0 |
189-
t.hasMethod(m0, _, _) and
190-
m = m0.getSourceDeclaration() and
191-
trackedMethod(m)
192-
)
193-
}
194-
195-
/**
196-
* Holds if `ma` may dispatch to the tracked method `m` declared or inherited
197-
* by the type constructed by `cie`. Thus the dispatch from `ma` to `m` will
198-
* only be included if `cie` flows to the qualifier of `ma`.
199-
*/
200-
private predicate dispatchOrigin(ClassInstanceExpr cie, MethodAccess ma, Method m) {
201-
m = viableImpl_inp(ma) and
202-
not m = ma.getMethod().getSourceDeclaration() and
203-
trackedMethodOnType(m, cie.getConstructedType().getSourceDeclaration())
204-
}
205-
206222
/**
207223
* Holds if `n` is forward-reachable from a relevant `ClassInstanceExpr`.
208224
*/

0 commit comments

Comments
 (0)