Skip to content

Commit 1aecdb4

Browse files
committed
UniversalFlow: Move Universal Flow library to its own file.
1 parent ea458c0 commit 1aecdb4

File tree

2 files changed

+285
-281
lines changed

2 files changed

+285
-281
lines changed
Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
private import codeql.util.Location
2+
private import codeql.util.Unit
3+
4+
/** Provides the input specification. */
5+
signature module UniversalFlowInput<LocationSig Location> {
6+
/**
7+
* A node for which certain data flow properties may be proved. For example,
8+
* expressions and method declarations.
9+
*/
10+
class TypeFlowNode {
11+
/** Gets a textual representation of this node. */
12+
string toString();
13+
14+
/** Gets the location of this node. */
15+
Location getLocation();
16+
}
17+
18+
/**
19+
* Holds if data can flow from `n1` to `n2` in one step.
20+
*
21+
* For a given `n2`, this predicate must include all possible `n1` that can flow to `n2`.
22+
*/
23+
predicate step(TypeFlowNode n1, TypeFlowNode n2);
24+
25+
/** Holds if `n` represents a `null` value. */
26+
predicate isNullValue(TypeFlowNode n);
27+
28+
/**
29+
* Holds if `n` should be excluded from the set of null values even if
30+
* the null analysis determines that `n` is always null.
31+
*/
32+
default predicate isExcludedFromNullAnalysis(TypeFlowNode n) { none() }
33+
}
34+
35+
module UfMake<LocationSig Location, UniversalFlowInput<Location> I> {
36+
private import I
37+
38+
/**
39+
* Holds if data can flow from `n1` to `n2` in one step, and `n1` is
40+
* functionally determined by `n2`.
41+
*/
42+
private predicate uniqStep(TypeFlowNode n1, TypeFlowNode n2) { n1 = unique(TypeFlowNode n | step(n, n2)) }
43+
44+
/**
45+
* Holds if data can flow from `n1` to `n2` in one step, and `n1` is not
46+
* functionally determined by `n2`.
47+
*/
48+
private predicate joinStep(TypeFlowNode n1, TypeFlowNode n2) { step(n1, n2) and not uniqStep(n1, n2) }
49+
50+
/** Holds if `null` is the only value that flows to `n`. */
51+
private predicate isNull(TypeFlowNode n) {
52+
isNullValue(n)
53+
or
54+
exists(TypeFlowNode mid | isNull(mid) and uniqStep(mid, n))
55+
or
56+
forex(TypeFlowNode mid | joinStep(mid, n) | isNull(mid)) and
57+
not isExcludedFromNullAnalysis(n)
58+
}
59+
60+
/**
61+
* Holds if data can flow from `n1` to `n2` in one step, `n1` is not necessarily
62+
* functionally determined by `n2`, and `n1` might take a non-null value.
63+
*/
64+
predicate joinStepNotNull(TypeFlowNode n1, TypeFlowNode n2) {
65+
joinStep(n1, n2) and not isNull(n1)
66+
}
67+
68+
predicate anyStep(TypeFlowNode n1, TypeFlowNode n2) {
69+
joinStepNotNull(n1, n2) or uniqStep(n1, n2)
70+
}
71+
72+
private predicate sccEdge(TypeFlowNode n1, TypeFlowNode n2) {
73+
anyStep(n1, n2) and anyStep+(n2, n1)
74+
}
75+
76+
private module Scc = QlBuiltins::EquivalenceRelation<TypeFlowNode, sccEdge/2>;
77+
78+
private class TypeFlowScc = Scc::EquivalenceClass;
79+
80+
/** Holds if `n` is part of an SCC of size 2 or more represented by `scc`. */
81+
private predicate sccRepr(TypeFlowNode n, TypeFlowScc scc) { scc = Scc::getEquivalenceClass(n) }
82+
83+
private predicate sccJoinStepNotNull(TypeFlowNode n, TypeFlowScc scc) {
84+
exists(TypeFlowNode mid |
85+
joinStepNotNull(n, mid) and
86+
sccRepr(mid, scc) and
87+
not sccRepr(n, scc)
88+
)
89+
}
90+
91+
private signature class NodeSig;
92+
93+
private signature module Edge {
94+
class Node;
95+
96+
predicate edge(TypeFlowNode n1, Node n2);
97+
}
98+
99+
private signature module RankedEdge<NodeSig Node> {
100+
predicate edgeRank(int r, TypeFlowNode n1, Node n2);
101+
102+
int lastRank(Node n);
103+
}
104+
105+
private module RankEdge<Edge E> implements RankedEdge<E::Node> {
106+
private import E
107+
108+
/**
109+
* Holds if `r` is a ranking of the incoming edges `(n1,n2)` to `n2`. The used
110+
* ordering is not necessarily total, so the ranking may have gaps.
111+
*/
112+
private predicate edgeRank1(int r, TypeFlowNode n1, Node n2) {
113+
n1 =
114+
rank[r](TypeFlowNode n, int startline, int startcolumn |
115+
edge(n, n2) and
116+
n.getLocation().hasLocationInfo(_, startline, startcolumn, _, _)
117+
|
118+
n order by startline, startcolumn
119+
)
120+
}
121+
122+
/**
123+
* Holds if `r2` is a ranking of the ranks from `edgeRank1`. This removes the
124+
* gaps from the ranking.
125+
*/
126+
private predicate edgeRank2(int r2, int r1, Node n) {
127+
r1 = rank[r2](int r | edgeRank1(r, _, n) | r)
128+
}
129+
130+
/** Holds if `r` is a ranking of the incoming edges `(n1,n2)` to `n2`. */
131+
predicate edgeRank(int r, TypeFlowNode n1, Node n2) {
132+
exists(int r1 |
133+
edgeRank1(r1, n1, n2) and
134+
edgeRank2(r, r1, n2)
135+
)
136+
}
137+
138+
int lastRank(Node n) { result = max(int r | edgeRank(r, _, n)) }
139+
}
140+
141+
private signature module TypePropagation {
142+
class Typ;
143+
144+
predicate candType(TypeFlowNode n, Typ t);
145+
146+
bindingset[t]
147+
predicate supportsType(TypeFlowNode n, Typ t);
148+
}
149+
150+
/** Implements recursion through `forall` by way of edge ranking. */
151+
private module ForAll<NodeSig Node, RankedEdge<Node> E, TypePropagation T> {
152+
/**
153+
* Holds if `t` is a bound that holds on one of the incoming edges to `n` and
154+
* thus is a candidate bound for `n`.
155+
*/
156+
pragma[nomagic]
157+
private predicate candJoinType(Node n, T::Typ t) {
158+
exists(TypeFlowNode mid |
159+
T::candType(mid, t) and
160+
E::edgeRank(_, mid, n)
161+
)
162+
}
163+
164+
/**
165+
* Holds if `t` is a candidate bound for `n` that is also valid for data coming
166+
* through the edges into `n` ranked from `1` to `r`.
167+
*/
168+
private predicate flowJoin(int r, Node n, T::Typ t) {
169+
(
170+
r = 1 and candJoinType(n, t)
171+
or
172+
flowJoin(r - 1, n, t) and E::edgeRank(r, _, n)
173+
) and
174+
forall(TypeFlowNode mid | E::edgeRank(r, mid, n) | T::supportsType(mid, t))
175+
}
176+
177+
/**
178+
* Holds if `t` is a candidate bound for `n` that is also valid for data
179+
* coming through all the incoming edges, and therefore is a valid bound for
180+
* `n`.
181+
*/
182+
predicate flowJoin(Node n, T::Typ t) { flowJoin(E::lastRank(n), n, t) }
183+
}
184+
185+
private module JoinStep implements Edge {
186+
class Node = TypeFlowNode;
187+
188+
predicate edge = joinStepNotNull/2;
189+
}
190+
191+
private module SccJoinStep implements Edge {
192+
class Node = TypeFlowScc;
193+
194+
predicate edge = sccJoinStepNotNull/2;
195+
}
196+
197+
private module RankedJoinStep = RankEdge<JoinStep>;
198+
199+
private module RankedSccJoinStep = RankEdge<SccJoinStep>;
200+
201+
signature module NullaryPropertySig {
202+
predicate hasPropertyBase(TypeFlowNode n);
203+
204+
default predicate barrier(TypeFlowNode n) { none() }
205+
}
206+
207+
module FlowNullary<NullaryPropertySig P> {
208+
private module Propagation implements TypePropagation {
209+
class Typ = Unit;
210+
211+
predicate candType(TypeFlowNode n, Unit u) { hasProperty(n) and exists(u) }
212+
213+
predicate supportsType = candType/2;
214+
}
215+
216+
predicate hasProperty(TypeFlowNode n) {
217+
P::hasPropertyBase(n)
218+
or
219+
not P::barrier(n) and
220+
(
221+
exists(TypeFlowNode mid | hasProperty(mid) and uniqStep(mid, n))
222+
or
223+
// The following is an optimized version of
224+
// `forex(TypeFlowNode mid | joinStepNotNull(mid, n) | hasPropery(mid))`
225+
ForAll<TypeFlowNode, RankedJoinStep, Propagation>::flowJoin(n, _)
226+
or
227+
exists(TypeFlowScc scc |
228+
sccRepr(n, scc) and
229+
// Optimized version of
230+
// `forex(TypeFlowNode mid | sccJoinStepNotNull(mid, scc) | hasPropery(mid))`
231+
ForAll<TypeFlowScc, RankedSccJoinStep, Propagation>::flowJoin(scc, _)
232+
)
233+
)
234+
}
235+
}
236+
237+
signature module PropertySig {
238+
class Prop;
239+
240+
bindingset[t1, t2]
241+
default predicate propImplies(Prop t1, Prop t2) { t1 = t2 }
242+
243+
predicate hasPropertyBase(TypeFlowNode n, Prop t);
244+
245+
default predicate barrier(TypeFlowNode n) { none() }
246+
}
247+
248+
module Flow<PropertySig P> {
249+
private module Propagation implements TypePropagation {
250+
class Typ = P::Prop;
251+
252+
predicate candType = hasProperty/2;
253+
254+
bindingset[t]
255+
predicate supportsType(TypeFlowNode n, Typ t) {
256+
exists(Typ t0 | hasProperty(n, t0) and P::propImplies(t0, t))
257+
}
258+
}
259+
260+
/**
261+
* Holds if the runtime type of `n` is exactly `t` and if this bound is a
262+
* non-trivial lower bound, that is, `t` has a subtype.
263+
*/
264+
predicate hasProperty(TypeFlowNode n, P::Prop t) {
265+
P::hasPropertyBase(n, t)
266+
or
267+
not P::barrier(n) and
268+
(
269+
exists(TypeFlowNode mid | hasProperty(mid, t) and uniqStep(mid, n))
270+
or
271+
// The following is an optimized version of
272+
// `forex(TypeFlowNode mid | joinStepNotNull(mid, n) | hasPropery(mid, t))`
273+
ForAll<TypeFlowNode, RankedJoinStep, Propagation>::flowJoin(n, t)
274+
or
275+
exists(TypeFlowScc scc |
276+
sccRepr(n, scc) and
277+
// Optimized version of
278+
// `forex(TypeFlowNode mid | sccJoinStepNotNull(mid, scc) | hasPropery(mid, t))`
279+
ForAll<TypeFlowScc, RankedSccJoinStep, Propagation>::flowJoin(scc, t)
280+
)
281+
)
282+
}
283+
}
284+
}

0 commit comments

Comments
 (0)