mirror of
https://github.com/github/codeql.git
synced 2026-04-28 10:15:14 +02:00
Ruby: generate type-tracking steps from simple summary specs
This commit is contained in:
@@ -373,7 +373,7 @@ private module Cached {
|
||||
n instanceof SynthReturnNode
|
||||
or
|
||||
// Needed for stores in type tracking
|
||||
TypeTrackerSpecific::basicStoreStep(_, n, _)
|
||||
TypeTrackerSpecific::postUpdateStoreStep(_, n, _)
|
||||
}
|
||||
|
||||
cached
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
private import codeql.ruby.AST as Ast
|
||||
private import codeql.ruby.CFG as Cfg
|
||||
private import Cfg::CfgNodes
|
||||
private import codeql.ruby.dataflow.FlowSummary
|
||||
private import codeql.ruby.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
|
||||
private import codeql.ruby.dataflow.internal.DataFlowPublic as DataFlowPublic
|
||||
private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate
|
||||
private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch
|
||||
private import codeql.ruby.dataflow.internal.SsaImpl as SsaImpl
|
||||
private import codeql.ruby.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import codeql.ruby.dataflow.internal.FlowSummaryImplSpecific as FlowSummaryImplSpecific
|
||||
private import codeql.ruby.dataflow.internal.AccessPathSyntax
|
||||
|
||||
class Node = DataFlowPublic::Node;
|
||||
|
||||
@@ -169,6 +173,27 @@ predicate returnStep(Node nodeFrom, Node nodeTo) {
|
||||
* called.
|
||||
*/
|
||||
predicate basicStoreStep(Node nodeFrom, Node nodeTo, TypeTrackerContent content) {
|
||||
postUpdateStoreStep(nodeFrom, nodeTo, content)
|
||||
or
|
||||
exists(
|
||||
DataFlowPublic::CallNode call, SummaryComponent input, DataFlowPublic::ContentSet contents,
|
||||
SummaryComponent output
|
||||
|
|
||||
summarizableCall(call.asExpr().getExpr(), //
|
||||
SummaryComponentStack::singleton(input),
|
||||
SummaryComponentStack::push(SummaryComponent::content(contents),
|
||||
SummaryComponentStack::singleton(output))) and
|
||||
nodeFrom = evaluateSummaryComponentLocal(call, input) and
|
||||
nodeTo = evaluateSummaryComponentLocal(call, output) and
|
||||
content.asContent() = contents.getAStoreContent()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A `content`-store step from `nodeFrom -> nodeTo` where the destination node is a post-update
|
||||
* node that should be treated as a local source node.
|
||||
*/
|
||||
predicate postUpdateStoreStep(Node nodeFrom, Node nodeTo, TypeTrackerContent content) {
|
||||
// TODO: support SetterMethodCall inside TuplePattern
|
||||
exists(ExprNodes::MethodCallCfgNode call |
|
||||
content = MkAttribute(getSetterCallAttributeName(call.getExpr())) and
|
||||
@@ -205,6 +230,19 @@ predicate basicLoadStep(Node nodeFrom, Node nodeTo, TypeTrackerContent content)
|
||||
nodeFrom.asExpr() = call.getReceiver() and
|
||||
nodeTo.asExpr() = call
|
||||
)
|
||||
or
|
||||
exists(
|
||||
DataFlowPublic::CallNode call, SummaryComponent input, DataFlowPublic::ContentSet contents,
|
||||
SummaryComponent output
|
||||
|
|
||||
summarizableCall(call.asExpr().getExpr(), //
|
||||
SummaryComponentStack::push(SummaryComponent::content(contents),
|
||||
SummaryComponentStack::singleton(input)), //
|
||||
SummaryComponentStack::singleton(output)) and
|
||||
nodeFrom = evaluateSummaryComponentLocal(call, input) and
|
||||
nodeTo = evaluateSummaryComponentLocal(call, output) and
|
||||
content.asContent() = contents.getAReadContent()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,3 +251,43 @@ predicate basicLoadStep(Node nodeFrom, Node nodeTo, TypeTrackerContent content)
|
||||
class Boolean extends boolean {
|
||||
Boolean() { this = true or this = false }
|
||||
}
|
||||
|
||||
/** Holds if `call` has a summary consisting of the given `input`/`output` pair. */
|
||||
private predicate summarizableCall(
|
||||
MethodCall call, SummaryComponentStack input, SummaryComponentStack output
|
||||
) {
|
||||
exists(SummarizedCallable callable |
|
||||
call = callable.getACallSimple() and
|
||||
callable.propagatesFlow(input, output, true)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node corresponding an argument or return value of `call`,
|
||||
* as specified by `component`.
|
||||
*/
|
||||
bindingset[call, component]
|
||||
private DataFlowPublic::Node evaluateSummaryComponentLocal(
|
||||
DataFlowPublic::CallNode call, SummaryComponent component
|
||||
) {
|
||||
exists(DataFlowDispatch::ParameterPosition pos | component = SummaryComponent::argument(pos) |
|
||||
exists(int i |
|
||||
pos.isPositional(i) and
|
||||
result = call.getPositionalArgument(i)
|
||||
)
|
||||
or
|
||||
exists(string name |
|
||||
pos.isKeyword(name) and
|
||||
result = call.getKeywordArgument(name)
|
||||
)
|
||||
or
|
||||
pos.isBlock() and
|
||||
result = call.getBlock()
|
||||
or
|
||||
pos.isSelf() and
|
||||
result = call.getReceiver()
|
||||
)
|
||||
or
|
||||
component = SummaryComponent::return() and
|
||||
result = call
|
||||
}
|
||||
|
||||
@@ -4,3 +4,5 @@ classMethodCalls
|
||||
instanceMethodCalls
|
||||
| test1.rb:61:1:61:12 | Use getMember("M1").getMember("C1").getMethod("new").getReturn().getMethod("m").getReturn() |
|
||||
| test1.rb:62:1:62:12 | Use getMember("M2").getMember("C3").getMethod("new").getReturn().getMethod("m").getReturn() |
|
||||
flowThroughArray
|
||||
| test1.rb:73:1:73:10 | call to m |
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* Tests of the public API of API Graphs
|
||||
*/
|
||||
|
||||
import ruby
|
||||
import codeql.ruby.DataFlow
|
||||
import codeql.ruby.ApiGraphs
|
||||
|
||||
query predicate classMethodCalls(API::Node node) {
|
||||
@@ -11,3 +13,8 @@ query predicate classMethodCalls(API::Node node) {
|
||||
query predicate instanceMethodCalls(API::Node node) {
|
||||
node = API::getTopLevelMember("M1").getMember("C1").getInstance().getReturn("m")
|
||||
}
|
||||
|
||||
query predicate flowThroughArray(DataFlow::Node node) {
|
||||
node =
|
||||
API::getTopLevelMember("A").getMember("B").getMember("C").getMethod("m").getReturn().asSource()
|
||||
}
|
||||
|
||||
@@ -68,3 +68,6 @@ def userDefinedFunction(x, y)
|
||||
x.customEntryPointCall(y) #$ call=entryPoint("CustomEntryPointCall") use=entryPoint("CustomEntryPointCall").getReturn() rhs=entryPoint("CustomEntryPointCall").getParameter(0)
|
||||
x.customEntryPointUse(y) #$ use=entryPoint("CustomEntryPointUse")
|
||||
end
|
||||
|
||||
array = [A::B::C] #$ use=getMember("Array").getMethod("[]").getReturn()
|
||||
array[0].m #$ use=getMember("A").getMember("B").getMember("C").getMethod("m").getReturn()
|
||||
|
||||
@@ -55,12 +55,12 @@ track
|
||||
| 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= |
|
||||
| type_tracker.rb:14:17:14:23 | "hello" | type tracker with call steps | type_tracker.rb:2:16:2:18 | val |
|
||||
| type_tracker.rb:14:17:14:23 | "hello" | type tracker with call steps | type_tracker.rb:2:16:2:18 | val |
|
||||
| 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) |
|
||||
| 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 |
|
||||
| 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) |
|
||||
| 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 |
|
||||
| type_tracker.rb:14:17:14:23 | "hello" | type tracker without call steps | type_tracker.rb:14:5:14:13 | call to field= |
|
||||
| type_tracker.rb:14:17:14:23 | "hello" | type tracker without call steps | type_tracker.rb:14:17:14:23 | "hello" |
|
||||
| type_tracker.rb:14:17:14:23 | "hello" | type tracker without call steps | type_tracker.rb:15:10:15:18 | call to field |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| type_tracker.rb:14:17:14:23 | __synth__0 | type tracker without call steps | type_tracker.rb:14:17:14:23 | __synth__0 |
|
||||
| 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 |
|
||||
| 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
|
||||
| type_tracker.rb:32:26:32:26 | 8 | type tracker with call steps | type_tracker.rb:25:13:25:14 | p1 |
|
||||
| type_tracker.rb:32:26:32:26 | 8 | type tracker with call steps | type_tracker.rb:25:13:25:14 | p1 |
|
||||
| type_tracker.rb:32:26:32:26 | 8 | type tracker without call steps | type_tracker.rb:32:26:32:26 | 8 |
|
||||
| type_tracker.rb:34:1:37:3 | &block | type tracker without call steps | type_tracker.rb:34:1:37:3 | &block |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| type_tracker.rb:34:1:37:3 | throughArray | type tracker without call steps | type_tracker.rb:34:1:37:3 | throughArray |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps | type_tracker.rb:34:18:34:20 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps | type_tracker.rb:36:5:36:10 | ...[...] |
|
||||
| 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 [] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:1:37:3 | return return in throughArray |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:18:34:20 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:18:34:20 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:18:34:20 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:36:5:36:10 | ...[...] |
|
||||
| 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 [] |
|
||||
| type_tracker.rb:35:5:35:7 | tmp | type tracker without call steps | type_tracker.rb:35:5:35:7 | tmp |
|
||||
| type_tracker.rb:35:11:35:15 | Array | type tracker without call steps | type_tracker.rb:35:11:35:15 | Array |
|
||||
| type_tracker.rb:35:11:35:15 | call to [] | type tracker without call steps | type_tracker.rb:35:11:35:15 | call to [] |
|
||||
| type_tracker.rb:36:5:36:10 | ...[...] | type tracker without call steps | type_tracker.rb:34:1:37:3 | return return in throughArray |
|
||||
| type_tracker.rb:36:5:36:10 | ...[...] | type tracker without call steps | type_tracker.rb:36:5:36:10 | ...[...] |
|
||||
| type_tracker.rb:36:9:36:9 | 0 | type tracker without call steps | type_tracker.rb:36:9:36:9 | 0 |
|
||||
trackEnd
|
||||
| type_tracker.rb:1:1:10:3 | self (type_tracker.rb) | type_tracker.rb:1:1:10:3 | self (type_tracker.rb) |
|
||||
| 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
|
||||
| type_tracker.rb:32:26:32:26 | 8 | type_tracker.rb:25:13:25:14 | p1 |
|
||||
| type_tracker.rb:32:26:32:26 | 8 | type_tracker.rb:26:10:26:11 | p1 |
|
||||
| type_tracker.rb:32:26:32:26 | 8 | type_tracker.rb:32:26:32:26 | 8 |
|
||||
| type_tracker.rb:34:1:37:3 | &block | type_tracker.rb:34:1:37:3 | &block |
|
||||
| type_tracker.rb:34:1:37:3 | return return in throughArray | type_tracker.rb:34:1:37:3 | return return in throughArray |
|
||||
| type_tracker.rb:34:1:37:3 | self in throughArray | type_tracker.rb:34:1:37:3 | self in throughArray |
|
||||
| type_tracker.rb:34:1:37:3 | throughArray | type_tracker.rb:34:1:37:3 | throughArray |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:1:37:3 | return return in throughArray |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:18:34:20 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:18:34:20 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:18:34:20 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:18:34:20 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:35:12:35:14 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:35:12:35:14 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:36:5:36:10 | ...[...] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:36:5:36:10 | ...[...] |
|
||||
| type_tracker.rb:35:5:35:7 | tmp | type_tracker.rb:35:5:35:7 | tmp |
|
||||
| type_tracker.rb:35:11:35:15 | Array | type_tracker.rb:35:11:35:15 | Array |
|
||||
| type_tracker.rb:35:11:35:15 | call to [] | type_tracker.rb:35:5:35:15 | ... = ... |
|
||||
| type_tracker.rb:35:11:35:15 | call to [] | type_tracker.rb:35:5:35:15 | ... = ... |
|
||||
| type_tracker.rb:35:11:35:15 | call to [] | type_tracker.rb:35:11:35:15 | call to [] |
|
||||
| type_tracker.rb:35:11:35:15 | call to [] | type_tracker.rb:36:5:36:7 | tmp |
|
||||
| type_tracker.rb:36:5:36:10 | ...[...] | type_tracker.rb:34:1:37:3 | return return in throughArray |
|
||||
| type_tracker.rb:36:5:36:10 | ...[...] | type_tracker.rb:36:5:36:10 | ...[...] |
|
||||
| type_tracker.rb:36:9:36:9 | 0 | type_tracker.rb:36:9:36:9 | 0 |
|
||||
|
||||
@@ -30,3 +30,8 @@ end
|
||||
keyword(p1: 3, p2: 4)
|
||||
keyword(p2: 5, p1: 6)
|
||||
keyword(:p2 => 7, :p1 => 8)
|
||||
|
||||
def throughArray(obj)
|
||||
tmp = [obj]
|
||||
tmp[0]
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user