mirror of
https://github.com/github/codeql.git
synced 2026-04-21 06:55:31 +02:00
Merge pull request #200 from github/hvitved/dataflow/call-sensitivity
Data flow: Call-sensitive resolution of lambda/block calls
This commit is contained in:
2
codeql
2
codeql
Submodule codeql updated: 2a7ceb2e19...f73960da8f
@@ -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. */
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() }
|
||||
|
||||
@@ -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" : |
|
||||
@@ -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()
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user