@@ -268,56 +268,146 @@ private module CallGraph {
268
268
)
269
269
}
270
270
271
+ /**
272
+ * A simple flow step that does not take flow through fields or flow out
273
+ * of callables into account.
274
+ */
275
+ pragma [ nomagic]
271
276
private predicate delegateFlowStep ( Expr pred , Expr succ ) {
272
277
Steps:: stepClosed ( pred , succ )
273
278
or
274
- exists ( Call call , Callable callable |
275
- callable .getUnboundDeclaration ( ) .canReturn ( pred ) and
276
- call = succ
277
- |
278
- callable = call .getTarget ( ) or
279
- callable = call .getTarget ( ) .( Method ) .getAnOverrider + ( ) or
280
- callable = call .getTarget ( ) .( Method ) .getAnUltimateImplementor ( ) or
281
- callable = getARuntimeDelegateTarget ( call , false )
282
- )
283
- or
284
279
pred = succ .( DelegateCreation ) .getArgument ( )
285
280
or
286
- exists ( AssignableDefinition def , Assignable a |
287
- a instanceof Field or
288
- a instanceof Property
289
- |
290
- a = def .getTarget ( ) and
291
- succ .( AssignableRead ) = a .getAnAccess ( ) and
292
- pred = def .getSource ( )
293
- )
294
- or
295
281
exists ( AddEventExpr ae | succ .( EventAccess ) .getTarget ( ) = ae .getTarget ( ) |
296
282
pred = ae .getRValue ( )
297
283
)
298
284
}
299
285
300
- private predicate reachesDelegateCall ( Expr e ) {
301
- delegateCall ( _ , e , _)
286
+ private predicate delegateCreationReaches ( Callable c , Expr e ) {
287
+ delegateCreation ( e , c , _)
302
288
or
303
- exists ( Expr mid | reachesDelegateCall ( mid ) | delegateFlowStep ( e , mid ) )
289
+ exists ( Expr mid |
290
+ delegateCreationReaches ( c , mid ) and
291
+ delegateFlowStep ( mid , e )
292
+ )
304
293
}
305
294
306
- pragma [ nomagic]
307
- private predicate delegateFlowStepReaches ( Expr pred , Expr succ ) {
308
- delegateFlowStep ( pred , succ ) and
309
- reachesDelegateCall ( succ )
295
+ private predicate reachesDelegateCall ( Expr e , Call c , boolean libraryDelegateCall ) {
296
+ delegateCall ( c , e , libraryDelegateCall )
297
+ or
298
+ exists ( Expr mid |
299
+ reachesDelegateCall ( mid , c , libraryDelegateCall ) and
300
+ delegateFlowStep ( e , mid )
301
+ )
310
302
}
311
303
312
- private Expr delegateCallSource ( Callable c ) {
313
- delegateCreation ( result , c , _)
314
- or
315
- delegateFlowStepReaches ( delegateCallSource ( c ) , result )
304
+ /**
305
+ * A "busy" flow element, that is, a node in the data-flow graph that typically
306
+ * has a large fan-in or a large fan-out (or both).
307
+ *
308
+ * For such busy elements, we do not track flow directly from all delegate
309
+ * creations, but instead we first perform a flow analysis between busy elements,
310
+ * and then only in the end join up with delegate creations and delegate calls.
311
+ */
312
+ abstract private class BusyFlowElement extends Element {
313
+ pragma [ nomagic]
314
+ abstract Expr getAnInput ( ) ;
315
+
316
+ pragma [ nomagic]
317
+ abstract Expr getAnOutput ( ) ;
318
+
319
+ /** Holds if this element can be reached from expression `e`. */
320
+ pragma [ nomagic]
321
+ private predicate exprReaches ( Expr e ) {
322
+ this .reachesCall ( _) and
323
+ e = this .getAnInput ( )
324
+ or
325
+ exists ( Expr mid |
326
+ this .exprReaches ( mid ) and
327
+ delegateFlowStep ( e , mid )
328
+ )
329
+ }
330
+
331
+ /**
332
+ * Holds if this element can reach a delegate call `c` directly without
333
+ * passing through another busy element.
334
+ */
335
+ pragma [ nomagic]
336
+ predicate delegateCall ( Call c , boolean libraryDelegateCall ) {
337
+ reachesDelegateCall ( this .getAnOutput ( ) , c , libraryDelegateCall )
338
+ }
339
+
340
+ pragma [ nomagic]
341
+ private BusyFlowElement getASuccessor ( ) { result .exprReaches ( this .getAnOutput ( ) ) }
342
+
343
+ /**
344
+ * Holds if this element reaches another busy element `other`,
345
+ * which can reach a delegate call directly without passing
346
+ * through another busy element.
347
+ */
348
+ pragma [ nomagic]
349
+ private predicate reachesCall ( BusyFlowElement other ) {
350
+ this = other and
351
+ other .delegateCall ( _, _)
352
+ or
353
+ this .getASuccessor ( ) .reachesCall ( other )
354
+ }
355
+
356
+ /** Holds if this element is reached by a delegate creation for `c`. */
357
+ pragma [ nomagic]
358
+ predicate isReachedBy ( Callable c ) {
359
+ exists ( BusyFlowElement pred |
360
+ pred .reachesCall ( this ) and
361
+ delegateCreationReaches ( c , pred .getAnInput ( ) )
362
+ )
363
+ }
364
+ }
365
+
366
+ private class TFieldOrProperty = @field or @property;
367
+
368
+ private class FieldOrPropertyFlow extends BusyFlowElement , Assignable , TFieldOrProperty {
369
+ final override Expr getAnInput ( ) {
370
+ exists ( Assignable target |
371
+ target = this .getUnboundDeclaration ( ) and
372
+ result = target .getAnAssignedValue ( )
373
+ )
374
+ }
375
+
376
+ final override AssignableRead getAnOutput ( ) {
377
+ exists ( Assignable target |
378
+ target = this .getUnboundDeclaration ( ) and
379
+ result = target .getAnAccess ( )
380
+ )
381
+ }
382
+ }
383
+
384
+ private class CallOutputFlow extends BusyFlowElement , Callable {
385
+ final override Expr getAnInput ( ) { this .canReturn ( result ) }
386
+
387
+ final override Call getAnOutput ( ) {
388
+ exists ( Callable target | this = target .getUnboundDeclaration ( ) |
389
+ target = result .getTarget ( ) or
390
+ target = result .getTarget ( ) .( Method ) .getAnOverrider + ( ) or
391
+ target = result .getTarget ( ) .( Method ) .getAnUltimateImplementor ( ) or
392
+ target = getARuntimeDelegateTarget ( result , false )
393
+ )
394
+ }
316
395
}
317
396
318
397
/** Gets a run-time target for the delegate call `c`. */
398
+ pragma [ nomagic]
319
399
Callable getARuntimeDelegateTarget ( Call c , boolean libraryDelegateCall ) {
320
- delegateCall ( c , delegateCallSource ( result ) , libraryDelegateCall )
400
+ // directly resolvable without going through a "busy" element
401
+ exists ( Expr e |
402
+ delegateCreationReaches ( result , e ) and
403
+ delegateCall ( c , e , libraryDelegateCall )
404
+ )
405
+ or
406
+ // resolvable by going through one or more "busy" elements
407
+ exists ( BusyFlowElement busy |
408
+ busy .isReachedBy ( result ) and
409
+ busy .delegateCall ( c , libraryDelegateCall )
410
+ )
321
411
}
322
412
}
323
413
0 commit comments