Skip to content

Commit 3498a04

Browse files
committed
Ruby: associate ContentSets with store/load edges in type tracker
1 parent 497258e commit 3498a04

File tree

3 files changed

+57
-50
lines changed

3 files changed

+57
-50
lines changed

ruby/ql/lib/codeql/ruby/ApiGraphs.qll

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -269,11 +269,8 @@ module API {
269269
* Gets a node representing the `contents` stored on the base object.
270270
*/
271271
Node getContents(DataFlow::ContentSet contents) {
272-
this instanceof API::Use and
273-
result = this.getContent(contents.getAStoreContent()) // The library has stored a value of interest in `contents`
274-
or
275-
this instanceof API::Def and
276-
result = this.getContent(contents.getAReadContent()) // The library going to read a value stored in `contents`
272+
// We already use getAStoreContent when generating the graph, and we always use getAReadContent when querying the graph.
273+
result = this.getContent(contents.getAReadContent())
277274
}
278275

279276
/** Gets a node representing the instance field of the given `name`, which must include the `@` character. */
@@ -523,9 +520,9 @@ module API {
523520
read = c.getExpr()
524521
)
525522
or
526-
exists(TypeTrackerSpecific::TypeTrackerContent c |
523+
exists(TypeTrackerSpecific::TypeTrackerContentSet c |
527524
TypeTrackerSpecific::basicLoadStep(node, ref, c) and
528-
lbl = Label::content(c)
525+
lbl = Label::content(c.getAStoreContent())
529526
)
530527
// note: method calls are not handled here as there is no DataFlow::Node for the intermediate MkMethodAccessNode API node
531528
}
@@ -535,9 +532,9 @@ module API {
535532
* from a def node that is reachable from `node`.
536533
*/
537534
private predicate defStep(Label::ApiLabel lbl, DataFlow::Node node, DataFlow::Node rhs) {
538-
exists(TypeTrackerSpecific::TypeTrackerContent c |
535+
exists(TypeTrackerSpecific::TypeTrackerContentSet c |
539536
TypeTrackerSpecific::basicStoreStep(rhs, node, c) and
540-
lbl = Label::content(c)
537+
lbl = Label::content(c.getAStoreContent())
541538
)
542539
}
543540

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

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,36 @@ private module Cached {
1212
LevelStep() or
1313
CallStep() or
1414
ReturnStep() or
15-
StoreStep(TypeTrackerContent content) or
16-
LoadStep(TypeTrackerContent content) or
15+
StoreStep(TypeTrackerContentSet contents) or
16+
LoadStep(TypeTrackerContentSet contents) or
1717
JumpStep()
1818

1919
/** Gets the summary resulting from appending `step` to type-tracking summary `tt`. */
2020
cached
2121
TypeTracker append(TypeTracker tt, StepSummary step) {
22-
exists(Boolean hasCall, OptionalTypeTrackerContent content |
23-
tt = MkTypeTracker(hasCall, content)
22+
exists(Boolean hasCall, OptionalTypeTrackerContent currentContent |
23+
tt = MkTypeTracker(hasCall, currentContent)
2424
|
2525
step = LevelStep() and result = tt
2626
or
27-
step = CallStep() and result = MkTypeTracker(true, content)
27+
step = CallStep() and result = MkTypeTracker(true, currentContent)
2828
or
2929
step = ReturnStep() and hasCall = false and result = tt
3030
or
31-
step = LoadStep(content) and result = MkTypeTracker(hasCall, noContent())
31+
exists(TypeTrackerContentSet contents |
32+
step = LoadStep(contents) and
33+
currentContent = contents.getAReadContent() and
34+
result = MkTypeTracker(hasCall, noContent())
35+
)
3236
or
33-
exists(TypeTrackerContent p |
34-
step = StoreStep(p) and content = noContent() and result = MkTypeTracker(hasCall, p)
37+
exists(TypeTrackerContentSet contents |
38+
step = StoreStep(contents) and
39+
currentContent = noContent() and
40+
result = MkTypeTracker(hasCall, contents.getAStoreContent())
3541
)
3642
or
3743
step = JumpStep() and
38-
result = MkTypeTracker(false, content)
44+
result = MkTypeTracker(false, currentContent)
3945
)
4046
}
4147

@@ -51,11 +57,17 @@ private module Cached {
5157
or
5258
step = ReturnStep() and result = MkTypeBackTracker(true, content)
5359
or
54-
exists(TypeTrackerContent p |
55-
step = LoadStep(p) and content = noContent() and result = MkTypeBackTracker(hasReturn, p)
60+
exists(TypeTrackerContentSet contents |
61+
step = LoadStep(contents) and
62+
content = noContent() and
63+
result = MkTypeBackTracker(hasReturn, contents.getAStoreContent())
5664
)
5765
or
58-
step = StoreStep(content) and result = MkTypeBackTracker(hasReturn, noContent())
66+
exists(TypeTrackerContentSet contents |
67+
step = StoreStep(contents) and
68+
content = contents.getAReadContent() and
69+
result = MkTypeBackTracker(hasReturn, noContent())
70+
)
5971
or
6072
step = JumpStep() and
6173
result = MkTypeBackTracker(false, content)
@@ -99,9 +111,11 @@ class StepSummary extends TStepSummary {
99111
or
100112
this instanceof ReturnStep and result = "return"
101113
or
102-
exists(TypeTrackerContent content | this = StoreStep(content) | result = "store " + content)
114+
exists(TypeTrackerContentSet contents | this = StoreStep(contents) |
115+
result = "store " + contents
116+
)
103117
or
104-
exists(TypeTrackerContent content | this = LoadStep(content) | result = "load " + content)
118+
exists(TypeTrackerContentSet contents | this = LoadStep(contents) | result = "load " + contents)
105119
or
106120
this instanceof JumpStep and result = "jump"
107121
}
@@ -115,11 +129,11 @@ private predicate smallstepNoCall(Node nodeFrom, TypeTrackingNode nodeTo, StepSu
115129
levelStep(nodeFrom, nodeTo) and
116130
summary = LevelStep()
117131
or
118-
exists(TypeTrackerContent content |
119-
StepSummary::localSourceStoreStep(nodeFrom, nodeTo, content) and
120-
summary = StoreStep(content)
132+
exists(TypeTrackerContentSet contents |
133+
StepSummary::localSourceStoreStep(nodeFrom, nodeTo, contents) and
134+
summary = StoreStep(contents)
121135
or
122-
basicLoadStep(nodeFrom, nodeTo, content) and summary = LoadStep(content)
136+
basicLoadStep(nodeFrom, nodeTo, contents) and summary = LoadStep(contents)
123137
)
124138
}
125139

@@ -189,8 +203,10 @@ module StepSummary {
189203
* function. This means we will track the fact that `x.attr` can have the type of `y` into the
190204
* assignment to `z` inside `bar`, even though this attribute write happens _after_ `bar` is called.
191205
*/
192-
predicate localSourceStoreStep(Node nodeFrom, TypeTrackingNode nodeTo, TypeTrackerContent content) {
193-
exists(Node obj | nodeTo.flowsTo(obj) and basicStoreStep(nodeFrom, obj, content))
206+
predicate localSourceStoreStep(
207+
Node nodeFrom, TypeTrackingNode nodeTo, TypeTrackerContentSet contents
208+
) {
209+
exists(Node obj | nodeTo.flowsTo(obj) and basicStoreStep(nodeFrom, obj, contents))
194210
}
195211
}
196212

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

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ private newtype TOptionalTypeTrackerContent =
2222

2323
class TypeTrackerContent = DataFlowPublic::Content;
2424

25+
class TypeTrackerContentSet = DataFlowPublic::ContentSet;
26+
2527
predicate noContent = DataFlowPublic::Content::noContent/0;
2628

2729
/** Holds if there is a simple local flow step from `nodeFrom` to `nodeTo` */
@@ -150,34 +152,30 @@ predicate returnStep(Node nodeFrom, Node nodeTo) {
150152
* to `z` inside `bar`, even though this content write happens _after_ `bar` is
151153
* called.
152154
*/
153-
predicate basicStoreStep(Node nodeFrom, Node nodeTo, TypeTrackerContent content) {
154-
postUpdateStoreStep(nodeFrom, nodeTo, content)
155+
predicate basicStoreStep(Node nodeFrom, Node nodeTo, TypeTrackerContentSet contents) {
156+
postUpdateStoreStep(nodeFrom, nodeTo, contents)
155157
or
156-
exists(
157-
DataFlowPublic::CallNode call, SummaryComponent input, DataFlowPublic::ContentSet contents,
158-
SummaryComponent output
159-
|
158+
exists(DataFlowPublic::CallNode call, SummaryComponent input, SummaryComponent output |
160159
summarizableCall(call.asExpr().getExpr(), //
161160
SummaryComponentStack::singleton(input),
162161
SummaryComponentStack::push(SummaryComponent::content(contents),
163162
SummaryComponentStack::singleton(output))) and
164163
nodeFrom = evaluateSummaryComponentLocal(call, input) and
165-
nodeTo = evaluateSummaryComponentLocal(call, output) and
166-
content = contents.getAStoreContent()
164+
nodeTo = evaluateSummaryComponentLocal(call, output)
167165
)
168166
}
169167

170168
/**
171169
* A `content`-store step from `nodeFrom -> nodeTo` where the destination node is a post-update
172170
* node that should be treated as a local source node.
173171
*/
174-
predicate postUpdateStoreStep(Node nodeFrom, Node nodeTo, TypeTrackerContent content) {
172+
predicate postUpdateStoreStep(Node nodeFrom, Node nodeTo, TypeTrackerContentSet contents) {
175173
// TODO: support SetterMethodCall inside TuplePattern
176174
exists(ExprNodes::MethodCallCfgNode call |
177-
content =
178-
DataFlowPublic::Content::getAttributeName(call.getExpr()
179-
.(Ast::SetterMethodCall)
180-
.getTargetName()) and
175+
contents
176+
.isSingleton(DataFlowPublic::Content::getAttributeName(call.getExpr()
177+
.(Ast::SetterMethodCall)
178+
.getTargetName())) and
181179
nodeTo.(DataFlowPublic::PostUpdateNode).getPreUpdateNode().asExpr() = call.getReceiver() and
182180
call.getExpr() instanceof Ast::SetterMethodCall and
183181
call.getArgument(call.getNumberOfArguments() - 1) =
@@ -188,25 +186,21 @@ predicate postUpdateStoreStep(Node nodeFrom, Node nodeTo, TypeTrackerContent con
188186
/**
189187
* Holds if `nodeTo` is the result of accessing the `content` content of `nodeFrom`.
190188
*/
191-
predicate basicLoadStep(Node nodeFrom, Node nodeTo, TypeTrackerContent content) {
189+
predicate basicLoadStep(Node nodeFrom, Node nodeTo, TypeTrackerContentSet contents) {
192190
exists(ExprNodes::MethodCallCfgNode call |
193191
call.getExpr().getNumberOfArguments() = 0 and
194-
content = DataFlowPublic::Content::getAttributeName(call.getExpr().getMethodName()) and
192+
contents.isSingleton(DataFlowPublic::Content::getAttributeName(call.getExpr().getMethodName())) and
195193
nodeFrom.asExpr() = call.getReceiver() and
196194
nodeTo.asExpr() = call
197195
)
198196
or
199-
exists(
200-
DataFlowPublic::CallNode call, SummaryComponent input, DataFlowPublic::ContentSet contents,
201-
SummaryComponent output
202-
|
197+
exists(DataFlowPublic::CallNode call, SummaryComponent input, SummaryComponent output |
203198
summarizableCall(call.asExpr().getExpr(), //
204199
SummaryComponentStack::push(SummaryComponent::content(contents),
205200
SummaryComponentStack::singleton(input)), //
206201
SummaryComponentStack::singleton(output)) and
207202
nodeFrom = evaluateSummaryComponentLocal(call, input) and
208-
nodeTo = evaluateSummaryComponentLocal(call, output) and
209-
content = contents.getAReadContent()
203+
nodeTo = evaluateSummaryComponentLocal(call, output)
210204
)
211205
}
212206

0 commit comments

Comments
 (0)