Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit cd9cddf

Browse files
committed
Ruby: generate type-tracking steps from simple summary specs
1 parent f1b99e8 commit cd9cddf

7 files changed

Lines changed: 140 additions & 4 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ private module Cached {
373373
n instanceof SynthReturnNode
374374
or
375375
// Needed for stores in type tracking
376-
TypeTrackerSpecific::basicStoreStep(_, n, _)
376+
TypeTrackerSpecific::postUpdateStoreStep(_, n, _)
377377
}
378378

379379
cached

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

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
private import codeql.ruby.AST as Ast
22
private import codeql.ruby.CFG as Cfg
33
private import Cfg::CfgNodes
4+
private import codeql.ruby.dataflow.FlowSummary
45
private import codeql.ruby.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
56
private import codeql.ruby.dataflow.internal.DataFlowPublic as DataFlowPublic
67
private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate
78
private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch
89
private import codeql.ruby.dataflow.internal.SsaImpl as SsaImpl
10+
private import codeql.ruby.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
11+
private import codeql.ruby.dataflow.internal.FlowSummaryImplSpecific as FlowSummaryImplSpecific
12+
private import codeql.ruby.dataflow.internal.AccessPathSyntax
913

1014
class Node = DataFlowPublic::Node;
1115

@@ -169,6 +173,27 @@ predicate returnStep(Node nodeFrom, Node nodeTo) {
169173
* called.
170174
*/
171175
predicate basicStoreStep(Node nodeFrom, Node nodeTo, TypeTrackerContent content) {
176+
postUpdateStoreStep(nodeFrom, nodeTo, content)
177+
or
178+
exists(
179+
DataFlowPublic::CallNode call, SummaryComponent input, DataFlowPublic::ContentSet contents,
180+
SummaryComponent output
181+
|
182+
summarizableCall(call.asExpr().getExpr(), //
183+
SummaryComponentStack::singleton(input),
184+
SummaryComponentStack::push(SummaryComponent::content(contents),
185+
SummaryComponentStack::singleton(output))) and
186+
nodeFrom = evaluateSummaryComponentLocal(call, input) and
187+
nodeTo = evaluateSummaryComponentLocal(call, output) and
188+
content.asContent() = contents.getAStoreContent()
189+
)
190+
}
191+
192+
/**
193+
* A `content`-store step from `nodeFrom -> nodeTo` where the destination node is a post-update
194+
* node that should be treated as a local source node.
195+
*/
196+
predicate postUpdateStoreStep(Node nodeFrom, Node nodeTo, TypeTrackerContent content) {
172197
// TODO: support SetterMethodCall inside TuplePattern
173198
exists(ExprNodes::MethodCallCfgNode call |
174199
content = MkAttribute(getSetterCallAttributeName(call.getExpr())) and
@@ -205,6 +230,19 @@ predicate basicLoadStep(Node nodeFrom, Node nodeTo, TypeTrackerContent content)
205230
nodeFrom.asExpr() = call.getReceiver() and
206231
nodeTo.asExpr() = call
207232
)
233+
or
234+
exists(
235+
DataFlowPublic::CallNode call, SummaryComponent input, DataFlowPublic::ContentSet contents,
236+
SummaryComponent output
237+
|
238+
summarizableCall(call.asExpr().getExpr(), //
239+
SummaryComponentStack::push(SummaryComponent::content(contents),
240+
SummaryComponentStack::singleton(input)), //
241+
SummaryComponentStack::singleton(output)) and
242+
nodeFrom = evaluateSummaryComponentLocal(call, input) and
243+
nodeTo = evaluateSummaryComponentLocal(call, output) and
244+
content.asContent() = contents.getAReadContent()
245+
)
208246
}
209247

210248
/**
@@ -213,3 +251,43 @@ predicate basicLoadStep(Node nodeFrom, Node nodeTo, TypeTrackerContent content)
213251
class Boolean extends boolean {
214252
Boolean() { this = true or this = false }
215253
}
254+
255+
/** Holds if `call` has a summary consisting of the given `input`/`output` pair. */
256+
private predicate summarizableCall(
257+
MethodCall call, SummaryComponentStack input, SummaryComponentStack output
258+
) {
259+
exists(SummarizedCallable callable |
260+
call = callable.getACallSimple() and
261+
callable.propagatesFlow(input, output, true)
262+
)
263+
}
264+
265+
/**
266+
* Gets a data flow node corresponding an argument or return value of `call`,
267+
* as specified by `component`.
268+
*/
269+
bindingset[call, component]
270+
private DataFlowPublic::Node evaluateSummaryComponentLocal(
271+
DataFlowPublic::CallNode call, SummaryComponent component
272+
) {
273+
exists(DataFlowDispatch::ParameterPosition pos | component = SummaryComponent::argument(pos) |
274+
exists(int i |
275+
pos.isPositional(i) and
276+
result = call.getPositionalArgument(i)
277+
)
278+
or
279+
exists(string name |
280+
pos.isKeyword(name) and
281+
result = call.getKeywordArgument(name)
282+
)
283+
or
284+
pos.isBlock() and
285+
result = call.getBlock()
286+
or
287+
pos.isSelf() and
288+
result = call.getReceiver()
289+
)
290+
or
291+
component = SummaryComponent::return() and
292+
result = call
293+
}

ruby/ql/test/library-tests/dataflow/api-graphs/ApiGraphs.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ classMethodCalls
44
instanceMethodCalls
55
| test1.rb:61:1:61:12 | Use getMember("M1").getMember("C1").getMethod("new").getReturn().getMethod("m").getReturn() |
66
| test1.rb:62:1:62:12 | Use getMember("M2").getMember("C3").getMethod("new").getReturn().getMethod("m").getReturn() |
7+
flowThroughArray
8+
| test1.rb:73:1:73:10 | call to m |

ruby/ql/test/library-tests/dataflow/api-graphs/ApiGraphs.ql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
* Tests of the public API of API Graphs
33
*/
44

5+
import ruby
6+
import codeql.ruby.DataFlow
57
import codeql.ruby.ApiGraphs
68

79
query predicate classMethodCalls(API::Node node) {
@@ -11,3 +13,8 @@ query predicate classMethodCalls(API::Node node) {
1113
query predicate instanceMethodCalls(API::Node node) {
1214
node = API::getTopLevelMember("M1").getMember("C1").getInstance().getReturn("m")
1315
}
16+
17+
query predicate flowThroughArray(DataFlow::Node node) {
18+
node =
19+
API::getTopLevelMember("A").getMember("B").getMember("C").getMethod("m").getReturn().asSource()
20+
}

ruby/ql/test/library-tests/dataflow/api-graphs/test1.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,6 @@ def userDefinedFunction(x, y)
6868
x.customEntryPointCall(y) #$ call=entryPoint("CustomEntryPointCall") use=entryPoint("CustomEntryPointCall").getReturn() rhs=entryPoint("CustomEntryPointCall").getParameter(0)
6969
x.customEntryPointUse(y) #$ use=entryPoint("CustomEntryPointUse")
7070
end
71+
72+
array = [A::B::C] #$ use=getMember("Array").getMethod("[]").getReturn()
73+
array[0].m #$ use=getMember("A").getMember("B").getMember("C").getMethod("m").getReturn()

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

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,12 @@ track
5555
| type_tracker.rb:14:5:14:13 | call to field= | type tracker without call steps | type_tracker.rb:14:5:14:13 | call to field= |
5656
| type_tracker.rb:14:17:14:23 | "hello" | type tracker with call steps | type_tracker.rb:2:16:2:18 | val |
5757
| type_tracker.rb:14:17:14:23 | "hello" | type tracker with call steps | type_tracker.rb:2:16:2:18 | val |
58-
| type_tracker.rb:14:17:14:23 | "hello" | type tracker with call steps with content field | type_tracker.rb:7:5:9:7 | self (field) |
59-
| type_tracker.rb:14:17:14:23 | "hello" | type tracker with call steps with content field | type_tracker.rb:7:5:9:7 | self in field |
58+
| type_tracker.rb:14:17:14:23 | "hello" | type tracker with call steps with content attribute field | type_tracker.rb:7:5:9:7 | self (field) |
59+
| type_tracker.rb:14:17:14:23 | "hello" | type tracker with call steps with content attribute field | type_tracker.rb:7:5:9:7 | self in field |
6060
| type_tracker.rb:14:17:14:23 | "hello" | type tracker without call steps | type_tracker.rb:14:5:14:13 | call to field= |
6161
| type_tracker.rb:14:17:14:23 | "hello" | type tracker without call steps | type_tracker.rb:14:17:14:23 | "hello" |
6262
| type_tracker.rb:14:17:14:23 | "hello" | type tracker without call steps | type_tracker.rb:15:10:15:18 | call to field |
63-
| type_tracker.rb:14:17:14:23 | "hello" | type tracker without call steps with content field | type_tracker.rb:14:5:14:7 | [post] var |
63+
| type_tracker.rb:14:17:14:23 | "hello" | type tracker without call steps with content attribute field | type_tracker.rb:14:5:14:7 | [post] var |
6464
| type_tracker.rb:14:17:14:23 | __synth__0 | type tracker without call steps | type_tracker.rb:14:17:14:23 | __synth__0 |
6565
| type_tracker.rb:15:5:15:18 | call to puts | type tracker without call steps | type_tracker.rb:12:1:16:3 | return return in m |
6666
| type_tracker.rb:15:5:15:18 | call to puts | type tracker without call steps | type_tracker.rb:15:5:15:18 | call to puts |
@@ -147,6 +147,25 @@ track
147147
| type_tracker.rb:32:26:32:26 | 8 | type tracker with call steps | type_tracker.rb:25:13:25:14 | p1 |
148148
| type_tracker.rb:32:26:32:26 | 8 | type tracker with call steps | type_tracker.rb:25:13:25:14 | p1 |
149149
| type_tracker.rb:32:26:32:26 | 8 | type tracker without call steps | type_tracker.rb:32:26:32:26 | 8 |
150+
| type_tracker.rb:34:1:37:3 | &block | type tracker without call steps | type_tracker.rb:34:1:37:3 | &block |
151+
| type_tracker.rb:34:1:37:3 | return return in throughArray | type tracker without call steps | type_tracker.rb:34:1:37:3 | return return in throughArray |
152+
| type_tracker.rb:34:1:37:3 | self in throughArray | type tracker without call steps | type_tracker.rb:34:1:37:3 | self in throughArray |
153+
| type_tracker.rb:34:1:37:3 | throughArray | type tracker without call steps | type_tracker.rb:34:1:37:3 | throughArray |
154+
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps | type_tracker.rb:34:18:34:20 | obj |
155+
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps | type_tracker.rb:36:5:36:10 | ...[...] |
156+
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps with content element 0 | type_tracker.rb:35:11:35:15 | call to [] |
157+
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:1:37:3 | return return in throughArray |
158+
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:18:34:20 | obj |
159+
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:18:34:20 | obj |
160+
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:18:34:20 | obj |
161+
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:36:5:36:10 | ...[...] |
162+
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element 0 | type_tracker.rb:35:11:35:15 | call to [] |
163+
| type_tracker.rb:35:5:35:7 | tmp | type tracker without call steps | type_tracker.rb:35:5:35:7 | tmp |
164+
| type_tracker.rb:35:11:35:15 | Array | type tracker without call steps | type_tracker.rb:35:11:35:15 | Array |
165+
| type_tracker.rb:35:11:35:15 | call to [] | type tracker without call steps | type_tracker.rb:35:11:35:15 | call to [] |
166+
| type_tracker.rb:36:5:36:10 | ...[...] | type tracker without call steps | type_tracker.rb:34:1:37:3 | return return in throughArray |
167+
| type_tracker.rb:36:5:36:10 | ...[...] | type tracker without call steps | type_tracker.rb:36:5:36:10 | ...[...] |
168+
| type_tracker.rb:36:9:36:9 | 0 | type tracker without call steps | type_tracker.rb:36:9:36:9 | 0 |
150169
trackEnd
151170
| type_tracker.rb:1:1:10:3 | self (type_tracker.rb) | type_tracker.rb:1:1:10:3 | self (type_tracker.rb) |
152171
| type_tracker.rb:1:1:10:3 | self (type_tracker.rb) | type_tracker.rb:18:1:21:3 | self (positional) |
@@ -358,3 +377,25 @@ trackEnd
358377
| type_tracker.rb:32:26:32:26 | 8 | type_tracker.rb:25:13:25:14 | p1 |
359378
| type_tracker.rb:32:26:32:26 | 8 | type_tracker.rb:26:10:26:11 | p1 |
360379
| type_tracker.rb:32:26:32:26 | 8 | type_tracker.rb:32:26:32:26 | 8 |
380+
| type_tracker.rb:34:1:37:3 | &block | type_tracker.rb:34:1:37:3 | &block |
381+
| type_tracker.rb:34:1:37:3 | return return in throughArray | type_tracker.rb:34:1:37:3 | return return in throughArray |
382+
| type_tracker.rb:34:1:37:3 | self in throughArray | type_tracker.rb:34:1:37:3 | self in throughArray |
383+
| type_tracker.rb:34:1:37:3 | throughArray | type_tracker.rb:34:1:37:3 | throughArray |
384+
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:1:37:3 | return return in throughArray |
385+
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:18:34:20 | obj |
386+
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:18:34:20 | obj |
387+
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:18:34:20 | obj |
388+
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:18:34:20 | obj |
389+
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:35:12:35:14 | obj |
390+
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:35:12:35:14 | obj |
391+
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:36:5:36:10 | ...[...] |
392+
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:36:5:36:10 | ...[...] |
393+
| type_tracker.rb:35:5:35:7 | tmp | type_tracker.rb:35:5:35:7 | tmp |
394+
| type_tracker.rb:35:11:35:15 | Array | type_tracker.rb:35:11:35:15 | Array |
395+
| type_tracker.rb:35:11:35:15 | call to [] | type_tracker.rb:35:5:35:15 | ... = ... |
396+
| type_tracker.rb:35:11:35:15 | call to [] | type_tracker.rb:35:5:35:15 | ... = ... |
397+
| type_tracker.rb:35:11:35:15 | call to [] | type_tracker.rb:35:11:35:15 | call to [] |
398+
| type_tracker.rb:35:11:35:15 | call to [] | type_tracker.rb:36:5:36:7 | tmp |
399+
| type_tracker.rb:36:5:36:10 | ...[...] | type_tracker.rb:34:1:37:3 | return return in throughArray |
400+
| type_tracker.rb:36:5:36:10 | ...[...] | type_tracker.rb:36:5:36:10 | ...[...] |
401+
| type_tracker.rb:36:9:36:9 | 0 | type_tracker.rb:36:9:36:9 | 0 |

ruby/ql/test/library-tests/dataflow/type-tracker/type_tracker.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,8 @@ def keyword(p1:, p2:)
3030
keyword(p1: 3, p2: 4)
3131
keyword(p2: 5, p1: 6)
3232
keyword(:p2 => 7, :p1 => 8)
33+
34+
def throughArray(obj)
35+
tmp = [obj]
36+
tmp[0]
37+
end

0 commit comments

Comments
 (0)