@@ -268,56 +268,146 @@ private module CallGraph {
268268 )
269269 }
270270
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]
271276 private predicate delegateFlowStep ( Expr pred , Expr succ ) {
272277 Steps:: stepClosed ( pred , succ )
273278 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
284279 pred = succ .( DelegateCreation ) .getArgument ( )
285280 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
295281 exists ( AddEventExpr ae | succ .( EventAccess ) .getTarget ( ) = ae .getTarget ( ) |
296282 pred = ae .getRValue ( )
297283 )
298284 }
299285
300- private predicate reachesDelegateCall ( Expr e ) {
301- delegateCall ( _ , e , _)
286+ private predicate delegateCreationReaches ( Callable c , Expr e ) {
287+ delegateCreation ( e , c , _)
302288 or
303- exists ( Expr mid | reachesDelegateCall ( mid ) | delegateFlowStep ( e , mid ) )
289+ exists ( Expr mid |
290+ delegateCreationReaches ( c , mid ) and
291+ delegateFlowStep ( mid , e )
292+ )
304293 }
305294
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+ )
310302 }
311303
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+ }
316395 }
317396
318397 /** Gets a run-time target for the delegate call `c`. */
398+ pragma [ nomagic]
319399 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+ )
321411 }
322412 }
323413
0 commit comments