@@ -26,15 +26,243 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) {
2626 tupleLimit = 1000
2727}
2828
29+ /**
30+ * Provides a simple data-flow analysis for resolving lambda calls. The analysis
31+ * currently excludes read-steps, store-steps, and flow-through.
32+ *
33+ * The analysis uses non-linear recursion: When computing a flow path in or out
34+ * of a call, we use the results of the analysis recursively to resolve lamba
35+ * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly.
36+ */
37+ private module LambdaFlow {
38+ private predicate viableParamNonLambda ( DataFlowCall call , int i , ParameterNode p ) {
39+ p .isParameterOf ( viableCallable ( call ) , i )
40+ }
41+
42+ private predicate viableParamLambda ( DataFlowCall call , int i , ParameterNode p ) {
43+ p .isParameterOf ( viableCallableLambda ( call , _) , i )
44+ }
45+
46+ private predicate viableParamArgNonLambda ( DataFlowCall call , ParameterNode p , ArgumentNode arg ) {
47+ exists ( int i |
48+ viableParamNonLambda ( call , i , p ) and
49+ arg .argumentOf ( call , i )
50+ )
51+ }
52+
53+ private predicate viableParamArgLambda ( DataFlowCall call , ParameterNode p , ArgumentNode arg ) {
54+ exists ( int i |
55+ viableParamLambda ( call , i , p ) and
56+ arg .argumentOf ( call , i )
57+ )
58+ }
59+
60+ private newtype TReturnPositionSimple =
61+ TReturnPositionSimple0 ( DataFlowCallable c , ReturnKind kind ) {
62+ exists ( ReturnNode ret |
63+ c = getNodeEnclosingCallable ( ret ) and
64+ kind = ret .getKind ( )
65+ )
66+ }
67+
68+ pragma [ noinline]
69+ private TReturnPositionSimple getReturnPositionSimple ( ReturnNode ret , ReturnKind kind ) {
70+ result = TReturnPositionSimple0 ( getNodeEnclosingCallable ( ret ) , kind )
71+ }
72+
73+ pragma [ nomagic]
74+ private TReturnPositionSimple viableReturnPosNonLambda ( DataFlowCall call , ReturnKind kind ) {
75+ result = TReturnPositionSimple0 ( viableCallable ( call ) , kind )
76+ }
77+
78+ pragma [ nomagic]
79+ private TReturnPositionSimple viableReturnPosLambda (
80+ DataFlowCall call , DataFlowCallOption lastCall , ReturnKind kind
81+ ) {
82+ result = TReturnPositionSimple0 ( viableCallableLambda ( call , lastCall ) , kind )
83+ }
84+
85+ private predicate viableReturnPosOutNonLambda (
86+ DataFlowCall call , TReturnPositionSimple pos , OutNode out
87+ ) {
88+ exists ( ReturnKind kind |
89+ pos = viableReturnPosNonLambda ( call , kind ) and
90+ out = getAnOutNode ( call , kind )
91+ )
92+ }
93+
94+ private predicate viableReturnPosOutLambda (
95+ DataFlowCall call , DataFlowCallOption lastCall , TReturnPositionSimple pos , OutNode out
96+ ) {
97+ exists ( ReturnKind kind |
98+ pos = viableReturnPosLambda ( call , lastCall , kind ) and
99+ out = getAnOutNode ( call , kind )
100+ )
101+ }
102+
103+ /**
104+ * Holds if data can flow (inter-procedurally) from `node` (of type `t`) to
105+ * the lambda call `lambdaCall`.
106+ *
107+ * The parameter `toReturn` indicates whether the path from `node` to
108+ * `lambdaCall` goes through a return, and `toJump` whether the path goes
109+ * through a jump step.
110+ *
111+ * The call context `lastCall` records the last call on the path from `node`
112+ * to `lambdaCall`, if any. That is, `lastCall` is able to target the enclosing
113+ * callable of `lambdaCall`.
114+ */
115+ pragma [ nomagic]
116+ predicate revLambdaFlow (
117+ DataFlowCall lambdaCall , LambdaCallKind kind , Node node , DataFlowType t , boolean toReturn ,
118+ boolean toJump , DataFlowCallOption lastCall
119+ ) {
120+ revLambdaFlow0 ( lambdaCall , kind , node , t , toReturn , toJump , lastCall ) and
121+ if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode
122+ then compatibleTypes ( t , getNodeType ( node ) )
123+ else any ( )
124+ }
125+
126+ pragma [ nomagic]
127+ predicate revLambdaFlow0 (
128+ DataFlowCall lambdaCall , LambdaCallKind kind , Node node , DataFlowType t , boolean toReturn ,
129+ boolean toJump , DataFlowCallOption lastCall
130+ ) {
131+ lambdaCall ( lambdaCall , kind , node ) and
132+ t = getNodeType ( node ) and
133+ toReturn = false and
134+ toJump = false and
135+ lastCall = TDataFlowCallNone ( )
136+ or
137+ // local flow
138+ exists ( Node mid , DataFlowType t0 |
139+ revLambdaFlow ( lambdaCall , kind , mid , t0 , toReturn , toJump , lastCall )
140+ |
141+ simpleLocalFlowStep ( node , mid ) and
142+ t = t0
143+ or
144+ exists ( boolean preservesValue |
145+ additionalLambdaFlowStep ( node , mid , preservesValue ) and
146+ getNodeEnclosingCallable ( node ) = getNodeEnclosingCallable ( mid )
147+ |
148+ preservesValue = false and
149+ t = getNodeType ( node )
150+ or
151+ preservesValue = true and
152+ t = t0
153+ )
154+ )
155+ or
156+ // jump step
157+ exists ( Node mid , DataFlowType t0 |
158+ revLambdaFlow ( lambdaCall , kind , mid , t0 , _, _, _) and
159+ toReturn = false and
160+ toJump = true and
161+ lastCall = TDataFlowCallNone ( )
162+ |
163+ jumpStep ( node , mid ) and
164+ t = t0
165+ or
166+ exists ( boolean preservesValue |
167+ additionalLambdaFlowStep ( node , mid , preservesValue ) and
168+ getNodeEnclosingCallable ( node ) != getNodeEnclosingCallable ( mid )
169+ |
170+ preservesValue = false and
171+ t = getNodeType ( node )
172+ or
173+ preservesValue = true and
174+ t = t0
175+ )
176+ )
177+ or
178+ // flow into a callable
179+ exists ( ParameterNode p , DataFlowCallOption lastCall0 , DataFlowCall call |
180+ revLambdaFlowIn ( lambdaCall , kind , p , t , toJump , lastCall0 ) and
181+ (
182+ if lastCall0 = TDataFlowCallNone ( ) and toJump = false
183+ then lastCall = TDataFlowCallSome ( call )
184+ else lastCall = lastCall0
185+ ) and
186+ toReturn = false
187+ |
188+ viableParamArgNonLambda ( call , p , node )
189+ or
190+ viableParamArgLambda ( call , p , node ) // non-linear recursion
191+ )
192+ or
193+ // flow out of a callable
194+ exists ( TReturnPositionSimple pos |
195+ revLambdaFlowOut ( lambdaCall , kind , pos , t , toJump , lastCall ) and
196+ getReturnPositionSimple ( node , node .( ReturnNode ) .getKind ( ) ) = pos and
197+ toReturn = true
198+ )
199+ }
200+
201+ pragma [ nomagic]
202+ predicate revLambdaFlowOutLambdaCall (
203+ DataFlowCall lambdaCall , LambdaCallKind kind , OutNode out , DataFlowType t , boolean toJump ,
204+ DataFlowCall call , DataFlowCallOption lastCall
205+ ) {
206+ revLambdaFlow ( lambdaCall , kind , out , t , _, toJump , lastCall ) and
207+ exists ( ReturnKindExt rk |
208+ out = rk .getAnOutNode ( call ) and
209+ lambdaCall ( call , _, _)
210+ )
211+ }
212+
213+ pragma [ nomagic]
214+ predicate revLambdaFlowOut (
215+ DataFlowCall lambdaCall , LambdaCallKind kind , TReturnPositionSimple pos , DataFlowType t ,
216+ boolean toJump , DataFlowCallOption lastCall
217+ ) {
218+ exists ( DataFlowCall call , OutNode out |
219+ revLambdaFlow ( lambdaCall , kind , out , t , _, toJump , lastCall ) and
220+ viableReturnPosOutNonLambda ( call , pos , out )
221+ or
222+ // non-linear recursion
223+ revLambdaFlowOutLambdaCall ( lambdaCall , kind , out , t , toJump , call , lastCall ) and
224+ viableReturnPosOutLambda ( call , _, pos , out )
225+ )
226+ }
227+
228+ pragma [ nomagic]
229+ predicate revLambdaFlowIn (
230+ DataFlowCall lambdaCall , LambdaCallKind kind , ParameterNode p , DataFlowType t , boolean toJump ,
231+ DataFlowCallOption lastCall
232+ ) {
233+ revLambdaFlow ( lambdaCall , kind , p , t , false , toJump , lastCall )
234+ }
235+ }
236+
237+ private DataFlowCallable viableCallableExt ( DataFlowCall call ) {
238+ result = viableCallable ( call )
239+ or
240+ result = viableCallableLambda ( call , _)
241+ }
242+
29243cached
30244private module Cached {
245+ /**
246+ * Gets a viable target for the lambda call `call`.
247+ *
248+ * `lastCall` records the call required to reach `call` in order for the result
249+ * to be a viable target, if any.
250+ */
251+ cached
252+ DataFlowCallable viableCallableLambda ( DataFlowCall call , DataFlowCallOption lastCall ) {
253+ exists ( Node creation , LambdaCallKind kind |
254+ LambdaFlow:: revLambdaFlow ( call , kind , creation , _, _, _, lastCall ) and
255+ lambdaCreation ( creation , kind , result )
256+ )
257+ }
258+
31259 /**
32260 * Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
33261 * The instance parameter is considered to have index `-1`.
34262 */
35263 pragma [ nomagic]
36264 private predicate viableParam ( DataFlowCall call , int i , ParameterNode p ) {
37- p .isParameterOf ( viableCallable ( call ) , i )
265+ p .isParameterOf ( viableCallableExt ( call ) , i )
38266 }
39267
40268 /**
@@ -52,7 +280,7 @@ private module Cached {
52280
53281 pragma [ nomagic]
54282 private ReturnPosition viableReturnPos ( DataFlowCall call , ReturnKindExt kind ) {
55- viableCallable ( call ) = result .getCallable ( ) and
283+ viableCallableExt ( call ) = result .getCallable ( ) and
56284 kind = result .getKind ( )
57285 }
58286
@@ -317,17 +545,46 @@ private module Cached {
317545
318546 cached
319547 private module DispatchWithCallContext {
548+ /**
549+ * Holds if the set of viable implementations that can be called by `call`
550+ * might be improved by knowing the call context.
551+ */
552+ pragma [ nomagic]
553+ private predicate mayBenefitFromCallContextExt ( DataFlowCall call , DataFlowCallable callable ) {
554+ mayBenefitFromCallContext ( call , callable )
555+ or
556+ callable = call .getEnclosingCallable ( ) and
557+ exists ( viableCallableLambda ( call , TDataFlowCallSome ( _) ) )
558+ }
559+
560+ /**
561+ * Gets a viable dispatch target of `call` in the context `ctx`. This is
562+ * restricted to those `call`s for which a context might make a difference.
563+ */
564+ pragma [ nomagic]
565+ private DataFlowCallable viableImplInCallContextExt ( DataFlowCall call , DataFlowCall ctx ) {
566+ result = viableImplInCallContext ( call , ctx )
567+ or
568+ result = viableCallableLambda ( call , TDataFlowCallSome ( ctx ) )
569+ or
570+ exists ( DataFlowCallable enclosing |
571+ mayBenefitFromCallContextExt ( call , enclosing ) and
572+ enclosing = viableCallableExt ( ctx ) and
573+ result = viableCallableLambda ( call , TDataFlowCallNone ( ) )
574+ )
575+ }
576+
320577 /**
321578 * Holds if the call context `ctx` reduces the set of viable run-time
322579 * dispatch targets of call `call` in `c`.
323580 */
324581 cached
325582 predicate reducedViableImplInCallContext ( DataFlowCall call , DataFlowCallable c , DataFlowCall ctx ) {
326583 exists ( int tgts , int ctxtgts |
327- mayBenefitFromCallContext ( call , c ) and
328- c = viableCallable ( ctx ) and
329- ctxtgts = count ( viableImplInCallContext ( call , ctx ) ) and
330- tgts = strictcount ( viableCallable ( call ) ) and
584+ mayBenefitFromCallContextExt ( call , c ) and
585+ c = viableCallableExt ( ctx ) and
586+ ctxtgts = count ( viableImplInCallContextExt ( call , ctx ) ) and
587+ tgts = strictcount ( viableCallableExt ( call ) ) and
331588 ctxtgts < tgts
332589 )
333590 }
@@ -339,7 +596,7 @@ private module Cached {
339596 */
340597 cached
341598 DataFlowCallable prunedViableImplInCallContext ( DataFlowCall call , DataFlowCall ctx ) {
342- result = viableImplInCallContext ( call , ctx ) and
599+ result = viableImplInCallContextExt ( call , ctx ) and
343600 reducedViableImplInCallContext ( call , _, ctx )
344601 }
345602
@@ -351,10 +608,10 @@ private module Cached {
351608 cached
352609 predicate reducedViableImplInReturn ( DataFlowCallable c , DataFlowCall call ) {
353610 exists ( int tgts , int ctxtgts |
354- mayBenefitFromCallContext ( call , _) and
355- c = viableCallable ( call ) and
356- ctxtgts = count ( DataFlowCall ctx | c = viableImplInCallContext ( call , ctx ) ) and
357- tgts = strictcount ( DataFlowCall ctx | viableCallable ( ctx ) = call .getEnclosingCallable ( ) ) and
611+ mayBenefitFromCallContextExt ( call , _) and
612+ c = viableCallableExt ( call ) and
613+ ctxtgts = count ( DataFlowCall ctx | c = viableImplInCallContextExt ( call , ctx ) ) and
614+ tgts = strictcount ( DataFlowCall ctx | viableCallableExt ( ctx ) = call .getEnclosingCallable ( ) ) and
358615 ctxtgts < tgts
359616 )
360617 }
@@ -367,7 +624,7 @@ private module Cached {
367624 */
368625 cached
369626 DataFlowCallable prunedViableImplInCallContextReverse ( DataFlowCall call , DataFlowCall ctx ) {
370- result = viableImplInCallContext ( call , ctx ) and
627+ result = viableImplInCallContextExt ( call , ctx ) and
371628 reducedViableImplInReturn ( result , call )
372629 }
373630 }
@@ -481,6 +738,11 @@ private module Cached {
481738 TBooleanNone ( ) or
482739 TBooleanSome ( boolean b ) { b = true or b = false }
483740
741+ cached
742+ newtype TDataFlowCallOption =
743+ TDataFlowCallNone ( ) or
744+ TDataFlowCallSome ( DataFlowCall call )
745+
484746 cached
485747 newtype TTypedContent = MkTypedContent ( Content c , DataFlowType t ) { store ( _, c , _, _, t ) }
486748
@@ -777,7 +1039,7 @@ ReturnPosition getReturnPosition(ReturnNodeExt ret) {
7771039
7781040bindingset [ cc, callable]
7791041predicate resolveReturn ( CallContext cc , DataFlowCallable callable , DataFlowCall call ) {
780- cc instanceof CallContextAny and callable = viableCallable ( call )
1042+ cc instanceof CallContextAny and callable = viableCallableExt ( call )
7811043 or
7821044 exists ( DataFlowCallable c0 , DataFlowCall call0 |
7831045 call0 .getEnclosingCallable ( ) = callable and
@@ -791,14 +1053,14 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
7911053 exists ( DataFlowCall ctx | cc = TSpecificCall ( ctx ) |
7921054 if reducedViableImplInCallContext ( call , _, ctx )
7931055 then result = prunedViableImplInCallContext ( call , ctx )
794- else result = viableCallable ( call )
1056+ else result = viableCallableExt ( call )
7951057 )
7961058 or
797- result = viableCallable ( call ) and cc instanceof CallContextSomeCall
1059+ result = viableCallableExt ( call ) and cc instanceof CallContextSomeCall
7981060 or
799- result = viableCallable ( call ) and cc instanceof CallContextAny
1061+ result = viableCallableExt ( call ) and cc instanceof CallContextAny
8001062 or
801- result = viableCallable ( call ) and cc instanceof CallContextReturn
1063+ result = viableCallableExt ( call ) and cc instanceof CallContextReturn
8021064}
8031065
8041066predicate read = readStep / 3 ;
@@ -812,6 +1074,19 @@ class BooleanOption extends TBooleanOption {
8121074 }
8131075}
8141076
1077+ /** An optional `DataFlowCall`. */
1078+ class DataFlowCallOption extends TDataFlowCallOption {
1079+ string toString ( ) {
1080+ this = TDataFlowCallNone ( ) and
1081+ result = "(none)"
1082+ or
1083+ exists ( DataFlowCall call |
1084+ this = TDataFlowCallSome ( call ) and
1085+ result = call .toString ( )
1086+ )
1087+ }
1088+ }
1089+
8151090/** Content tagged with the type of a containing object. */
8161091class TypedContent extends MkTypedContent {
8171092 private Content c ;
0 commit comments