Skip to content

Commit

Permalink
Ruby: Rework hash splat argument/parameter matching
Browse files Browse the repository at this point in the history
  • Loading branch information
hvitved committed Aug 15, 2024
1 parent dc8e1f1 commit 3ca4888
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 208 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1538,9 +1538,14 @@ predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) {
or
exists(string name | ppos.isKeyword(name) and apos.isKeyword(name))
or
(ppos.isHashSplat() or ppos.isSynthHashSplat()) and
ppos.isHashSplat() and
(apos.isHashSplat() or apos.isSynthHashSplat())
or
// no case for `apos.isSynthHashSplat() and ppos.isSynthHashSplat()`, since
// direct keyword matching is possible
ppos.isSynthHashSplat() and
apos.isHashSplat()
or
exists(int pos, boolean hasActualSplatParam, boolean hasActualSplatArg |
(
ppos.isSplat(pos) and
Expand Down
53 changes: 3 additions & 50 deletions ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,7 @@ private module Cached {
)
} or
deprecated TSplatContent(int i, Boolean shifted) { i in [0 .. 10] } or
THashSplatContent(ConstantValue::ConstantSymbolValue cv) or
deprecated THashSplatContent(ConstantValue::ConstantSymbolValue cv) or
TCapturedVariableContent(VariableCapture::CapturedVariable v) or
// Only used by type-tracking
TAttributeName(string name) { name = any(SetterMethodCall c).getTargetName() }
Expand All @@ -686,24 +686,16 @@ private module Cached {
TUnknownElementContentApprox() or
TKnownIntegerElementContentApprox() or
TKnownElementContentApprox(string approx) { approx = approxKnownElementIndex(_) } or
THashSplatContentApprox(string approx) { approx = approxKnownElementIndex(_) } or
TNonElementContentApprox(Content c) { not c instanceof Content::ElementContent } or
TCapturedVariableContentApprox(VariableCapture::CapturedVariable v)

cached
newtype TDataFlowType =
TLambdaDataFlowType(Callable c) { c = any(LambdaSelfReferenceNode n).getCallable() } or
// In order to reduce the set of cons-candidates, we annotate all implicit (hash) splat
// creations with the name of the method that they are passed into. This includes
// array/hash literals as well (where the name is simply `[]`), because of how they
// are modeled (see `Array.qll` and `Hash.qll`).
TSynthHashSplatArgumentType(string methodName) {
methodName = any(SynthHashSplatArgumentNode n).getMethodName()
} or
TUnknownDataFlowType()
}

class TElementContent = TKnownElementContent or TUnknownElementContent or THashSplatContent;
class TElementContent = TKnownElementContent or TUnknownElementContent;

import Cached

Expand Down Expand Up @@ -1118,18 +1110,6 @@ private module ParameterNodes {
*
* by adding read steps out of the synthesized parameter node to the relevant
* keyword parameters.
*
* In order to avoid redundancy (and improve performance) in cases like
*
* ```rb
* foo(p1: taint(1), p2: taint(2))
* ```
*
* where direct keyword matching is possible, we use a special `HashSplatContent`
* (instead of reusing `KnownElementContent`) when we construct a synthesized hash
* splat argument (`SynthHashSplatArgumentNode`) at the call site, and then only
* add read steps out of this node for actual hash-splat arguments (which will use
* a normal `KnownElementContent`).
*/
class SynthHashSplatParameterNode extends ParameterNodeImpl, TSynthHashSplatParameterNode {
private DataFlowCallable callable;
Expand Down Expand Up @@ -1436,24 +1416,7 @@ module ArgumentNodes {
not cv.isSymbol(_)
)
|
if call instanceof CfgNodes::ExprNodes::HashLiteralCfgNode
then
/*
* Needed for cases like
*
* ```rb
* hash = { a: taint, b: safe }
*
* def foo(a:, b:)
* sink(a)
* end
*
* foo(**hash)
* ```
*/

c.isSingleton(Content::getElementContent(cv))
else c.isSingleton(THashSplatContent(cv))
c.isSingleton(Content::getElementContent(cv))
)
}

Expand Down Expand Up @@ -1938,11 +1901,8 @@ DataFlowType getNodeType(Node n) {
result = TLambdaDataFlowType(c)
)
or
result = TSynthHashSplatArgumentType(n.(SynthHashSplatArgumentNode).getMethodName())
or
not n instanceof LambdaSelfReferenceNode and
not mustHaveLambdaType(n, _) and
not n instanceof SynthHashSplatArgumentNode and
result = TUnknownDataFlowType()
}

Expand Down Expand Up @@ -2168,11 +2128,6 @@ class ContentApprox extends TContentApprox {
result = "approximated element " + approx
)
or
exists(string s |
this = THashSplatContentApprox(s) and
result = "approximated hash-splat position " + s
)
or
exists(Content c |
this = TNonElementContentApprox(c) and
result = c.toString()
Expand Down Expand Up @@ -2212,8 +2167,6 @@ ContentApprox getContentApprox(Content c) {
result =
TKnownElementContentApprox(approxKnownElementIndex(c.(Content::KnownElementContent).getIndex()))
or
result = THashSplatContentApprox(approxKnownElementIndex(c.(Content::HashSplatContent).getKey()))
or
result = TNonElementContentApprox(c)
}

Expand Down
5 changes: 1 addition & 4 deletions ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,7 @@ module Content {
*
* we have an implicit hash-splat argument containing `{:a => 1, :b => 2, :c => 3}`.
*/
class HashSplatContent extends ElementContent, THashSplatContent {
deprecated class HashSplatContent extends Content, THashSplatContent {
private ConstantValue::ConstantSymbolValue cv;

HashSplatContent() { this = THashSplatContent(cv) }
Expand Down Expand Up @@ -797,7 +797,6 @@ class ContentSet extends TContentSet {
private Content getAnElementReadContent() {
exists(Content::KnownElementContent c | this.isKnownOrUnknownElement(c) |
result = c or
result = THashSplatContent(c.getIndex()) or
result = TUnknownElementContent()
)
or
Expand All @@ -815,8 +814,6 @@ class ContentSet extends TContentSet {
|
type = result.(Content::KnownElementContent).getIndex().getValueType()
or
type = result.(Content::HashSplatContent).getKey().getValueType()
or
includeUnknown = true and
result = TUnknownElementContent()
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
failures
testFailures
failures
Original file line number Diff line number Diff line change
Expand Up @@ -144,14 +144,14 @@ edges
| semantics.rb:108:5:108:5 | b | semantics.rb:110:27:110:27 | b | provenance | |
| semantics.rb:108:9:108:18 | call to source | semantics.rb:108:5:108:5 | b | provenance | |
| semantics.rb:108:9:108:18 | call to source | semantics.rb:108:5:108:5 | b | provenance | |
| semantics.rb:109:10:109:28 | call to s15 [hash-splat position :foo] | semantics.rb:109:10:109:34 | ...[...] | provenance | |
| semantics.rb:109:10:109:28 | call to s15 [hash-splat position :foo] | semantics.rb:109:10:109:34 | ...[...] | provenance | |
| semantics.rb:109:19:109:19 | a | semantics.rb:109:10:109:28 | call to s15 [hash-splat position :foo] | provenance | |
| semantics.rb:109:19:109:19 | a | semantics.rb:109:10:109:28 | call to s15 [hash-splat position :foo] | provenance | |
| semantics.rb:110:10:110:28 | call to s15 [hash-splat position :bar] | semantics.rb:110:10:110:34 | ...[...] | provenance | |
| semantics.rb:110:10:110:28 | call to s15 [hash-splat position :bar] | semantics.rb:110:10:110:34 | ...[...] | provenance | |
| semantics.rb:110:27:110:27 | b | semantics.rb:110:10:110:28 | call to s15 [hash-splat position :bar] | provenance | |
| semantics.rb:110:27:110:27 | b | semantics.rb:110:10:110:28 | call to s15 [hash-splat position :bar] | provenance | |
| semantics.rb:109:10:109:28 | call to s15 [element :foo] | semantics.rb:109:10:109:34 | ...[...] | provenance | |
| semantics.rb:109:10:109:28 | call to s15 [element :foo] | semantics.rb:109:10:109:34 | ...[...] | provenance | |
| semantics.rb:109:19:109:19 | a | semantics.rb:109:10:109:28 | call to s15 [element :foo] | provenance | |
| semantics.rb:109:19:109:19 | a | semantics.rb:109:10:109:28 | call to s15 [element :foo] | provenance | |
| semantics.rb:110:10:110:28 | call to s15 [element :bar] | semantics.rb:110:10:110:34 | ...[...] | provenance | |
| semantics.rb:110:10:110:28 | call to s15 [element :bar] | semantics.rb:110:10:110:34 | ...[...] | provenance | |
| semantics.rb:110:27:110:27 | b | semantics.rb:110:10:110:28 | call to s15 [element :bar] | provenance | |
| semantics.rb:110:27:110:27 | b | semantics.rb:110:10:110:28 | call to s15 [element :bar] | provenance | |
| semantics.rb:114:5:114:5 | a | semantics.rb:116:14:116:14 | a | provenance | |
| semantics.rb:114:5:114:5 | a | semantics.rb:116:14:116:14 | a | provenance | |
| semantics.rb:114:5:114:5 | a | semantics.rb:119:17:119:17 | a | provenance | |
Expand Down Expand Up @@ -1269,14 +1269,14 @@ nodes
| semantics.rb:108:5:108:5 | b | semmle.label | b |
| semantics.rb:108:9:108:18 | call to source | semmle.label | call to source |
| semantics.rb:108:9:108:18 | call to source | semmle.label | call to source |
| semantics.rb:109:10:109:28 | call to s15 [hash-splat position :foo] | semmle.label | call to s15 [hash-splat position :foo] |
| semantics.rb:109:10:109:28 | call to s15 [hash-splat position :foo] | semmle.label | call to s15 [hash-splat position :foo] |
| semantics.rb:109:10:109:28 | call to s15 [element :foo] | semmle.label | call to s15 [element :foo] |
| semantics.rb:109:10:109:28 | call to s15 [element :foo] | semmle.label | call to s15 [element :foo] |
| semantics.rb:109:10:109:34 | ...[...] | semmle.label | ...[...] |
| semantics.rb:109:10:109:34 | ...[...] | semmle.label | ...[...] |
| semantics.rb:109:19:109:19 | a | semmle.label | a |
| semantics.rb:109:19:109:19 | a | semmle.label | a |
| semantics.rb:110:10:110:28 | call to s15 [hash-splat position :bar] | semmle.label | call to s15 [hash-splat position :bar] |
| semantics.rb:110:10:110:28 | call to s15 [hash-splat position :bar] | semmle.label | call to s15 [hash-splat position :bar] |
| semantics.rb:110:10:110:28 | call to s15 [element :bar] | semmle.label | call to s15 [element :bar] |
| semantics.rb:110:10:110:28 | call to s15 [element :bar] | semmle.label | call to s15 [element :bar] |
| semantics.rb:110:10:110:34 | ...[...] | semmle.label | ...[...] |
| semantics.rb:110:10:110:34 | ...[...] | semmle.label | ...[...] |
| semantics.rb:110:27:110:27 | b | semmle.label | b |
Expand Down
Loading

0 comments on commit 3ca4888

Please sign in to comment.