Skip to content

Commit 97ed5b8

Browse files
hvitvedhmac
authored andcommitted
Ruby: Improvments to splat flow
- Only step through a `SynthSplatParameterElementNode` when there is a splat parameter at index > 0. - Model read+stores via `SynthSplatArgumentElementNode` as a single read-store step in type tracking.
1 parent bf51cba commit 97ed5b8

File tree

3 files changed

+419
-263
lines changed

3 files changed

+419
-263
lines changed

ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll

Lines changed: 23 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ private module Cached {
454454
} or
455455
TSynthSplatParameterElementNode(DataFlowCallable c, int n) {
456456
exists(c.asCallable()) and // exclude library callables
457-
isParameterNode(_, c, any(ParameterPosition p | p.isSplat(_))) and
457+
isParameterNode(_, c, any(ParameterPosition p | p.isSplat(any(int i | i > 0)))) and
458458
n in [0 .. 10]
459459
} or
460460
TExprPostUpdateNode(CfgNodes::ExprCfgNode n) {
@@ -1214,7 +1214,7 @@ module ArgumentNodes {
12141214
* part of the method signature, such that those cannot end up in the hash-splat
12151215
* parameter.
12161216
*/
1217-
class SynthHashSplatArgumentNode extends ArgumentNode, TSynthHashSplatArgumentNode {
1217+
class SynthHashSplatArgumentNode extends ArgumentNode, NodeImpl, TSynthHashSplatArgumentNode {
12181218
CfgNodes::ExprNodes::CallCfgNode c;
12191219

12201220
SynthHashSplatArgumentNode() { this = TSynthHashSplatArgumentNode(c) }
@@ -1227,12 +1227,6 @@ module ArgumentNodes {
12271227
call = c and
12281228
pos.isHashSplat()
12291229
}
1230-
}
1231-
1232-
private class SynthHashSplatArgumentNodeImpl extends NodeImpl, TSynthHashSplatArgumentNode {
1233-
CfgNodes::ExprNodes::CallCfgNode c;
1234-
1235-
SynthHashSplatArgumentNodeImpl() { this = TSynthHashSplatArgumentNode(c) }
12361230

12371231
override CfgScope getCfgScope() { result = c.getExpr().getCfgScope() }
12381232

@@ -1541,6 +1535,23 @@ predicate storeStepCommon(Node node1, ContentSet c, Node node2) {
15411535
)
15421536
}
15431537

1538+
// Store from TSynthSplatArgumentElementNode(n)
1539+
// into TSynthSplatArgumentNode[n]
1540+
predicate synthSplatArgumentElementStoreStep(
1541+
SynthSplatArgumentElementNode node1, ContentSet c, SynthSplatArgumentNode node2
1542+
) {
1543+
exists(CfgNodes::ExprNodes::CallCfgNode call, int n |
1544+
node2 = TSynthSplatArgumentNode(call) and
1545+
node1 = TSynthSplatArgumentElementNode(call, n) and
1546+
(
1547+
c = getPositionalContent(n)
1548+
or
1549+
n = -1 and
1550+
c.isSingleton(TUnknownElementContent())
1551+
)
1552+
)
1553+
}
1554+
15441555
/**
15451556
* Holds if data can flow from `node1` to `node2` via an assignment to
15461557
* content `c`.
@@ -1578,18 +1589,7 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
15781589
c = getPositionalContent(elemNode.getStorePosition() - splatPos)
15791590
)
15801591
or
1581-
// Store from TSynthSplatArgumentElementNode(n)
1582-
// into TSynthSplatArgumentNode[n]
1583-
exists(CfgNodes::ExprNodes::CallCfgNode call, int n |
1584-
node2 = TSynthSplatArgumentNode(call) and
1585-
node1 = TSynthSplatArgumentElementNode(call, n) and
1586-
(
1587-
c = getPositionalContent(n)
1588-
or
1589-
n = -1 and
1590-
c.isSingleton(TUnknownElementContent())
1591-
)
1592-
)
1592+
synthSplatArgumentElementStoreStep(node1, c, node2)
15931593
or
15941594
storeStepCommon(node1, c, node2)
15951595
or
@@ -1604,19 +1604,6 @@ predicate readStepCommon(Node node1, ContentSet c, Node node2) {
16041604
node2 = node1.(SynthHashSplatParameterNode).getAKeywordParameter(c)
16051605
or
16061606
node2 = node1.(SynthSplatParameterNode).getAParameter(c)
1607-
or
1608-
// TODO: convert into the above form
1609-
synthSplatArgumentElementReadStep(node1, c, node2)
1610-
or
1611-
// read from SynthSplatParameterNode[n] to nth positional parameter
1612-
exists(SynthSplatParameterNode paramNode, NormalParameterNode posNode, int n |
1613-
paramNode = node1 and
1614-
posNode = node2 and
1615-
posNode
1616-
.isParameterOf(paramNode.getEnclosingCallable(),
1617-
any(ParameterPosition p | p.isPositional(n))) and
1618-
c = getPositionalContent(n)
1619-
)
16201607
}
16211608

16221609
// read from splat arg to synth splat arg element
@@ -1678,6 +1665,9 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
16781665
c = getPositionalContent(e.getReadPosition())
16791666
)
16801667
or
1668+
// TODO: convert into the above form
1669+
synthSplatArgumentElementReadStep(node1, c, node2)
1670+
or
16811671
readStepCommon(node1, c, node2)
16821672
}
16831673

ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,11 @@ predicate readStoreStepIntoSourceNode(
339339
nodeTo = mid.getSplatParameterNode(_) and
340340
storeContent = DataFlowPrivate::getPositionalContent(mid.getStorePosition())
341341
)
342+
or
343+
exists(DataFlowPrivate::SynthSplatArgumentElementNode mid |
344+
DataFlowPrivate::synthSplatArgumentElementReadStep(nodeFrom, loadContent, mid) and
345+
DataFlowPrivate::synthSplatArgumentElementStoreStep(mid, storeContent, nodeTo)
346+
)
342347
}
343348

344349
/**

0 commit comments

Comments
 (0)