Skip to content

Commit ea458c0

Browse files
committed
TypeFlow: Extract a universal flow library abstraction from TypeFlow.
1 parent fba4d09 commit ea458c0

File tree

1 file changed

+154
-72
lines changed

1 file changed

+154
-72
lines changed

shared/typeflow/codeql/typeflow/internal/TypeFlowImpl.qll

Lines changed: 154 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,37 @@ private import codeql.typeflow.TypeFlow
22
private import codeql.util.Location
33
private import codeql.util.Unit
44

5-
module TypeFlow<LocationSig Location, TypeFlowInput<Location> I> {
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> {
636
private import I
737

838
/**
@@ -31,11 +61,11 @@ module TypeFlow<LocationSig Location, TypeFlowInput<Location> I> {
3161
* Holds if data can flow from `n1` to `n2` in one step, `n1` is not necessarily
3262
* functionally determined by `n2`, and `n1` might take a non-null value.
3363
*/
34-
private predicate joinStepNotNull(TypeFlowNode n1, TypeFlowNode n2) {
64+
predicate joinStepNotNull(TypeFlowNode n1, TypeFlowNode n2) {
3565
joinStep(n1, n2) and not isNull(n1)
3666
}
3767

38-
private predicate anyStep(TypeFlowNode n1, TypeFlowNode n2) {
68+
predicate anyStep(TypeFlowNode n1, TypeFlowNode n2) {
3969
joinStepNotNull(n1, n2) or uniqStep(n1, n2)
4070
}
4171

@@ -168,34 +198,117 @@ module TypeFlow<LocationSig Location, TypeFlowInput<Location> I> {
168198

169199
private module RankedSccJoinStep = RankEdge<SccJoinStep>;
170200

171-
private module ExactTypePropagation implements TypePropagation {
172-
class Typ = Type;
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;
173210

174-
predicate candType = exactType/2;
211+
predicate candType(TypeFlowNode n, Unit u) { hasProperty(n) and exists(u) }
175212

176-
predicate supportsType = exactType/2;
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+
}
285+
286+
module TypeFlow<LocationSig Location, TypeFlowInput<Location> I> {
287+
private import I
288+
289+
private module UfInput implements UniversalFlowInput<Location> {
290+
class TypeFlowNode = I::TypeFlowNode;
291+
292+
predicate step = I::step/2;
293+
294+
predicate isNullValue = I::isNullValue/1;
295+
296+
predicate isExcludedFromNullAnalysis = I::isExcludedFromNullAnalysis/1;
297+
}
298+
299+
private module UnivFlow = UfMake<Location, UfInput>;
300+
301+
private module ExactTypeProperty implements UnivFlow::PropertySig {
302+
class Prop = Type;
303+
304+
predicate hasPropertyBase = exactTypeBase/2;
177305
}
178306

179307
/**
180308
* Holds if the runtime type of `n` is exactly `t` and if this bound is a
181309
* non-trivial lower bound, that is, `t` has a subtype.
182310
*/
183-
private predicate exactType(TypeFlowNode n, Type t) {
184-
exactTypeBase(n, t)
185-
or
186-
exists(TypeFlowNode mid | exactType(mid, t) and uniqStep(mid, n))
187-
or
188-
// The following is an optimized version of
189-
// `forex(TypeFlowNode mid | joinStepNotNull(mid, n) | exactType(mid, t))`
190-
ForAll<TypeFlowNode, RankedJoinStep, ExactTypePropagation>::flowJoin(n, t)
191-
or
192-
exists(TypeFlowScc scc |
193-
sccRepr(n, scc) and
194-
// Optimized version of
195-
// `forex(TypeFlowNode mid | sccJoinStepNotNull(mid, scc) | exactType(mid, t))`
196-
ForAll<TypeFlowScc, RankedSccJoinStep, ExactTypePropagation>::flowJoin(scc, t)
197-
)
198-
}
311+
private predicate exactType = UnivFlow::Flow<ExactTypeProperty>::hasProperty/2;
199312

200313
/**
201314
* Gets the source declaration of a direct supertype of this type, excluding itself.
@@ -226,34 +339,22 @@ module TypeFlow<LocationSig Location, TypeFlowInput<Location> I> {
226339
)
227340
}
228341

229-
private module TypeFlowPropagation implements TypePropagation {
230-
class Typ = Type;
342+
private module TypeFlowProperty implements UnivFlow::PropertySig {
343+
class Prop = Type;
231344

232-
predicate candType = typeFlow/2;
345+
bindingset[t1, t2]
346+
predicate propImplies(Type t1, Type t2) { getAnAncestor(pragma[only_bind_out](t1)) = t2 }
233347

234-
bindingset[t]
235-
predicate supportsType(TypeFlowNode mid, Type t) {
236-
exists(Type midtyp | exactType(mid, midtyp) or typeFlow(mid, midtyp) |
237-
getAnAncestor(pragma[only_bind_out](midtyp)) = t
238-
)
239-
}
348+
predicate hasPropertyBase(TypeFlowNode n, Prop t) { typeFlowBase(n, t) or exactType(n, t) }
240349
}
241350

242351
/**
243352
* Holds if the runtime type of `n` is bounded by `t` and if this bound is
244353
* likely to be better than the static type of `n`.
245354
*/
246355
private predicate typeFlow(TypeFlowNode n, Type t) {
247-
typeFlowBase(n, t)
248-
or
249-
exists(TypeFlowNode mid | typeFlow(mid, t) and uniqStep(mid, n))
250-
or
251-
ForAll<TypeFlowNode, RankedJoinStep, TypeFlowPropagation>::flowJoin(n, t)
252-
or
253-
exists(TypeFlowScc scc |
254-
sccRepr(n, scc) and
255-
ForAll<TypeFlowScc, RankedSccJoinStep, TypeFlowPropagation>::flowJoin(scc, t)
256-
)
356+
UnivFlow::Flow<TypeFlowProperty>::hasProperty(n, t) and
357+
not exactType(n, t)
257358
}
258359

259360
pragma[nomagic]
@@ -341,48 +442,28 @@ module TypeFlow<LocationSig Location, TypeFlowInput<Location> I> {
341442
*/
342443
private predicate unionTypeFlowBaseCand(TypeFlowNode n, Type t, boolean exact) {
343444
exists(TypeFlowNode next |
344-
joinStepNotNull(n, next) and
445+
UnivFlow::joinStepNotNull(n, next) and
345446
bestTypeFlowOrTypeFlowBase(n, t, exact) and
346447
not bestTypeFlowOrTypeFlowBase(next, t, exact) and
347448
not exactType(next, _)
348449
)
349450
}
350451

351-
private module HasUnionTypePropagation implements TypePropagation {
352-
class Typ = Unit;
353-
354-
predicate candType(TypeFlowNode mid, Unit unit) {
355-
exists(unit) and
356-
(unionTypeFlowBaseCand(mid, _, _) or hasUnionTypeFlow(mid))
452+
module UnionTypeFlowProperty implements UnivFlow::NullaryPropertySig {
453+
predicate hasPropertyBase(TypeFlowNode n) {
454+
unionTypeFlowBaseCand(n, _, _) or
455+
instanceofDisjunctionGuarded(n, _)
357456
}
358457

359-
predicate supportsType = candType/2;
458+
predicate barrier(TypeFlowNode n) { exactType(n, _) }
360459
}
361460

362461
/**
363462
* Holds if all incoming type flow can be traced back to a
364463
* `unionTypeFlowBaseCand`, such that we can compute a union type bound for `n`.
365464
* Disregards nodes for which we have an exact bound.
366465
*/
367-
private predicate hasUnionTypeFlow(TypeFlowNode n) {
368-
not exactType(n, _) and
369-
(
370-
// Optimized version of
371-
// `forex(TypeFlowNode mid | joinStepNotNull(mid, n) | unionTypeFlowBaseCand(mid, _, _) or hasUnionTypeFlow(mid))`
372-
ForAll<TypeFlowNode, RankedJoinStep, HasUnionTypePropagation>::flowJoin(n, _)
373-
or
374-
exists(TypeFlowScc scc |
375-
sccRepr(n, scc) and
376-
// Optimized version of
377-
// `forex(TypeFlowNode mid | sccJoinStep(mid, scc) | unionTypeFlowBaseCand(mid, _, _) or hasUnionTypeFlow(mid))`
378-
ForAll<TypeFlowScc, RankedSccJoinStep, HasUnionTypePropagation>::flowJoin(scc, _)
379-
)
380-
or
381-
exists(TypeFlowNode mid | uniqStep(mid, n) and hasUnionTypeFlow(mid))
382-
or
383-
instanceofDisjunctionGuarded(n, _)
384-
)
385-
}
466+
private predicate hasUnionTypeFlow = UnivFlow::FlowNullary<UnionTypeFlowProperty>::hasProperty/1;
386467

387468
pragma[nomagic]
388469
private Type getTypeBound(TypeFlowNode n) {
@@ -395,9 +476,9 @@ module TypeFlow<LocationSig Location, TypeFlowInput<Location> I> {
395476
private predicate unionTypeFlow0(TypeFlowNode n, Type t, boolean exact) {
396477
hasUnionTypeFlow(n) and
397478
(
398-
exists(TypeFlowNode mid | anyStep(mid, n) |
399-
unionTypeFlowBaseCand(mid, t, exact) or unionTypeFlow(mid, t, exact)
400-
)
479+
exists(TypeFlowNode mid | UnivFlow::anyStep(mid, n) | unionTypeFlow(mid, t, exact))
480+
or
481+
unionTypeFlowBaseCand(n, t, exact)
401482
or
402483
instanceofDisjunctionGuarded(n, t) and exact = false
403484
)
@@ -482,6 +563,7 @@ module TypeFlow<LocationSig Location, TypeFlowInput<Location> I> {
482563
*/
483564
predicate bestUnionType(TypeFlowNode n, Type t, boolean exact) {
484565
unionTypeFlow(n, t, exact) and
566+
not exactType(n, _) and
485567
not irrelevantUnionType(n) and
486568
not irrelevantUnionTypePart(n, t, exact)
487569
}

0 commit comments

Comments
 (0)