@@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _
535535
536536private predicate throughFlowNodeCand1 ( Node node , Configuration config ) {
537537 nodeCand1 ( node , true , config ) and
538+ nodeCandFwd1 ( node , true , config ) and
538539 not fullBarrier ( node , config ) and
539540 not inBarrier ( node , config ) and
540541 not outBarrier ( node , config )
@@ -1523,11 +1524,50 @@ private predicate flowCandIsReturned(
15231524 )
15241525}
15251526
1527+ /**
1528+ * Holds if `argApf` is recorded as the summary context for flow reaching `node`
1529+ * and remains relevant for the following pruning stage.
1530+ */
1531+ private predicate flowCandSummaryCtx ( Node node , AccessPathFront argApf , Configuration config ) {
1532+ exists ( AccessPathFront apf |
1533+ flowCand ( node , true , _, apf , config ) and
1534+ flowCandFwd ( node , true , TAccessPathFrontSome ( argApf ) , apf , config )
1535+ )
1536+ }
1537+
1538+ /**
1539+ * Holds if a length 2 access path approximation with the head `tc` is expected
1540+ * to be expensive.
1541+ */
1542+ private predicate expensiveLen2unfolding ( TypedContent tc , Configuration config ) {
1543+ exists ( int tails , int nodes , int apLimit , int tupleLimit |
1544+ tails = strictcount ( AccessPathFront apf | flowCandConsCand ( tc , apf , config ) ) and
1545+ nodes =
1546+ strictcount ( Node n |
1547+ flowCand ( n , _, _, any ( AccessPathFrontHead apf | apf .headUsesContent ( tc ) ) , config )
1548+ or
1549+ flowCandSummaryCtx ( n , any ( AccessPathFrontHead apf | apf .headUsesContent ( tc ) ) , _)
1550+ ) and
1551+ accessPathApproxCostLimits ( apLimit , tupleLimit ) and
1552+ apLimit < tails and
1553+ tupleLimit < ( tails - 1 ) * nodes
1554+ )
1555+ }
1556+
15261557private newtype TAccessPathApprox =
15271558 TNil ( DataFlowType t ) or
1528- TConsNil ( TypedContent tc , DataFlowType t ) { flowCandConsCand ( tc , TFrontNil ( t ) , _) } or
1559+ TConsNil ( TypedContent tc , DataFlowType t ) {
1560+ flowCandConsCand ( tc , TFrontNil ( t ) , _) and
1561+ not expensiveLen2unfolding ( tc , _)
1562+ } or
15291563 TConsCons ( TypedContent tc1 , TypedContent tc2 , int len ) {
1530- flowCandConsCand ( tc1 , TFrontHead ( tc2 ) , _) and len in [ 2 .. accessPathLimit ( ) ]
1564+ flowCandConsCand ( tc1 , TFrontHead ( tc2 ) , _) and
1565+ len in [ 2 .. accessPathLimit ( ) ] and
1566+ not expensiveLen2unfolding ( tc1 , _)
1567+ } or
1568+ TCons1 ( TypedContent tc , int len ) {
1569+ len in [ 1 .. accessPathLimit ( ) ] and
1570+ expensiveLen2unfolding ( tc , _)
15311571 }
15321572
15331573/**
@@ -1622,6 +1662,49 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons {
16221662 or
16231663 len = 2 and
16241664 result = TConsNil ( tc2 , _)
1665+ or
1666+ result = TCons1 ( tc2 , len - 1 )
1667+ )
1668+ }
1669+ }
1670+
1671+ private class AccessPathApproxCons1 extends AccessPathApproxCons , TCons1 {
1672+ private TypedContent tc ;
1673+ private int len ;
1674+
1675+ AccessPathApproxCons1 ( ) { this = TCons1 ( tc , len ) }
1676+
1677+ override string toString ( ) {
1678+ if len = 1
1679+ then result = "[" + tc .toString ( ) + "]"
1680+ else result = "[" + tc .toString ( ) + ", ... (" + len .toString ( ) + ")]"
1681+ }
1682+
1683+ override TypedContent getHead ( ) { result = tc }
1684+
1685+ override int len ( ) { result = len }
1686+
1687+ override DataFlowType getType ( ) { result = tc .getContainerType ( ) }
1688+
1689+ override AccessPathFront getFront ( ) { result = TFrontHead ( tc ) }
1690+
1691+ override AccessPathApprox pop ( TypedContent head ) {
1692+ head = tc and
1693+ (
1694+ exists ( TypedContent tc2 | flowCandConsCand ( tc , TFrontHead ( tc2 ) , _) |
1695+ result = TConsCons ( tc2 , _, len - 1 )
1696+ or
1697+ len = 2 and
1698+ result = TConsNil ( tc2 , _)
1699+ or
1700+ result = TCons1 ( tc2 , len - 1 )
1701+ )
1702+ or
1703+ exists ( DataFlowType t |
1704+ len = 1 and
1705+ flowCandConsCand ( tc , TFrontNil ( t ) , _) and
1706+ result = TNil ( t )
1707+ )
16251708 )
16261709 }
16271710}
@@ -2045,23 +2128,33 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config)
20452128
20462129pragma [ noinline]
20472130private predicate parameterFlow (
2048- ParameterNode p , AccessPathApprox apa , DataFlowCallable c , Configuration config
2131+ ParameterNode p , AccessPathApprox apa , AccessPathApprox apa0 , DataFlowCallable c ,
2132+ Configuration config
20492133) {
2050- flow ( p , true , _ , apa , config ) and
2134+ flow ( p , true , TAccessPathApproxSome ( apa0 ) , apa , config ) and
20512135 c = p .getEnclosingCallable ( )
20522136}
20532137
2054- private predicate parameterMayFlowThrough ( ParameterNode p , AccessPathApprox apa ) {
2138+ private predicate parameterMayFlowThrough ( ParameterNode p , DataFlowCallable c , AccessPathApprox apa ) {
20552139 exists ( ReturnNodeExt ret , Configuration config , AccessPathApprox apa0 |
2056- parameterFlow ( p , apa , ret .getEnclosingCallable ( ) , config ) and
2140+ parameterFlow ( p , apa , apa0 , c , config ) and
2141+ c = ret .getEnclosingCallable ( ) and
20572142 flow ( ret , true , TAccessPathApproxSome ( _) , apa0 , config ) and
20582143 flowFwd ( ret , any ( CallContextCall ccc ) , TAccessPathApproxSome ( apa ) , _, apa0 , config )
20592144 )
20602145}
20612146
2147+ private predicate nodeMayUseSummary ( Node n , AccessPathApprox apa ) {
2148+ exists ( DataFlowCallable c , AccessPathApprox apa0 |
2149+ parameterMayFlowThrough ( _, c , apa ) and
2150+ flow ( n , true , _, apa0 , _) and
2151+ flowFwd ( n , any ( CallContextCall ccc ) , TAccessPathApproxSome ( apa ) , _, apa0 , _)
2152+ )
2153+ }
2154+
20622155private newtype TSummaryCtx =
20632156 TSummaryCtxNone ( ) or
2064- TSummaryCtxSome ( ParameterNode p , AccessPath ap ) { parameterMayFlowThrough ( p , ap .getApprox ( ) ) }
2157+ TSummaryCtxSome ( ParameterNode p , AccessPath ap ) { parameterMayFlowThrough ( p , _ , ap .getApprox ( ) ) }
20652158
20662159/**
20672160 * A context for generating flow summaries. This represents flow entry through
@@ -2096,9 +2189,118 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
20962189 }
20972190}
20982191
2192+ /**
2193+ * Gets the number of length 2 access path approximations that correspond to `apa`.
2194+ */
2195+ private int count1to2unfold ( AccessPathApproxCons1 apa , Configuration config ) {
2196+ exists ( TypedContent tc , int len |
2197+ tc = apa .getHead ( ) and
2198+ len = apa .len ( ) and
2199+ result =
2200+ strictcount ( AccessPathFront apf |
2201+ flowConsCand ( tc , any ( AccessPathApprox ap | ap .getFront ( ) = apf and ap .len ( ) = len - 1 ) ,
2202+ config )
2203+ )
2204+ )
2205+ }
2206+
2207+ /**
2208+ * Holds if a length 2 access path approximation matching `apa` is expected
2209+ * to be expensive.
2210+ */
2211+ private predicate expensiveLen1to2unfolding ( AccessPathApproxCons1 apa , Configuration config ) {
2212+ exists ( int aps , int nodes , int apLimit , int tupleLimit |
2213+ aps = count1to2unfold ( apa , config ) and
2214+ nodes =
2215+ strictcount ( Node n |
2216+ flow ( n , _, _, apa , config )
2217+ or
2218+ nodeMayUseSummary ( n , apa )
2219+ ) and
2220+ accessPathCostLimits ( apLimit , tupleLimit ) and
2221+ apLimit < aps and
2222+ tupleLimit < ( aps - 1 ) * nodes
2223+ )
2224+ }
2225+
2226+ private AccessPathApprox getATail ( AccessPathApprox apa , Configuration config ) {
2227+ exists ( TypedContent head |
2228+ apa .pop ( head ) = result and
2229+ flowConsCand ( head , result , config )
2230+ )
2231+ }
2232+
2233+ /**
2234+ * Holds with `unfold = false` if a precise head-tail representation of `apa` is
2235+ * expected to be expensive. Holds with `unfold = true` otherwise.
2236+ */
2237+ private predicate evalUnfold ( AccessPathApprox apa , boolean unfold , Configuration config ) {
2238+ exists ( int aps , int nodes , int apLimit , int tupleLimit |
2239+ aps = countPotentialAps ( apa , config ) and
2240+ nodes =
2241+ strictcount ( Node n |
2242+ flow ( n , _, _, apa , _)
2243+ or
2244+ nodeMayUseSummary ( n , apa )
2245+ ) and
2246+ accessPathCostLimits ( apLimit , tupleLimit ) and
2247+ if apLimit < aps and tupleLimit < ( aps - 1 ) * nodes then unfold = false else unfold = true
2248+ )
2249+ }
2250+
2251+ /**
2252+ * Gets the number of `AccessPath`s that correspond to `apa`.
2253+ */
2254+ private int countAps ( AccessPathApprox apa , Configuration config ) {
2255+ evalUnfold ( apa , false , config ) and
2256+ result = 1 and
2257+ ( not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding ( apa , config ) )
2258+ or
2259+ evalUnfold ( apa , false , config ) and
2260+ result = count1to2unfold ( apa , config ) and
2261+ not expensiveLen1to2unfolding ( apa , config )
2262+ or
2263+ evalUnfold ( apa , true , config ) and
2264+ result = countPotentialAps ( apa , config )
2265+ }
2266+
2267+ /**
2268+ * Gets the number of `AccessPath`s that would correspond to `apa` assuming
2269+ * that it is expanded to a precise head-tail representation.
2270+ */
2271+ language [ monotonicAggregates]
2272+ private int countPotentialAps ( AccessPathApprox apa , Configuration config ) {
2273+ apa instanceof AccessPathApproxNil and result = 1
2274+ or
2275+ result = strictsum ( AccessPathApprox tail | tail = getATail ( apa , config ) | countAps ( tail , config ) )
2276+ }
2277+
20992278private newtype TAccessPath =
21002279 TAccessPathNil ( DataFlowType t ) or
2101- TAccessPathCons ( TypedContent head , AccessPath tail ) { flowConsCand ( head , tail .getApprox ( ) , _) }
2280+ TAccessPathCons ( TypedContent head , AccessPath tail ) {
2281+ exists ( AccessPathApproxCons apa |
2282+ not evalUnfold ( apa , false , _) and
2283+ head = apa .getHead ( ) and
2284+ tail .getApprox ( ) = getATail ( apa , _)
2285+ )
2286+ } or
2287+ TAccessPathCons2 ( TypedContent head1 , TypedContent head2 , int len ) {
2288+ exists ( AccessPathApproxCons apa |
2289+ evalUnfold ( apa , false , _) and
2290+ not expensiveLen1to2unfolding ( apa , _) and
2291+ apa .len ( ) = len and
2292+ head1 = apa .getHead ( ) and
2293+ head2 = getATail ( apa , _) .getHead ( )
2294+ )
2295+ } or
2296+ TAccessPathCons1 ( TypedContent head , int len ) {
2297+ exists ( AccessPathApproxCons apa |
2298+ evalUnfold ( apa , false , _) and
2299+ expensiveLen1to2unfolding ( apa , _) and
2300+ apa .len ( ) = len and
2301+ head = apa .getHead ( )
2302+ )
2303+ }
21022304
21032305private newtype TPathNode =
21042306 TPathNodeMid ( Node node , CallContext cc , SummaryCtx sc , AccessPath ap , Configuration config ) {
@@ -2202,20 +2404,96 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
22022404 result = TConsNil ( head , tail .( AccessPathNil ) .getType ( ) )
22032405 or
22042406 result = TConsCons ( head , tail .getHead ( ) , this .length ( ) )
2407+ or
2408+ result = TCons1 ( head , this .length ( ) )
22052409 }
22062410
22072411 override int length ( ) { result = 1 + tail .length ( ) }
22082412
2209- private string toStringImpl ( ) {
2413+ private string toStringImpl ( boolean needsSuffix ) {
22102414 exists ( DataFlowType t |
22112415 tail = TAccessPathNil ( t ) and
2416+ needsSuffix = false and
22122417 result = head .toString ( ) + "]" + concat ( " : " + ppReprType ( t ) )
22132418 )
22142419 or
2215- result = head + ", " + tail .( AccessPathCons ) .toStringImpl ( )
2420+ result = head + ", " + tail .( AccessPathCons ) .toStringImpl ( needsSuffix )
2421+ or
2422+ exists ( TypedContent tc2 , TypedContent tc3 , int len | tail = TAccessPathCons2 ( tc2 , tc3 , len ) |
2423+ result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true
2424+ or
2425+ result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false
2426+ )
2427+ or
2428+ exists ( TypedContent tc2 , int len | tail = TAccessPathCons1 ( tc2 , len ) |
2429+ result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true
2430+ or
2431+ result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false
2432+ )
22162433 }
22172434
2218- override string toString ( ) { result = "[" + this .toStringImpl ( ) }
2435+ override string toString ( ) {
2436+ result = "[" + this .toStringImpl ( true ) + length ( ) .toString ( ) + ")]"
2437+ or
2438+ result = "[" + this .toStringImpl ( false )
2439+ }
2440+ }
2441+
2442+ private class AccessPathCons2 extends AccessPath , TAccessPathCons2 {
2443+ private TypedContent head1 ;
2444+ private TypedContent head2 ;
2445+ private int len ;
2446+
2447+ AccessPathCons2 ( ) { this = TAccessPathCons2 ( head1 , head2 , len ) }
2448+
2449+ override TypedContent getHead ( ) { result = head1 }
2450+
2451+ override AccessPath getTail ( ) {
2452+ flowConsCand ( head1 , result .getApprox ( ) , _) and
2453+ result .getHead ( ) = head2 and
2454+ result .length ( ) = len - 1
2455+ }
2456+
2457+ override AccessPathFrontHead getFront ( ) { result = TFrontHead ( head1 ) }
2458+
2459+ override AccessPathApproxCons getApprox ( ) {
2460+ result = TConsCons ( head1 , head2 , len ) or
2461+ result = TCons1 ( head1 , len )
2462+ }
2463+
2464+ override int length ( ) { result = len }
2465+
2466+ override string toString ( ) {
2467+ if len = 2
2468+ then result = "[" + head1 .toString ( ) + ", " + head2 .toString ( ) + "]"
2469+ else
2470+ result = "[" + head1 .toString ( ) + ", " + head2 .toString ( ) + ", ... (" + len .toString ( ) + ")]"
2471+ }
2472+ }
2473+
2474+ private class AccessPathCons1 extends AccessPath , TAccessPathCons1 {
2475+ private TypedContent head ;
2476+ private int len ;
2477+
2478+ AccessPathCons1 ( ) { this = TAccessPathCons1 ( head , len ) }
2479+
2480+ override TypedContent getHead ( ) { result = head }
2481+
2482+ override AccessPath getTail ( ) {
2483+ flowConsCand ( head , result .getApprox ( ) , _) and result .length ( ) = len - 1
2484+ }
2485+
2486+ override AccessPathFrontHead getFront ( ) { result = TFrontHead ( head ) }
2487+
2488+ override AccessPathApproxCons getApprox ( ) { result = TCons1 ( head , len ) }
2489+
2490+ override int length ( ) { result = len }
2491+
2492+ override string toString ( ) {
2493+ if len = 1
2494+ then result = "[" + head .toString ( ) + "]"
2495+ else result = "[" + head .toString ( ) + ", ... (" + len .toString ( ) + ")]"
2496+ }
22192497}
22202498
22212499/**
0 commit comments