@@ -186,10 +186,17 @@ module Private {
186
186
TArgumentSummaryComponent ( int i ) { parameterPosition ( i ) } or
187
187
TReturnSummaryComponent ( ReturnKind rk )
188
188
189
+ private TSummaryComponent thisParam ( ) {
190
+ result = TParameterSummaryComponent ( instanceParameterPosition ( ) )
191
+ }
192
+
189
193
newtype TSummaryComponentStack =
190
194
TSingletonSummaryComponentStack ( SummaryComponent c ) or
191
195
TConsSummaryComponentStack ( SummaryComponent head , SummaryComponentStack tail ) {
192
196
tail .( RequiredSummaryComponentStack ) .required ( head )
197
+ or
198
+ tail .( RequiredSummaryComponentStack ) .required ( TParameterSummaryComponent ( _) ) and
199
+ head = thisParam ( )
193
200
}
194
201
195
202
pragma [ nomagic]
@@ -198,21 +205,63 @@ module Private {
198
205
boolean preservesValue
199
206
) {
200
207
c .propagatesFlow ( input , output , preservesValue )
208
+ or
209
+ // observe side effects of callbacks on input arguments
210
+ c .propagatesFlow ( output , input , preservesValue ) and
211
+ preservesValue = true and
212
+ isCallbackParameter ( input ) and
213
+ isContentOfArgument ( output )
214
+ or
215
+ // flow from the receiver of a callback into the instance-parameter
216
+ exists ( SummaryComponentStack s , SummaryComponentStack callbackRef |
217
+ c .propagatesFlow ( s , _, _) or c .propagatesFlow ( _, s , _)
218
+ |
219
+ callbackRef = s .drop ( _) and
220
+ ( isCallbackParameter ( callbackRef ) or callbackRef .head ( ) = TReturnSummaryComponent ( _) ) and
221
+ input = callbackRef .tail ( ) and
222
+ output = TConsSummaryComponentStack ( thisParam ( ) , input ) and
223
+ preservesValue = true
224
+ )
225
+ }
226
+
227
+ private predicate isCallbackParameter ( SummaryComponentStack s ) {
228
+ s .head ( ) = TParameterSummaryComponent ( _) and exists ( s .tail ( ) )
229
+ }
230
+
231
+ private predicate isContentOfArgument ( SummaryComponentStack s ) {
232
+ s .head ( ) = TContentSummaryComponent ( _) and isContentOfArgument ( s .tail ( ) )
233
+ or
234
+ s = TSingletonSummaryComponentStack ( TArgumentSummaryComponent ( _) )
235
+ }
236
+
237
+ private predicate outputState ( SummarizedCallable c , SummaryComponentStack s ) {
238
+ summary ( c , _, s , _)
239
+ or
240
+ exists ( SummaryComponentStack out |
241
+ outputState ( c , out ) and
242
+ out .head ( ) = TContentSummaryComponent ( _) and
243
+ s = out .tail ( )
244
+ )
245
+ or
246
+ // Add the argument node corresponding to the requested post-update node
247
+ inputState ( c , s ) and isCallbackParameter ( s )
248
+ }
249
+
250
+ private predicate inputState ( SummarizedCallable c , SummaryComponentStack s ) {
251
+ summary ( c , s , _, _)
252
+ or
253
+ exists ( SummaryComponentStack inp | inputState ( c , inp ) and s = inp .tail ( ) )
254
+ or
255
+ exists ( SummaryComponentStack out |
256
+ outputState ( c , out ) and
257
+ out .head ( ) = TParameterSummaryComponent ( _) and
258
+ s = out .tail ( )
259
+ )
201
260
}
202
261
203
262
private newtype TSummaryNodeState =
204
- TSummaryNodeInputState ( SummaryComponentStack s ) {
205
- exists ( SummaryComponentStack input |
206
- summary ( _, input , _, _) and
207
- s = input .drop ( _)
208
- )
209
- } or
210
- TSummaryNodeOutputState ( SummaryComponentStack s ) {
211
- exists ( SummaryComponentStack output |
212
- summary ( _, _, output , _) and
213
- s = output .drop ( _)
214
- )
215
- }
263
+ TSummaryNodeInputState ( SummaryComponentStack s ) { inputState ( _, s ) } or
264
+ TSummaryNodeOutputState ( SummaryComponentStack s ) { outputState ( _, s ) }
216
265
217
266
/**
218
267
* A state used to break up (complex) flow summaries into atomic flow steps.
@@ -238,20 +287,14 @@ module Private {
238
287
pragma [ nomagic]
239
288
predicate isInputState ( SummarizedCallable c , SummaryComponentStack s ) {
240
289
this = TSummaryNodeInputState ( s ) and
241
- exists ( SummaryComponentStack input |
242
- summary ( c , input , _, _) and
243
- s = input .drop ( _)
244
- )
290
+ inputState ( c , s )
245
291
}
246
292
247
293
/** Holds if this state is a valid output state for `c`. */
248
294
pragma [ nomagic]
249
295
predicate isOutputState ( SummarizedCallable c , SummaryComponentStack s ) {
250
296
this = TSummaryNodeOutputState ( s ) and
251
- exists ( SummaryComponentStack output |
252
- summary ( c , _, output , _) and
253
- s = output .drop ( _)
254
- )
297
+ outputState ( c , s )
255
298
}
256
299
257
300
/** Gets a textual representation of this state. */
@@ -331,19 +374,12 @@ module Private {
331
374
receiver = summaryNodeInputState ( c , s .drop ( 1 ) )
332
375
}
333
376
334
- private Node pre ( Node post ) {
335
- summaryPostUpdateNode ( post , result )
336
- or
337
- not summaryPostUpdateNode ( post , _) and
338
- result = post
339
- }
340
-
341
377
private predicate callbackInput (
342
378
SummarizedCallable c , SummaryComponentStack s , Node receiver , int i
343
379
) {
344
380
any ( SummaryNodeState state ) .isOutputState ( c , s ) and
345
381
s .head ( ) = TParameterSummaryComponent ( i ) and
346
- receiver = pre ( summaryNodeOutputState ( c , s .drop ( 1 ) ) )
382
+ receiver = summaryNodeInputState ( c , s .drop ( 1 ) )
347
383
}
348
384
349
385
/** Holds if a call targeting `receiver` should be synthesized inside `c`. */
@@ -395,7 +431,7 @@ module Private {
395
431
or
396
432
exists ( int i | head = TParameterSummaryComponent ( i ) |
397
433
result =
398
- getCallbackParameterType ( getNodeType ( summaryNodeOutputState ( pragma [ only_bind_out ] ( c ) ,
434
+ getCallbackParameterType ( getNodeType ( summaryNodeInputState ( pragma [ only_bind_out ] ( c ) ,
399
435
s .drop ( 1 ) ) ) , i )
400
436
)
401
437
)
@@ -421,10 +457,16 @@ module Private {
421
457
}
422
458
423
459
/** Holds if summary node `post` is a post-update node with pre-update node `pre`. */
424
- predicate summaryPostUpdateNode ( Node post , ParamNode pre ) {
460
+ predicate summaryPostUpdateNode ( Node post , Node pre ) {
425
461
exists ( SummarizedCallable c , int i |
426
462
isParameterPostUpdate ( post , c , i ) and
427
- pre .isParameterOf ( c , i )
463
+ pre .( ParamNode ) .isParameterOf ( c , i )
464
+ )
465
+ or
466
+ exists ( SummarizedCallable callable , SummaryComponentStack s |
467
+ callbackInput ( callable , s , _, _) and
468
+ pre = summaryNodeOutputState ( callable , s ) and
469
+ post = summaryNodeInputState ( callable , s )
428
470
)
429
471
}
430
472
@@ -462,7 +504,11 @@ module Private {
462
504
// for `StringBuilder.append(x)` with a specified value flow from qualifier to
463
505
// return value and taint flow from argument 0 to the qualifier, then this
464
506
// allows us to infer taint flow from argument 0 to the return value.
465
- summaryPostUpdateNode ( pred , succ ) and preservesValue = true
507
+ succ instanceof ParamNode and summaryPostUpdateNode ( pred , succ ) and preservesValue = true
508
+ or
509
+ // Similarly we would like to chain together summaries where values get passed
510
+ // into callbacks along the way.
511
+ pred instanceof ArgNode and summaryPostUpdateNode ( succ , pred ) and preservesValue = true
466
512
}
467
513
468
514
/**
@@ -824,4 +870,95 @@ module Private {
824
870
)
825
871
}
826
872
}
873
+
874
+ /**
875
+ * Provides query predicates for rendering the generated data flow graph for
876
+ * a summarized callable.
877
+ *
878
+ * Import this module into a `.ql` file of `@kind graph` to render the graph.
879
+ * The graph is restricted to callables from `RelevantSummarizedCallable`.
880
+ */
881
+ module RenderSummarizedCallable {
882
+ /** A summarized callable to include in the graph. */
883
+ abstract class RelevantSummarizedCallable extends SummarizedCallable { }
884
+
885
+ private newtype TNodeOrCall =
886
+ MkNode ( Node n ) {
887
+ exists ( RelevantSummarizedCallable c |
888
+ n = summaryNode ( c , _)
889
+ or
890
+ n .( ParamNode ) .isParameterOf ( c , _)
891
+ )
892
+ } or
893
+ MkCall ( DataFlowCall call ) {
894
+ call = summaryDataFlowCall ( _) and
895
+ call .getEnclosingCallable ( ) instanceof RelevantSummarizedCallable
896
+ }
897
+
898
+ private class NodeOrCall extends TNodeOrCall {
899
+ Node asNode ( ) { this = MkNode ( result ) }
900
+
901
+ DataFlowCall asCall ( ) { this = MkCall ( result ) }
902
+
903
+ string toString ( ) {
904
+ result = this .asNode ( ) .toString ( )
905
+ or
906
+ result = this .asCall ( ) .toString ( )
907
+ }
908
+
909
+ /**
910
+ * Holds if this element is at the specified location.
911
+ * The location spans column `startcolumn` of line `startline` to
912
+ * column `endcolumn` of line `endline` in file `filepath`.
913
+ * For more information, see
914
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
915
+ */
916
+ predicate hasLocationInfo (
917
+ string filepath , int startline , int startcolumn , int endline , int endcolumn
918
+ ) {
919
+ this .asNode ( ) .hasLocationInfo ( filepath , startline , startcolumn , endline , endcolumn )
920
+ or
921
+ this .asCall ( ) .hasLocationInfo ( filepath , startline , startcolumn , endline , endcolumn )
922
+ }
923
+ }
924
+
925
+ query predicate nodes ( NodeOrCall n , string key , string val ) {
926
+ key = "semmle.label" and val = n .toString ( )
927
+ }
928
+
929
+ private predicate edgesComponent ( NodeOrCall a , NodeOrCall b , string value ) {
930
+ exists ( boolean preservesValue |
931
+ Private:: Steps:: summaryLocalStep ( a .asNode ( ) , b .asNode ( ) , preservesValue ) and
932
+ if preservesValue = true then value = "value" else value = "taint"
933
+ )
934
+ or
935
+ exists ( Content c |
936
+ Private:: Steps:: summaryReadStep ( a .asNode ( ) , c , b .asNode ( ) ) and
937
+ value = "read (" + c + ")"
938
+ or
939
+ Private:: Steps:: summaryStoreStep ( a .asNode ( ) , c , b .asNode ( ) ) and
940
+ value = "store (" + c + ")"
941
+ or
942
+ Private:: Steps:: summaryClearsContent ( a .asNode ( ) , c ) and
943
+ b = a and
944
+ value = "clear (" + c + ")"
945
+ )
946
+ or
947
+ summaryPostUpdateNode ( b .asNode ( ) , a .asNode ( ) ) and
948
+ value = "post-update"
949
+ or
950
+ b .asCall ( ) = summaryDataFlowCall ( a .asNode ( ) ) and
951
+ value = "receiver"
952
+ or
953
+ exists ( int i |
954
+ summaryArgumentNode ( b .asCall ( ) , a .asNode ( ) , i ) and
955
+ value = "argument (" + i + ")"
956
+ )
957
+ }
958
+
959
+ query predicate edges ( NodeOrCall a , NodeOrCall b , string key , string value ) {
960
+ key = "semmle.label" and
961
+ value = strictconcat ( string s | edgesComponent ( a , b , s ) | s , " / " )
962
+ }
963
+ }
827
964
}
0 commit comments