@@ -26,15 +26,243 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) {
26
26
tupleLimit = 1000
27
27
}
28
28
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
+
29
243
cached
30
244
private 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
+
31
259
/**
32
260
* Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
33
261
* The instance parameter is considered to have index `-1`.
34
262
*/
35
263
pragma [ nomagic]
36
264
private predicate viableParam ( DataFlowCall call , int i , ParameterNode p ) {
37
- p .isParameterOf ( viableCallable ( call ) , i )
265
+ p .isParameterOf ( viableCallableExt ( call ) , i )
38
266
}
39
267
40
268
/**
@@ -52,7 +280,7 @@ private module Cached {
52
280
53
281
pragma [ nomagic]
54
282
private ReturnPosition viableReturnPos ( DataFlowCall call , ReturnKindExt kind ) {
55
- viableCallable ( call ) = result .getCallable ( ) and
283
+ viableCallableExt ( call ) = result .getCallable ( ) and
56
284
kind = result .getKind ( )
57
285
}
58
286
@@ -317,17 +545,46 @@ private module Cached {
317
545
318
546
cached
319
547
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
+
320
577
/**
321
578
* Holds if the call context `ctx` reduces the set of viable run-time
322
579
* dispatch targets of call `call` in `c`.
323
580
*/
324
581
cached
325
582
predicate reducedViableImplInCallContext ( DataFlowCall call , DataFlowCallable c , DataFlowCall ctx ) {
326
583
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
331
588
ctxtgts < tgts
332
589
)
333
590
}
@@ -339,7 +596,7 @@ private module Cached {
339
596
*/
340
597
cached
341
598
DataFlowCallable prunedViableImplInCallContext ( DataFlowCall call , DataFlowCall ctx ) {
342
- result = viableImplInCallContext ( call , ctx ) and
599
+ result = viableImplInCallContextExt ( call , ctx ) and
343
600
reducedViableImplInCallContext ( call , _, ctx )
344
601
}
345
602
@@ -351,10 +608,10 @@ private module Cached {
351
608
cached
352
609
predicate reducedViableImplInReturn ( DataFlowCallable c , DataFlowCall call ) {
353
610
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
358
615
ctxtgts < tgts
359
616
)
360
617
}
@@ -367,7 +624,7 @@ private module Cached {
367
624
*/
368
625
cached
369
626
DataFlowCallable prunedViableImplInCallContextReverse ( DataFlowCall call , DataFlowCall ctx ) {
370
- result = viableImplInCallContext ( call , ctx ) and
627
+ result = viableImplInCallContextExt ( call , ctx ) and
371
628
reducedViableImplInReturn ( result , call )
372
629
}
373
630
}
@@ -481,6 +738,11 @@ private module Cached {
481
738
TBooleanNone ( ) or
482
739
TBooleanSome ( boolean b ) { b = true or b = false }
483
740
741
+ cached
742
+ newtype TDataFlowCallOption =
743
+ TDataFlowCallNone ( ) or
744
+ TDataFlowCallSome ( DataFlowCall call )
745
+
484
746
cached
485
747
newtype TTypedContent = MkTypedContent ( Content c , DataFlowType t ) { store ( _, c , _, _, t ) }
486
748
@@ -777,7 +1039,7 @@ ReturnPosition getReturnPosition(ReturnNodeExt ret) {
777
1039
778
1040
bindingset [ cc, callable]
779
1041
predicate resolveReturn ( CallContext cc , DataFlowCallable callable , DataFlowCall call ) {
780
- cc instanceof CallContextAny and callable = viableCallable ( call )
1042
+ cc instanceof CallContextAny and callable = viableCallableExt ( call )
781
1043
or
782
1044
exists ( DataFlowCallable c0 , DataFlowCall call0 |
783
1045
call0 .getEnclosingCallable ( ) = callable and
@@ -791,14 +1053,14 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
791
1053
exists ( DataFlowCall ctx | cc = TSpecificCall ( ctx ) |
792
1054
if reducedViableImplInCallContext ( call , _, ctx )
793
1055
then result = prunedViableImplInCallContext ( call , ctx )
794
- else result = viableCallable ( call )
1056
+ else result = viableCallableExt ( call )
795
1057
)
796
1058
or
797
- result = viableCallable ( call ) and cc instanceof CallContextSomeCall
1059
+ result = viableCallableExt ( call ) and cc instanceof CallContextSomeCall
798
1060
or
799
- result = viableCallable ( call ) and cc instanceof CallContextAny
1061
+ result = viableCallableExt ( call ) and cc instanceof CallContextAny
800
1062
or
801
- result = viableCallable ( call ) and cc instanceof CallContextReturn
1063
+ result = viableCallableExt ( call ) and cc instanceof CallContextReturn
802
1064
}
803
1065
804
1066
predicate read = readStep / 3 ;
@@ -812,6 +1074,19 @@ class BooleanOption extends TBooleanOption {
812
1074
}
813
1075
}
814
1076
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
+
815
1090
/** Content tagged with the type of a containing object. */
816
1091
class TypedContent extends MkTypedContent {
817
1092
private Content c ;
0 commit comments