Skip to content

Commit f664a77

Browse files
committed
Ruby: ensure Hash flow works again
1 parent 7cf969f commit f664a77

File tree

5 files changed

+44
-68
lines changed

5 files changed

+44
-68
lines changed

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

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ private module Cached {
381381
n instanceof SynthReturnNode
382382
or
383383
// Needed for stores in type tracking
384-
TypeTrackerSpecific::postUpdateStoreStep(_, n, _)
384+
TypeTrackerSpecific::storeStepIntoSourceNode(_, n, _)
385385
}
386386

387387
cached
@@ -1010,6 +1010,31 @@ private ContentSet getKeywordContent(string name) {
10101010
)
10111011
}
10121012

1013+
/**
1014+
* Subset of `storeStep` that should be shared with type-tracking.
1015+
*/
1016+
predicate storeStepCommon(Node node1, ContentSet c, Node node2) {
1017+
// Wrap all key-value arguments in a synthesized hash-splat argument node
1018+
exists(CfgNodes::ExprNodes::CallCfgNode call | node2 = TSynthHashSplatArgumentNode(call) |
1019+
// symbol key
1020+
exists(ArgumentPosition keywordPos, string name |
1021+
node1.asExpr().(Argument).isArgumentOf(call, keywordPos) and
1022+
keywordPos.isKeyword(name) and
1023+
c = getKeywordContent(name)
1024+
)
1025+
or
1026+
// non-symbol key
1027+
exists(CfgNodes::ExprNodes::PairCfgNode pair, CfgNodes::ExprCfgNode key, ConstantValue cv |
1028+
node1.asExpr() = pair.getValue() and
1029+
pair = call.getAnArgument() and
1030+
key = pair.getKey() and
1031+
cv = key.getConstantValue() and
1032+
not cv.isSymbol(_) and
1033+
c.isSingleton(TKnownElementContent(cv))
1034+
)
1035+
)
1036+
}
1037+
10131038
/**
10141039
* Holds if data can flow from `node1` to `node2` via an assignment to
10151040
* content `c`.
@@ -1040,25 +1065,7 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
10401065
or
10411066
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1, c, node2)
10421067
or
1043-
// Wrap all key-value arguments in a synthesized hash-splat argument node
1044-
exists(CfgNodes::ExprNodes::CallCfgNode call | node2 = TSynthHashSplatArgumentNode(call) |
1045-
// symbol key
1046-
exists(ArgumentPosition keywordPos, string name |
1047-
node1.asExpr().(Argument).isArgumentOf(call, keywordPos) and
1048-
keywordPos.isKeyword(name) and
1049-
c = getKeywordContent(name)
1050-
)
1051-
or
1052-
// non-symbol key
1053-
exists(CfgNodes::ExprNodes::PairCfgNode pair, CfgNodes::ExprCfgNode key, ConstantValue cv |
1054-
node1.asExpr() = pair.getValue() and
1055-
pair = call.getAnArgument() and
1056-
key = pair.getKey() and
1057-
cv = key.getConstantValue() and
1058-
not cv.isSymbol(_) and
1059-
c.isSingleton(TKnownElementContent(cv))
1060-
)
1061-
)
1068+
storeStepCommon(node1, c, node2)
10621069
}
10631070

10641071
/**

ruby/ql/lib/codeql/ruby/frameworks/Core.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ private class SplatSummary extends SummarizedCallable {
7777
private class HashSplatSummary extends SummarizedCallable {
7878
HashSplatSummary() { this = "**(hash-splat)" }
7979

80-
override HashSplatExpr getACall() { any() }
80+
override HashSplatExpr getACallSimple() { any() }
8181

8282
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
8383
input = "Argument[self].WithElement[any]" and

ruby/ql/lib/codeql/ruby/frameworks/core/Hash.qll

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ private import codeql.ruby.DataFlow
77
private import codeql.ruby.dataflow.FlowSummary
88
private import codeql.ruby.dataflow.internal.DataFlowDispatch
99
private import codeql.ruby.ast.internal.Module
10-
private import codeql.ruby.typetracking.TypeTrackerSpecific
1110

1211
/**
1312
* Provides flow summaries for the `Hash` class.
@@ -42,50 +41,6 @@ module Hash {
4241
}
4342
}
4443

45-
/** Holds if `literal` is a call to `Hash.[]` and `argument` is one of its arguments. */
46-
private predicate hashLiteralStore(DataFlow::CallNode literal, DataFlow::Node argument) {
47-
literal.getExprNode().getExpr() = getAStaticHashCall("[]") and
48-
argument = literal.getArgument(_)
49-
}
50-
51-
/**
52-
* A set of type-tracking steps to replace the `Hash.[]` summary.
53-
*
54-
* The `Hash.[]` method tends to have a large number of summaries, which would result
55-
* in too many unnecessary type-tracking edges, so we specialize it here.
56-
*/
57-
private class HashLiteralTypeTracker extends TypeTrackingStep {
58-
override predicate suppressSummary(SummarizedCallable callable) {
59-
callable instanceof HashLiteralSummary
60-
}
61-
62-
override predicate storeStep(Node pred, TypeTrackingNode succ, TypeTrackerContent content) {
63-
// Store edge: `value -> { key: value }` with content derived from `key`
64-
exists(Cfg::CfgNodes::ExprNodes::PairCfgNode pair |
65-
hashLiteralStore(succ, any(DataFlow::Node n | n.asExpr() = pair)) and
66-
pred.asExpr() = pair.getValue()
67-
|
68-
exists(ConstantValue constant |
69-
constant = pair.getKey().getConstantValue() and
70-
content.isSingleton(DataFlow::Content::getElementContent(constant))
71-
)
72-
or
73-
not exists(pair.getKey().getConstantValue()) and
74-
content.isAnyElement()
75-
)
76-
}
77-
78-
override predicate withContentStep(Node pred, Node succ, ContentFilter filter) {
79-
// `WithContent[element]` edge: `args --> { **args }`.
80-
exists(DataFlow::Node node |
81-
hashLiteralStore(succ, node) and
82-
node.asExpr().getExpr() instanceof HashSplatExpr and
83-
pred.asExpr() = node.asExpr().(Cfg::CfgNodes::ExprNodes::UnaryOperationCfgNode).getOperand() and
84-
filter = ContentFilter::hasElements()
85-
)
86-
}
87-
}
88-
8944
/**
9045
* `Hash[]` called on an existing hash, e.g.
9146
*

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ predicate returnStep(Node nodeFrom, Node nodeTo) {
209209
* called.
210210
*/
211211
predicate basicStoreStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet contents) {
212-
postUpdateStoreStep(nodeFrom, nodeTo, contents)
212+
storeStepIntoSourceNode(nodeFrom, nodeTo, contents)
213213
or
214214
exists(
215215
SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponentStack input,
@@ -229,7 +229,7 @@ predicate basicStoreStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet conten
229229
* Holds if a store step `nodeFrom -> nodeTo` with `contents` exists, where the destination node
230230
* is a post-update node that should be treated as a local source node.
231231
*/
232-
predicate postUpdateStoreStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet contents) {
232+
predicate storeStepIntoSourceNode(Node nodeFrom, Node nodeTo, DataFlow::ContentSet contents) {
233233
// TODO: support SetterMethodCall inside TuplePattern
234234
exists(ExprNodes::MethodCallCfgNode call |
235235
contents
@@ -241,6 +241,8 @@ predicate postUpdateStoreStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet c
241241
call.getArgument(call.getNumberOfArguments() - 1) =
242242
nodeFrom.(DataFlowPublic::ExprNode).getExprNode()
243243
)
244+
or
245+
DataFlowPrivate::storeStepCommon(nodeFrom, contents, nodeTo)
244246
}
245247

246248
/**

ruby/ql/test/library-tests/dataflow/type-tracker/TypeTracker.expected

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,39 +114,48 @@ track
114114
| type_tracker.rb:27:5:27:11 | call to puts | type tracker without call steps | type_tracker.rb:30:1:30:21 | call to keyword |
115115
| type_tracker.rb:27:5:27:11 | call to puts | type tracker without call steps | type_tracker.rb:31:1:31:21 | call to keyword |
116116
| type_tracker.rb:27:5:27:11 | call to puts | type tracker without call steps | type_tracker.rb:32:1:32:27 | call to keyword |
117+
| type_tracker.rb:30:1:30:21 | ** | type tracker without call steps | type_tracker.rb:30:1:30:21 | ** |
117118
| type_tracker.rb:30:1:30:21 | call to keyword | type tracker without call steps | type_tracker.rb:30:1:30:21 | call to keyword |
118119
| type_tracker.rb:30:9:30:10 | :p1 | type tracker without call steps | type_tracker.rb:30:9:30:10 | :p1 |
119120
| type_tracker.rb:30:9:30:13 | Pair | type tracker without call steps | type_tracker.rb:30:9:30:13 | Pair |
120121
| type_tracker.rb:30:13:30:13 | 3 | type tracker with call steps | type_tracker.rb:25:13:25:14 | p1 |
121122
| type_tracker.rb:30:13:30:13 | 3 | type tracker with call steps | type_tracker.rb:25:13:25:14 | p1 |
122123
| type_tracker.rb:30:13:30:13 | 3 | type tracker without call steps | type_tracker.rb:30:13:30:13 | 3 |
124+
| type_tracker.rb:30:13:30:13 | 3 | type tracker without call steps with content element :p1 | type_tracker.rb:30:1:30:21 | ** |
123125
| type_tracker.rb:30:16:30:17 | :p2 | type tracker without call steps | type_tracker.rb:30:16:30:17 | :p2 |
124126
| type_tracker.rb:30:16:30:20 | Pair | type tracker without call steps | type_tracker.rb:30:16:30:20 | Pair |
125127
| type_tracker.rb:30:20:30:20 | 4 | type tracker with call steps | type_tracker.rb:25:18:25:19 | p2 |
126128
| type_tracker.rb:30:20:30:20 | 4 | type tracker with call steps | type_tracker.rb:25:18:25:19 | p2 |
127129
| type_tracker.rb:30:20:30:20 | 4 | type tracker without call steps | type_tracker.rb:30:20:30:20 | 4 |
130+
| type_tracker.rb:30:20:30:20 | 4 | type tracker without call steps with content element :p2 | type_tracker.rb:30:1:30:21 | ** |
131+
| type_tracker.rb:31:1:31:21 | ** | type tracker without call steps | type_tracker.rb:31:1:31:21 | ** |
128132
| type_tracker.rb:31:1:31:21 | call to keyword | type tracker without call steps | type_tracker.rb:31:1:31:21 | call to keyword |
129133
| type_tracker.rb:31:9:31:10 | :p2 | type tracker without call steps | type_tracker.rb:31:9:31:10 | :p2 |
130134
| type_tracker.rb:31:9:31:13 | Pair | type tracker without call steps | type_tracker.rb:31:9:31:13 | Pair |
131135
| type_tracker.rb:31:13:31:13 | 5 | type tracker with call steps | type_tracker.rb:25:18:25:19 | p2 |
132136
| type_tracker.rb:31:13:31:13 | 5 | type tracker with call steps | type_tracker.rb:25:18:25:19 | p2 |
133137
| type_tracker.rb:31:13:31:13 | 5 | type tracker without call steps | type_tracker.rb:31:13:31:13 | 5 |
138+
| type_tracker.rb:31:13:31:13 | 5 | type tracker without call steps with content element :p2 | type_tracker.rb:31:1:31:21 | ** |
134139
| type_tracker.rb:31:16:31:17 | :p1 | type tracker without call steps | type_tracker.rb:31:16:31:17 | :p1 |
135140
| type_tracker.rb:31:16:31:20 | Pair | type tracker without call steps | type_tracker.rb:31:16:31:20 | Pair |
136141
| type_tracker.rb:31:20:31:20 | 6 | type tracker with call steps | type_tracker.rb:25:13:25:14 | p1 |
137142
| type_tracker.rb:31:20:31:20 | 6 | type tracker with call steps | type_tracker.rb:25:13:25:14 | p1 |
138143
| type_tracker.rb:31:20:31:20 | 6 | type tracker without call steps | type_tracker.rb:31:20:31:20 | 6 |
144+
| type_tracker.rb:31:20:31:20 | 6 | type tracker without call steps with content element :p1 | type_tracker.rb:31:1:31:21 | ** |
145+
| type_tracker.rb:32:1:32:27 | ** | type tracker without call steps | type_tracker.rb:32:1:32:27 | ** |
139146
| type_tracker.rb:32:1:32:27 | call to keyword | type tracker without call steps | type_tracker.rb:32:1:32:27 | call to keyword |
140147
| type_tracker.rb:32:9:32:11 | :p2 | type tracker without call steps | type_tracker.rb:32:9:32:11 | :p2 |
141148
| type_tracker.rb:32:9:32:16 | Pair | type tracker without call steps | type_tracker.rb:32:9:32:16 | Pair |
142149
| type_tracker.rb:32:16:32:16 | 7 | type tracker with call steps | type_tracker.rb:25:18:25:19 | p2 |
143150
| type_tracker.rb:32:16:32:16 | 7 | type tracker with call steps | type_tracker.rb:25:18:25:19 | p2 |
144151
| type_tracker.rb:32:16:32:16 | 7 | type tracker without call steps | type_tracker.rb:32:16:32:16 | 7 |
152+
| type_tracker.rb:32:16:32:16 | 7 | type tracker without call steps with content element :p2 | type_tracker.rb:32:1:32:27 | ** |
145153
| type_tracker.rb:32:19:32:21 | :p1 | type tracker without call steps | type_tracker.rb:32:19:32:21 | :p1 |
146154
| type_tracker.rb:32:19:32:26 | Pair | type tracker without call steps | type_tracker.rb:32:19:32:26 | Pair |
147155
| type_tracker.rb:32:26:32:26 | 8 | type tracker with call steps | type_tracker.rb:25:13:25:14 | p1 |
148156
| type_tracker.rb:32:26:32:26 | 8 | type tracker with call steps | type_tracker.rb:25:13:25:14 | p1 |
149157
| type_tracker.rb:32:26:32:26 | 8 | type tracker without call steps | type_tracker.rb:32:26:32:26 | 8 |
158+
| type_tracker.rb:32:26:32:26 | 8 | type tracker without call steps with content element :p1 | type_tracker.rb:32:1:32:27 | ** |
150159
| type_tracker.rb:34:1:53:3 | &block | type tracker without call steps | type_tracker.rb:34:1:53:3 | &block |
151160
| type_tracker.rb:34:1:53:3 | return return in throughArray | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
152161
| type_tracker.rb:34:1:53:3 | self in throughArray | type tracker without call steps | type_tracker.rb:34:1:53:3 | self in throughArray |
@@ -483,6 +492,7 @@ trackEnd
483492
| type_tracker.rb:27:5:27:11 | call to puts | type_tracker.rb:30:1:30:21 | call to keyword |
484493
| type_tracker.rb:27:5:27:11 | call to puts | type_tracker.rb:31:1:31:21 | call to keyword |
485494
| type_tracker.rb:27:5:27:11 | call to puts | type_tracker.rb:32:1:32:27 | call to keyword |
495+
| type_tracker.rb:30:1:30:21 | ** | type_tracker.rb:30:1:30:21 | ** |
486496
| type_tracker.rb:30:1:30:21 | call to keyword | type_tracker.rb:30:1:30:21 | call to keyword |
487497
| type_tracker.rb:30:9:30:10 | :p1 | type_tracker.rb:30:9:30:10 | :p1 |
488498
| type_tracker.rb:30:9:30:13 | Pair | type_tracker.rb:30:9:30:13 | Pair |
@@ -496,6 +506,7 @@ trackEnd
496506
| type_tracker.rb:30:20:30:20 | 4 | type_tracker.rb:25:18:25:19 | p2 |
497507
| type_tracker.rb:30:20:30:20 | 4 | type_tracker.rb:27:10:27:11 | p2 |
498508
| type_tracker.rb:30:20:30:20 | 4 | type_tracker.rb:30:20:30:20 | 4 |
509+
| type_tracker.rb:31:1:31:21 | ** | type_tracker.rb:31:1:31:21 | ** |
499510
| type_tracker.rb:31:1:31:21 | call to keyword | type_tracker.rb:31:1:31:21 | call to keyword |
500511
| type_tracker.rb:31:9:31:10 | :p2 | type_tracker.rb:31:9:31:10 | :p2 |
501512
| type_tracker.rb:31:9:31:13 | Pair | type_tracker.rb:31:9:31:13 | Pair |
@@ -509,6 +520,7 @@ trackEnd
509520
| type_tracker.rb:31:20:31:20 | 6 | type_tracker.rb:25:13:25:14 | p1 |
510521
| type_tracker.rb:31:20:31:20 | 6 | type_tracker.rb:26:10:26:11 | p1 |
511522
| type_tracker.rb:31:20:31:20 | 6 | type_tracker.rb:31:20:31:20 | 6 |
523+
| type_tracker.rb:32:1:32:27 | ** | type_tracker.rb:32:1:32:27 | ** |
512524
| type_tracker.rb:32:1:32:27 | call to keyword | type_tracker.rb:32:1:32:27 | call to keyword |
513525
| type_tracker.rb:32:9:32:11 | :p2 | type_tracker.rb:32:9:32:11 | :p2 |
514526
| type_tracker.rb:32:9:32:16 | Pair | type_tracker.rb:32:9:32:16 | Pair |

0 commit comments

Comments
 (0)