@@ -448,15 +448,14 @@ private module Cached {
448
448
TSynthHashSplatParameterNode ( DataFlowCallable c ) {
449
449
isParameterNode ( _, c , any ( ParameterPosition p | p .isKeyword ( _) ) )
450
450
} or
451
- TSynthSplatParameterNode ( DataFlowCallable c , int n ) {
451
+ TSynthSplatParameterNode ( DataFlowCallable c ) {
452
452
exists ( c .asCallable ( ) ) and // exclude library callables
453
- isParameterNode ( _, c , any ( ParameterPosition p | p .isPositional ( _) ) ) and
453
+ isParameterNode ( _, c , any ( ParameterPosition p | p .isPositional ( _) ) )
454
454
// `n` is the position of the splat argument that is matched to this node
455
- exists ( ArgumentPosition pos | pos .isSplat ( n ) )
456
455
} or
457
456
TSynthSplatArgParameterNode ( DataFlowCallable c ) {
458
- exists ( c .asCallable ( ) ) and // exclude library callables
459
- isParameterNode ( _, c , any ( ParameterPosition p | p .isSplat ( _) ) )
457
+ exists ( c .asCallable ( ) ) // exclude library callables
458
+ // and isParameterNode(_, c, any(ParameterPosition p | p.isSplat(_)))
460
459
} or
461
460
TSynthSplatParameterElementNode ( DataFlowCallable c , int n ) {
462
461
exists ( c .asCallable ( ) ) and // exclude library callables
@@ -478,15 +477,17 @@ private module Cached {
478
477
or
479
478
c .getAnArgument ( ) instanceof CfgNodes:: ExprNodes:: PairCfgNode
480
479
} or
481
- TSynthSplatArgumentNode ( CfgNodes:: ExprNodes:: CallCfgNode c ) {
482
- exists ( Argument arg , ArgumentPosition pos | pos .isPositional ( _) | arg .isArgumentOf ( c , pos ) ) and
483
- not exists ( Argument arg , ArgumentPosition pos | pos .isSplat ( _) | arg .isArgumentOf ( c , pos ) )
480
+ TSynthSplatArgumentNode ( CfgNodes:: ExprNodes:: CallCfgNode c ) or
481
+ TSynthSplatArgumentElementNode ( CfgNodes:: ExprNodes:: CallCfgNode c , int n ) {
482
+ n in [ 0 .. 10 ] and
483
+ exists ( Argument arg , ArgumentPosition pos | pos .isSplat ( _) and arg .isArgumentOf ( c , pos ) )
484
484
} or
485
485
TCaptureNode ( VariableCapture:: Flow:: SynthesizedCaptureNode cn )
486
486
487
487
class TSourceParameterNode =
488
488
TNormalParameterNode or TBlockParameterNode or TSelfParameterNode or
489
- TSynthHashSplatParameterNode or TSynthSplatParameterNode or TSynthSplatArgParameterNode ;
489
+ TSynthHashSplatParameterNode or TSynthSplatParameterNode or TSynthSplatArgParameterNode or
490
+ TSynthSplatArgumentElementNode ;
490
491
491
492
cached
492
493
Location getLocation ( NodeImpl n ) { result = n .getLocationImpl ( ) }
@@ -1037,7 +1038,7 @@ private module ParameterNodes {
1037
1038
*
1038
1039
* This node stores the index of the splat argument it is matched to, which allows us to shift
1039
1040
* indices correctly when adding read steps. Without this, in the example above we would erroneously
1040
- * get a read step to `x` and index 0 and `y` and index 1 etc.
1041
+ * get a read step to `x` at index 0 and `y` at index 1 etc.
1041
1042
*
1042
1043
* We don't yet correctly handle cases where a positional argument follows the splat argument, e.g. in
1043
1044
* ```rb
@@ -1046,10 +1047,8 @@ private module ParameterNodes {
1046
1047
*/
1047
1048
class SynthSplatParameterNode extends ParameterNodeImpl , TSynthSplatParameterNode {
1048
1049
private DataFlowCallable callable ;
1049
- // The position of the splat argument that is matched to this node
1050
- private int n ;
1051
1050
1052
- SynthSplatParameterNode ( ) { this = TSynthSplatParameterNode ( callable , n ) }
1051
+ SynthSplatParameterNode ( ) { this = TSynthSplatParameterNode ( callable ) }
1053
1052
1054
1053
/**
1055
1054
* Gets a parameter which will contain the value given by `c`.
@@ -1064,10 +1063,10 @@ private module ParameterNodes {
1064
1063
* then `getAParameter(element 0) = y`.
1065
1064
*/
1066
1065
ParameterNode getAParameter ( ContentSet c ) {
1067
- exists ( int m |
1068
- isParameterNode ( result , callable , ( any ( ParameterPosition p | p .isPositional ( m ) ) ) ) and
1066
+ exists ( int n |
1067
+ isParameterNode ( result , callable , ( any ( ParameterPosition p | p .isPositional ( n ) ) ) ) and
1069
1068
(
1070
- c = getPositionalContent ( m - n )
1069
+ c = getPositionalContent ( n )
1071
1070
or
1072
1071
c .isSingleton ( TUnknownElementContent ( ) )
1073
1072
)
@@ -1077,7 +1076,7 @@ private module ParameterNodes {
1077
1076
final override Parameter getParameter ( ) { none ( ) }
1078
1077
1079
1078
final override predicate isParameterOf ( DataFlowCallable c , ParameterPosition pos ) {
1080
- c = callable and pos .isSynthSplat ( n )
1079
+ c = callable and pos .isSynthSplat ( )
1081
1080
}
1082
1081
1083
1082
final override CfgScope getCfgScope ( ) { result = callable .asCallable ( ) }
@@ -1132,12 +1131,7 @@ private module ParameterNodes {
1132
1131
1133
1132
int getStorePosition ( ) { result = pos }
1134
1133
1135
- int getReadPosition ( ) {
1136
- exists ( int splatPos |
1137
- exists ( this .getSplatParameterNode ( splatPos ) ) and
1138
- result = pos + splatPos
1139
- )
1140
- }
1134
+ int getReadPosition ( ) { result = pos }
1141
1135
1142
1136
final override CfgScope getCfgScope ( ) { result = callable .asCallable ( ) }
1143
1137
@@ -1322,6 +1316,23 @@ module ArgumentNodes {
1322
1316
1323
1317
override string toStringImpl ( ) { result = "*" }
1324
1318
}
1319
+
1320
+ class SynthSplatArgumentElementNode extends NodeImpl , TSynthSplatArgumentElementNode {
1321
+ CfgNodes:: ExprNodes:: CallCfgNode c ;
1322
+ int n ;
1323
+
1324
+ SynthSplatArgumentElementNode ( ) { this = TSynthSplatArgumentElementNode ( c , n ) }
1325
+
1326
+ CfgNodes:: ExprNodes:: CallCfgNode getCall ( ) { result = c }
1327
+
1328
+ int getPosition ( ) { result = n }
1329
+
1330
+ override CfgScope getCfgScope ( ) { result = c .getExpr ( ) .getCfgScope ( ) }
1331
+
1332
+ override Location getLocationImpl ( ) { result = c .getLocation ( ) }
1333
+
1334
+ override string toStringImpl ( ) { result = "*[" + n + "]" }
1335
+ }
1325
1336
}
1326
1337
1327
1338
import ArgumentNodes
@@ -1592,11 +1603,19 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
1592
1603
FlowSummaryImpl:: Private:: Steps:: summaryStoreStep ( node1 .( FlowSummaryNode ) .getSummaryNode ( ) , c ,
1593
1604
node2 .( FlowSummaryNode ) .getSummaryNode ( ) )
1594
1605
or
1595
- node1 =
1596
- any ( SynthSplatParameterElementNode elemNode |
1597
- node2 = elemNode .getSplatParameterNode ( _) and
1598
- c = getPositionalContent ( elemNode .getStorePosition ( ) )
1599
- )
1606
+ exists ( SynthSplatParameterElementNode elemNode , int splatPos |
1607
+ node1 = elemNode and
1608
+ node2 = elemNode .getSplatParameterNode ( splatPos ) and
1609
+ c = getPositionalContent ( elemNode .getStorePosition ( ) - splatPos )
1610
+ )
1611
+ or
1612
+ // Store from TSynthSplatArgumentElementNode(n)
1613
+ // into TSynthSplatArgumentNode[n]
1614
+ exists ( CfgNodes:: ExprNodes:: CallCfgNode call , int n |
1615
+ node1 = TSynthSplatArgumentElementNode ( call , n ) and
1616
+ node2 = TSynthSplatArgumentNode ( call ) and
1617
+ c = getPositionalContent ( n )
1618
+ )
1600
1619
or
1601
1620
storeStepCommon ( node1 , c , node2 )
1602
1621
or
@@ -1611,6 +1630,38 @@ predicate readStepCommon(Node node1, ContentSet c, Node node2) {
1611
1630
node2 = node1 .( SynthHashSplatParameterNode ) .getAKeywordParameter ( c )
1612
1631
or
1613
1632
node2 = node1 .( SynthSplatParameterNode ) .getAParameter ( c )
1633
+ or
1634
+ // TODO: convert into the above form
1635
+ synthSplatArgumentElementReadStep ( node1 , c , node2 )
1636
+ or
1637
+ // read from SynthSplatArgParameterNode[n] to nth positional parameter
1638
+ exists ( SynthSplatArgParameterNode argParamNode , NormalParameterNode posNode , int n |
1639
+ argParamNode = node1 and
1640
+ posNode = node2 and
1641
+ posNode
1642
+ .isParameterOf ( argParamNode .getEnclosingCallable ( ) ,
1643
+ any ( ParameterPosition p | p .isPositional ( n ) ) ) and
1644
+ c = getPositionalContent ( n )
1645
+ )
1646
+ }
1647
+
1648
+ // read from splat arg to synth splat arg element
1649
+ predicate synthSplatArgumentElementReadStep (
1650
+ Node node1 , ContentSet c , SynthSplatArgumentElementNode node2
1651
+ ) {
1652
+ exists ( int n , int splatPos , CfgNodes:: ExprNodes:: CallCfgNode call |
1653
+ // Don't propagate taint on the `self` element of the splat
1654
+ // since that won't (probably) won't reach the parameters of the callable.
1655
+ // This saves a node per call.
1656
+ n >= 0 and
1657
+ node1 .asExpr ( ) .( Argument ) .isArgumentOf ( call , any ( ArgumentPosition p | p .isSplat ( splatPos ) ) ) and
1658
+ node2 .getCall ( ) = call and
1659
+ node2 .getPosition ( ) = n + splatPos and
1660
+ (
1661
+ c = getPositionalContent ( n ) or
1662
+ c .isSingleton ( TUnknownElementContent ( ) )
1663
+ )
1664
+ )
1614
1665
}
1615
1666
1616
1667
/**
0 commit comments