Skip to content

Commit

Permalink
temp
Browse files Browse the repository at this point in the history
  • Loading branch information
hvitved committed Sep 18, 2023
1 parent aa3bafe commit 5ae4441
Show file tree
Hide file tree
Showing 7 changed files with 398 additions and 362 deletions.
27 changes: 26 additions & 1 deletion ruby/ql/lib/codeql/ruby/ApiGraphs.qll
Original file line number Diff line number Diff line change
Expand Up @@ -887,7 +887,9 @@ module API {
or
implicitCallEdge(pred, succ)
or
exists(DataFlow::HashLiteralNode splat | hashSplatEdge(splat, pred, succ))
exists(DataFlow::HashLiteralNode literal | hashSplatEdge(literal, pred, succ))

Check warning

Code scanning / CodeQL

Omittable 'exists' variable Warning

This exists variable can be omitted by using a don't-care expression
in this argument
.
or
exists(DataFlow::ArrayLiteralNode literal | splatEdge(literal, pred, succ))

Check warning

Code scanning / CodeQL

Omittable 'exists' variable Warning

This exists variable can be omitted by using a don't-care expression
in this argument
.
}

/**
Expand Down Expand Up @@ -1009,6 +1011,29 @@ module API {
)
}

pragma[nomagic]
private DataFlow::Node getSplatArgument(DataFlow::ArrayLiteralNode literal) {
result = DataFlowPrivate::TSynthSplatArgumentNode(literal.asExpr())
}

/**
* Holds if the epsilon edge `pred -> succ` should be generated to account for the members of an array literal.
*
* This currently exists because array literals are desugared to `Array.[]` calls, whose summary relies on `WithContent`.
* However, `contentEdge` does not currently generate edges for `WithContent` steps.
*/
bindingset[literal]
pragma[inline_late]
private predicate splatEdge(DataFlow::ArrayLiteralNode literal, ApiNode pred, ApiNode succ) {
exists(TypeTracker t |
pred = Impl::MkForwardNode(getALocalSourceStrict(getSplatArgument(literal)), t) and
succ = Impl::MkForwardNode(pragma[only_bind_out](literal), pragma[only_bind_out](t))
or
succ = Impl::MkBackwardNode(getALocalSourceStrict(getSplatArgument(literal)), t) and
pred = Impl::MkBackwardNode(pragma[only_bind_out](literal), pragma[only_bind_out](t))
)
}

pragma[nomagic]
private DataFlow::LocalSourceNode getAModuleReference(DataFlow::ModuleNode mod) {
result = mod.getAnImmediateReference()
Expand Down
69 changes: 37 additions & 32 deletions ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll
Original file line number Diff line number Diff line change
Expand Up @@ -1255,16 +1255,35 @@ module ArgumentNodes {
}
}

abstract class SynthHashSplatOrSplatArgumentNode extends ArgumentNode, NodeImpl {
CfgNodes::ExprNodes::CallCfgNode call_;

final string getMethodName() {
result = call_.(CfgNodes::ExprNodes::MethodCallCfgNode).getMethodName()
or
not call_ instanceof CfgNodes::ExprNodes::MethodCallCfgNode and
result = "yield"
}

final override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
this.sourceArgumentOf(call.asCall(), pos)
}

final override CfgScope getCfgScope() { result = call_.getExpr().getCfgScope() }

final override Location getLocationImpl() { result = call_.getLocation() }
}

/**
* A data-flow node that represents all keyword arguments wrapped in a hash.
*
* The callee is responsible for filtering out the keyword arguments that are
* part of the method signature, such that those cannot end up in the hash-splat
* parameter. See also `SynthHashSplatParameterNode`.
*/
class SynthHashSplatArgumentNode extends ArgumentNode, NodeImpl, TSynthHashSplatArgumentNode {
CfgNodes::ExprNodes::CallCfgNode call_;

class SynthHashSplatArgumentNode extends SynthHashSplatOrSplatArgumentNode,
TSynthHashSplatArgumentNode
{
SynthHashSplatArgumentNode() { this = TSynthHashSplatArgumentNode(call_) }

/**
Expand Down Expand Up @@ -1295,19 +1314,11 @@ module ArgumentNodes {
)
}

override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
this.sourceArgumentOf(call.asCall(), pos)
}

override predicate sourceArgumentOf(CfgNodes::ExprNodes::CallCfgNode call, ArgumentPosition pos) {
call = call_ and
pos.isSynthHashSplat()
}

override CfgScope getCfgScope() { result = call_.getExpr().getCfgScope() }

override Location getLocationImpl() { result = call_.getLocation() }

override string toStringImpl() { result = "synthetic hash-splat argument" }
}

Expand All @@ -1318,9 +1329,7 @@ module ArgumentNodes {
* part of the method signature, such that those cannot end up in the splat
* parameter. See also `SynthSplatParameterNode`.
*/
class SynthSplatArgumentNode extends ArgumentNode, NodeImpl, TSynthSplatArgumentNode {
CfgNodes::ExprNodes::CallCfgNode call_;

class SynthSplatArgumentNode extends SynthHashSplatOrSplatArgumentNode, TSynthSplatArgumentNode {
SynthSplatArgumentNode() { this = TSynthSplatArgumentNode(call_) }

/**
Expand All @@ -1331,24 +1340,18 @@ module ArgumentNodes {
exists(ArgumentPosition pos, int n |
arg.asExpr().(Argument).isArgumentOf(call_, pos) and
pos.isPositional(n) and
c = getSplatContent(n, false) and
not exists(int i | splatArgumentAt(call_, i) and i < n)
not exists(int i | splatArgumentAt(call_, i) and i < n) and
if call_ instanceof CfgNodes::ExprNodes::ArrayLiteralCfgNode
then c = getArrayContent(n)
else c = getSplatContent(n, false)
)
}

override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
this.sourceArgumentOf(call.asCall(), pos)
}

override predicate sourceArgumentOf(CfgNodes::ExprNodes::CallCfgNode call, ArgumentPosition pos) {
call = call_ and
pos.isSynthSplat()
}

override CfgScope getCfgScope() { result = call_.getExpr().getCfgScope() }

override Location getLocationImpl() { result = call_.getLocation() }

override string toStringImpl() { result = "synthetic splat argument" }
}

Expand Down Expand Up @@ -1741,8 +1744,13 @@ predicate expectsContent(Node n, ContentSet c) {

private newtype TDataFlowType =
TLambdaDataFlowType(Callable c) { c = any(LambdaSelfReferenceNode n).getCallable() } or
// TSynthHashSplatArgumentType(SynthHashSplatArgumentNode n) or
// TSynthSplatArgumentType(SynthSplatArgumentNode n) or
TSynthHashSplatOrSplatArgumentType(string methodName) {
// 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 modelled (see `Array.qll` and `Hash.qll`).

Check warning

Code scanning / CodeQL

Misspelling Warning

This comment contains the non-US spelling 'modelled', which should instead be 'modeled'.
methodName = any(SynthHashSplatOrSplatArgumentNode n).getMethodName()
} or
TUnknownDataFlowType()

class DataFlowType extends TDataFlowType {
Expand Down Expand Up @@ -1772,14 +1780,11 @@ DataFlowType getNodeType(Node n) {
result = TLambdaDataFlowType(c)
)
or
// or
// result = TSynthHashSplatArgumentType(n)
// or
// result = TSynthSplatArgumentType(n)
result = TSynthHashSplatOrSplatArgumentType(n.(SynthHashSplatOrSplatArgumentNode).getMethodName())
or
not n instanceof LambdaSelfReferenceNode and
not mustHaveLambdaType(n, _) and
// not n instanceof SynthHashSplatArgumentNode and
// not n instanceof SynthSplatArgumentNode and
not n instanceof SynthHashSplatOrSplatArgumentNode and
result = TUnknownDataFlowType()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ SummaryComponent interpretComponentSpecific(AccessPathToken c) {
or
arg = "hash-splat" and
ppos.isHashSplat()
or
arg = "splat" and
ppos.isSynthSplat()
)
or
result = interpretElementArg(c.getAnArgument("Element"))
Expand Down Expand Up @@ -229,6 +232,9 @@ string getParameterPosition(ParameterPosition pos) {
or
pos.isHashSplat() and
result = "hash-splat"
or
pos.isSynthSplat() and
result = "splat"
}

/** Gets the textual representation of an argument position in the format used for flow summaries. */
Expand Down
8 changes: 3 additions & 5 deletions ruby/ql/lib/codeql/ruby/frameworks/core/Array.qll
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,9 @@ module Array {
override MethodCall getACallSimple() { result = getAStaticArrayCall("[]") }

override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
exists(ArrayIndex i |
input = "Argument[" + i + "]" and
output = "ReturnValue.Element[" + i + "]" and
preservesValue = true
)
input = "Argument[splat].WithElement[any]" and
output = "ReturnValue" and
preservesValue = true
}
}

Expand Down
Loading

0 comments on commit 5ae4441

Please sign in to comment.