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