Merge pull request #200 from github/hvitved/dataflow/call-sensitivity

Data flow: Call-sensitive resolution of lambda/block calls
This commit is contained in:
Tom Hvitved
2021-06-04 16:25:13 +02:00
committed by GitHub
8 changed files with 375 additions and 113 deletions

2
codeql

Submodule codeql updated: 2a7ceb2e19...f73960da8f

View File

@@ -249,7 +249,7 @@ module ExprNodes {
class CallCfgNode extends ExprCfgNode {
override CallExprChildMapping e;
final override Call getExpr() { result = ExprCfgNode.super.getExpr() }
override Call getExpr() { result = super.getExpr() }
/** Gets the `n`th argument of this call. */
final ExprCfgNode getArgument(int n) { e.hasCfgChild(e.getArgument(n), this, result) }
@@ -270,7 +270,9 @@ module ExprNodes {
/** A control-flow node that wraps a `MethodCall` AST expression. */
class MethodCallCfgNode extends CallCfgNode {
MethodCallCfgNode() { this.getExpr() instanceof MethodCall }
MethodCallCfgNode() { super.getExpr() instanceof MethodCall }
final override MethodCall getExpr() { result = super.getExpr() }
}
/** A control-flow node that wraps a `CaseExpr` AST expression. */

View File

@@ -282,7 +282,10 @@ private DataFlow::LocalSourceNode trackModule(Module tp) {
}
/** Gets a viable run-time target for the call `call`. */
DataFlowCallable viableCallable(DataFlowCall call) { result = call.getTarget() }
DataFlowCallable viableCallable(DataFlowCall call) {
result = call.getTarget() and
not call.getExpr() instanceof YieldCall // handled by `lambdaCreation`/`lambdaCall`
}
/**
* Holds if the set of viable implementations that can be called by `call`

View File

@@ -532,7 +532,9 @@ private module Stage1 {
}
/**
* Holds if an output from `call` is reached in the flow covered by `revFlow`.
* Holds if an output from `call` is reached in the flow covered by `revFlow`
* and data might flow through the target callable resulting in reverse flow
* reaching an argument of `call`.
*/
pragma[nomagic]
private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) {
@@ -605,6 +607,15 @@ private module Stage1 {
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNode arg, boolean toReturn |
revFlow(arg, toReturn, config) and
revFlowInToReturn(call, arg, config) and
revFlowIsReturned(call, toReturn, config)
)
}
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
nodes = count(Node node | fwdFlow(node, config)) and
@@ -827,6 +838,16 @@ private module Stage2 {
PrevStage::revFlow(node, _, _, apa, config)
}
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _,
pragma[only_bind_into](config))
}
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -893,13 +914,11 @@ private module Stage2 {
)
or
// flow out of a callable
exists(DataFlowCall call |
fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
or
exists(Ap argAp0 |
fwdFlowOutFromArg(call, node, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
@@ -950,17 +969,14 @@ private module Stage2 {
)
}
/**
* Holds if flow may exit from `call` at `out` with access path `ap`. The
* inner call context is `innercc`, but `ccOut` is just the call context
* based on the return step. In the case of through-flow `ccOut` is discarded
* and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
*/
pragma[nomagic]
private predicate fwdFlowOut(
DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutNotFromArg(
Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
exists(
DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = getNodeEnclosingCallable(ret) and
@@ -975,7 +991,13 @@ private module Stage2 {
private predicate fwdFlowOutFromArg(
DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
) {
fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
ccc.matchesCall(call)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
/**
@@ -1006,6 +1028,27 @@ private module Stage2 {
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -1071,14 +1114,12 @@ private module Stage2 {
)
or
// flow into a callable
exists(DataFlowCall call |
revFlowIn(call, node, toReturn, returnAp, ap, config) and
toReturn = false
or
exists(Ap returnAp0 |
revFlowInToReturn(call, node, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
revFlowInNotToReturn(node, returnAp, ap, config) and
toReturn = false
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -1126,12 +1167,10 @@ private module Stage2 {
}
pragma[nomagic]
private predicate revFlowIn(
DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) {
exists(ParamNode p, boolean allowsFieldFlow |
revFlow(p, toReturn, returnAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config)
revFlow(p, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1141,7 +1180,12 @@ private module Stage2 {
private predicate revFlowInToReturn(
DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config
) {
revFlowIn(call, arg, true, apSome(returnAp), ap, config)
exists(ParamNode p, boolean allowsFieldFlow |
revFlow(p, true, apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
/**
@@ -1211,6 +1255,15 @@ private module Stage2 {
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap |
revFlow(arg, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
}
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
@@ -1459,6 +1512,16 @@ private module Stage3 {
)
}
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _,
pragma[only_bind_into](config))
}
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -1532,13 +1595,11 @@ private module Stage3 {
)
or
// flow out of a callable
exists(DataFlowCall call |
fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
or
exists(Ap argAp0 |
fwdFlowOutFromArg(call, node, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
@@ -1589,17 +1650,14 @@ private module Stage3 {
)
}
/**
* Holds if flow may exit from `call` at `out` with access path `ap`. The
* inner call context is `innercc`, but `ccOut` is just the call context
* based on the return step. In the case of through-flow `ccOut` is discarded
* and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
*/
pragma[nomagic]
private predicate fwdFlowOut(
DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutNotFromArg(
Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
exists(
DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = getNodeEnclosingCallable(ret) and
@@ -1614,7 +1672,13 @@ private module Stage3 {
private predicate fwdFlowOutFromArg(
DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
) {
fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
ccc.matchesCall(call)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
/**
@@ -1645,6 +1709,27 @@ private module Stage3 {
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -1710,14 +1795,12 @@ private module Stage3 {
)
or
// flow into a callable
exists(DataFlowCall call |
revFlowIn(call, node, toReturn, returnAp, ap, config) and
toReturn = false
or
exists(Ap returnAp0 |
revFlowInToReturn(call, node, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
revFlowInNotToReturn(node, returnAp, ap, config) and
toReturn = false
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -1765,12 +1848,10 @@ private module Stage3 {
}
pragma[nomagic]
private predicate revFlowIn(
DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) {
exists(ParamNode p, boolean allowsFieldFlow |
revFlow(p, toReturn, returnAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config)
revFlow(p, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1780,7 +1861,12 @@ private module Stage3 {
private predicate revFlowInToReturn(
DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config
) {
revFlowIn(call, arg, true, apSome(returnAp), ap, config)
exists(ParamNode p, boolean allowsFieldFlow |
revFlow(p, true, apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
/**
@@ -1850,6 +1936,15 @@ private module Stage3 {
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap |
revFlow(arg, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
}
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
@@ -2120,8 +2215,6 @@ private module Stage4 {
bindingset[innercc, inner, call]
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
resolveReturn(innercc, inner, call)
or
innercc.(CallContextCall).matchesCall(call)
}
bindingset[node, cc, config]
@@ -2174,6 +2267,16 @@ private module Stage4 {
)
}
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _,
pragma[only_bind_into](config))
}
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -2247,13 +2350,11 @@ private module Stage4 {
)
or
// flow out of a callable
exists(DataFlowCall call |
fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
or
exists(Ap argAp0 |
fwdFlowOutFromArg(call, node, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
@@ -2304,17 +2405,14 @@ private module Stage4 {
)
}
/**
* Holds if flow may exit from `call` at `out` with access path `ap`. The
* inner call context is `innercc`, but `ccOut` is just the call context
* based on the return step. In the case of through-flow `ccOut` is discarded
* and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
*/
pragma[nomagic]
private predicate fwdFlowOut(
DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutNotFromArg(
Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
exists(
DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = getNodeEnclosingCallable(ret) and
@@ -2329,7 +2427,13 @@ private module Stage4 {
private predicate fwdFlowOutFromArg(
DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
) {
fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
ccc.matchesCall(call)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
/**
@@ -2360,6 +2464,27 @@ private module Stage4 {
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -2425,14 +2550,12 @@ private module Stage4 {
)
or
// flow into a callable
exists(DataFlowCall call |
revFlowIn(call, node, toReturn, returnAp, ap, config) and
toReturn = false
or
exists(Ap returnAp0 |
revFlowInToReturn(call, node, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
revFlowInNotToReturn(node, returnAp, ap, config) and
toReturn = false
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -2480,12 +2603,10 @@ private module Stage4 {
}
pragma[nomagic]
private predicate revFlowIn(
DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) {
exists(ParamNode p, boolean allowsFieldFlow |
revFlow(p, toReturn, returnAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config)
revFlow(p, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -2495,7 +2616,12 @@ private module Stage4 {
private predicate revFlowInToReturn(
DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config
) {
revFlowIn(call, arg, true, apSome(returnAp), ap, config)
exists(ParamNode p, boolean allowsFieldFlow |
revFlow(p, true, apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
/**
@@ -2565,6 +2691,15 @@ private module Stage4 {
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap |
revFlow(arg, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
}
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
nodes = count(Node node | fwdFlow(node, _, _, _, config)) and

View File

@@ -177,18 +177,17 @@ private module Cached {
cached
newtype TContent = TTodoContent() // stub
/** Holds if `n` should be hidden from path explanations. */
cached
predicate nodeIsHidden(Node n) {
exists(Ssa::Definition def | def = n.(SsaDefinitionNode).getDefinition() |
def instanceof Ssa::PhiNode
)
}
}
import Cached
/** Holds if `n` should be hidden from path explanations. */
predicate nodeIsHidden(Node n) {
exists(Ssa::Definition def | def = n.(SsaDefinitionNode).getDefinition() |
def instanceof Ssa::PhiNode
)
}
/** An SSA definition, viewed as a node in a data flow graph. */
class SsaDefinitionNode extends NodeImpl, TSsaDefinitionNode {
Ssa::Definition def;
@@ -308,7 +307,6 @@ import ParameterNodes
/** A data-flow node that represents a call argument. */
abstract class ArgumentNode extends Node {
/** Holds if this argument occurs at the given position in the given call. */
cached
abstract predicate argumentOf(DataFlowCall call, int pos);
/** Gets the call in which this node is an argument. */
@@ -420,7 +418,6 @@ import ReturnNodes
/** A data-flow node that represents the output of a call. */
abstract class OutNode extends Node {
/** Gets the underlying call, where this node is a corresponding output of kind `kind`. */
cached
abstract DataFlowCall getCall(ReturnKind kind);
}
@@ -556,13 +553,39 @@ class BarrierGuard extends Expr {
Node getAGuardedNode() { none() }
}
class LambdaCallKind = Unit;
newtype LambdaCallKind =
TYieldCallKind() or
TLambdaCallKind()
/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */
predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { none() }
predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) {
kind = TYieldCallKind() and
creation.asExpr().getExpr() = c.(Block)
or
kind = TLambdaCallKind() and
(
creation.asExpr().getExpr() = c.(Lambda)
or
creation.asExpr() =
any(CfgNodes::ExprNodes::MethodCallCfgNode mc |
c = mc.getBlock().getExpr() and
mc.getExpr().getMethodName() = "lambda"
)
)
}
/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() }
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
kind = TYieldCallKind() and
receiver.(BlockParameterNode).getMethod() = call.getExpr().(YieldCall).getEnclosingMethod()
or
kind = TLambdaCallKind() and
call =
any(CfgNodes::ExprNodes::MethodCallCfgNode mc |
receiver.asExpr() = mc.getReceiver() and
mc.getExpr().getMethodName() = "call"
)
}
/** Extra data-flow steps needed for lambda flow analysis. */
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }

View File

@@ -0,0 +1,35 @@
edges
| call_sensitivity.rb:7:13:7:13 | x : | call_sensitivity.rb:8:11:8:11 | x : |
| call_sensitivity.rb:8:11:8:11 | x : | call_sensitivity.rb:15:20:15:20 | x : |
| call_sensitivity.rb:15:9:15:15 | "taint" : | call_sensitivity.rb:7:13:7:13 | x : |
| call_sensitivity.rb:15:20:15:20 | x : | call_sensitivity.rb:15:28:15:28 | x |
| call_sensitivity.rb:17:27:17:27 | x : | call_sensitivity.rb:18:17:18:17 | x : |
| call_sensitivity.rb:17:27:17:27 | x : | call_sensitivity.rb:18:17:18:17 | x : |
| call_sensitivity.rb:18:17:18:17 | x : | call_sensitivity.rb:27:17:27:17 | x : |
| call_sensitivity.rb:18:17:18:17 | x : | call_sensitivity.rb:36:23:36:23 | x : |
| call_sensitivity.rb:27:17:27:17 | x : | call_sensitivity.rb:27:27:27:27 | x |
| call_sensitivity.rb:28:25:28:31 | "taint" : | call_sensitivity.rb:17:27:17:27 | x : |
| call_sensitivity.rb:36:23:36:23 | x : | call_sensitivity.rb:36:31:36:31 | x |
| call_sensitivity.rb:37:25:37:31 | "taint" : | call_sensitivity.rb:17:27:17:27 | x : |
nodes
| call_sensitivity.rb:5:6:5:12 | "taint" | semmle.label | "taint" |
| call_sensitivity.rb:7:13:7:13 | x : | semmle.label | x : |
| call_sensitivity.rb:8:11:8:11 | x : | semmle.label | x : |
| call_sensitivity.rb:15:9:15:15 | "taint" : | semmle.label | "taint" : |
| call_sensitivity.rb:15:20:15:20 | x : | semmle.label | x : |
| call_sensitivity.rb:15:28:15:28 | x | semmle.label | x |
| call_sensitivity.rb:17:27:17:27 | x : | semmle.label | x : |
| call_sensitivity.rb:17:27:17:27 | x : | semmle.label | x : |
| call_sensitivity.rb:18:17:18:17 | x : | semmle.label | x : |
| call_sensitivity.rb:18:17:18:17 | x : | semmle.label | x : |
| call_sensitivity.rb:27:17:27:17 | x : | semmle.label | x : |
| call_sensitivity.rb:27:27:27:27 | x | semmle.label | x |
| call_sensitivity.rb:28:25:28:31 | "taint" : | semmle.label | "taint" : |
| call_sensitivity.rb:36:23:36:23 | x : | semmle.label | x : |
| call_sensitivity.rb:36:31:36:31 | x | semmle.label | x |
| call_sensitivity.rb:37:25:37:31 | "taint" : | semmle.label | "taint" : |
#select
| call_sensitivity.rb:5:6:5:12 | "taint" | call_sensitivity.rb:5:6:5:12 | "taint" | call_sensitivity.rb:5:6:5:12 | "taint" | $@ | call_sensitivity.rb:5:6:5:12 | "taint" | "taint" |
| call_sensitivity.rb:15:28:15:28 | x | call_sensitivity.rb:15:9:15:15 | "taint" : | call_sensitivity.rb:15:28:15:28 | x | $@ | call_sensitivity.rb:15:9:15:15 | "taint" : | "taint" : |
| call_sensitivity.rb:27:27:27:27 | x | call_sensitivity.rb:28:25:28:31 | "taint" : | call_sensitivity.rb:27:27:27:27 | x | $@ | call_sensitivity.rb:28:25:28:31 | "taint" : | "taint" : |
| call_sensitivity.rb:36:31:36:31 | x | call_sensitivity.rb:37:25:37:31 | "taint" : | call_sensitivity.rb:36:31:36:31 | x | $@ | call_sensitivity.rb:37:25:37:31 | "taint" : | "taint" : |

View File

@@ -0,0 +1,26 @@
/**
* @kind path-problem
*/
import ruby
import codeql_ruby.DataFlow
import DataFlow::PathGraph
class Conf extends DataFlow::Configuration {
Conf() { this = "Conf" }
override predicate isSource(DataFlow::Node src) {
src.asExpr().getExpr().(StringLiteral).getValueText() = "taint"
}
override predicate isSink(DataFlow::Node sink) {
exists(MethodCall mc |
mc.getMethodName() = "sink" and
mc.getAnArgument() = sink.asExpr().getExpr()
)
}
}
from DataFlow::PathNode source, DataFlow::PathNode sink, Conf conf
where conf.hasFlowPath(source, sink)
select sink, source, sink, "$@", source, source.toString()

View File

@@ -0,0 +1,38 @@
def sink s
puts s
end
sink "taint"
def yielder x
yield x
end
yielder "no taint" { |x| sink x } # no flow
yielder "taint" { |x| puts x } # no flow
yielder "taint" { |x| sink x } # flow
def apply_lambda (lambda, x)
lambda.call(x)
end
my_lambda = -> (x) { sink x }
apply_lambda(my_lambda, "no taint") # no flow
my_lambda = -> (x) { puts x }
apply_lambda(my_lambda, "taint") # no flow
my_lambda = -> (x) { sink x }
apply_lambda(my_lambda, "taint") # flow
my_lambda = lambda { |x| sink x }
apply_lambda(my_lambda, "no taint") # no flow
my_lambda = lambda { |x| puts x }
apply_lambda(my_lambda, "taint") # no flow
my_lambda = lambda { |x| sink x }
apply_lambda(my_lambda, "taint") # flow