mirror of
https://github.com/github/codeql.git
synced 2026-05-01 03:35:13 +02:00
Data flow: Introduce ReturnKind
This commit is contained in:
@@ -153,6 +153,12 @@ private predicate localFlowStep(Node node1, Node node2, boolean preservesValue,
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnKind viableReturnKind(DataFlowCall call, ReturnPosition pos) {
|
||||
viableImpl(call) = pos.getCallable() and
|
||||
result = pos.getKind()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is reachable from a source in the given configuration
|
||||
* ignoring call contexts.
|
||||
@@ -193,20 +199,21 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
// flow into a callable
|
||||
exists(Node arg |
|
||||
nodeCandFwd1(arg, stored, config) and
|
||||
viableParamArg(node, arg)
|
||||
viableParamArg(_, node, arg)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
nodeCandFwd1(mid, stored, config) and
|
||||
parameterValueFlowsToUpdate(p, mid) and
|
||||
viableParamArg(p, node.(PostUpdateNode).getPreUpdateNode())
|
||||
viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
|
||||
nodeCandFwd1(ret, stored, config) and
|
||||
node = getAViableOutNode(ret.getPosition())
|
||||
kind = viableReturnKind(call, getReturnPosition(ret)) and
|
||||
node = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -268,21 +275,22 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
or
|
||||
// flow into a callable
|
||||
exists(Node param |
|
||||
viableParamArg(param, node) and
|
||||
viableParamArg(_, param, node) and
|
||||
nodeCand1(param, stored, config)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node) and
|
||||
viableParamArg(p, mid.getPreUpdateNode()) and
|
||||
viableParamArg(_, p, mid.getPreUpdateNode()) and
|
||||
nodeCand1(mid, stored, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(Node out |
|
||||
exists(DataFlowCall call, ReturnKind kind, OutNode out |
|
||||
nodeCand1(out, stored, config) and
|
||||
out = getAViableOutNode(node.(ReturnNode).getPosition())
|
||||
kind = viableReturnKind(call, getReturnPosition(node)) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -314,7 +322,12 @@ private predicate simpleParameterFlow(
|
||||
nodeCand1(node, false, config) and
|
||||
p = node and
|
||||
t = getErasedRepr(node.getType()) and
|
||||
not parameterValueFlowsThrough(p, _)
|
||||
exists(ReturnNode ret, CallContextCall cc |
|
||||
returnNodeGetEnclosingCallable(ret) = p.getEnclosingCallable() and
|
||||
cc = getAValidCallContextForParameter(p)
|
||||
|
|
||||
not parameterValueFlowsThrough(p, ret.getKind(), cc)
|
||||
)
|
||||
or
|
||||
nodeCand1(node, false, unbind(config)) and
|
||||
exists(Node mid |
|
||||
@@ -341,7 +354,7 @@ private predicate simpleParameterFlow(
|
||||
nodeCand1(node, false, config) and
|
||||
exists(Node arg |
|
||||
simpleParameterFlow(p, arg, t, config) and
|
||||
argumentValueFlowsThrough(arg, node) and
|
||||
argumentValueFlowsThrough(arg, node, _) and
|
||||
compatibleTypes(t, node.getType())
|
||||
)
|
||||
or
|
||||
@@ -353,21 +366,31 @@ private predicate simpleParameterFlow(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate simpleArgumentFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) |
|
||||
kind = ret.getKind() and
|
||||
viableParamArg(call, p, arg)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `arg` through the `call` taking simple call
|
||||
* contexts into consideration and that this is part of a path from a source
|
||||
* to a sink. This is restricted to paths through the `call` that does not
|
||||
* Holds if data can flow from `arg` through a call to `out`, taking simple
|
||||
* call contexts into consideration, and that this is part of a path from a
|
||||
* source to a sink. This is restricted to paths through calla that do not
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, Node out, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode param, ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
nodeCand1(out, false, unbind(config)) and
|
||||
viableParamArgOut(param, arg, ret.getPosition(), out) and
|
||||
simpleParameterFlow(param, ret, t, config)
|
||||
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -379,7 +402,7 @@ private predicate flowThroughCallableCand1(
|
||||
) {
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
|
||||
or
|
||||
argumentValueFlowsThrough(node1, node2) and preservesValue = true
|
||||
argumentValueFlowsThrough(node1, node2, _) and preservesValue = true
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -405,11 +428,15 @@ private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration c
|
||||
// flow out of an argument
|
||||
exists(ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node1) and
|
||||
viableParamArg(p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
node2 = getAViableOutNode(node1.(ReturnNode).getPosition())
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
kind = viableReturnKind(call, getReturnPosition(node1))
|
||||
|
|
||||
node2 = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -418,7 +445,7 @@ private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration c
|
||||
* path from a source to a sink.
|
||||
*/
|
||||
private predicate flowIntoCallableCand1(Node node1, Node node2, Configuration config) {
|
||||
viableParamArg(node2, node1) and
|
||||
viableParamArg(_, node2, node1) and
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config)
|
||||
}
|
||||
@@ -1438,7 +1465,9 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
flowOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowThroughCallable(mid, node, ap, cc)
|
||||
flowThroughCallable(mid, node, cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
or
|
||||
valueFlowThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
@@ -1458,30 +1487,34 @@ private predicate contentStoreStep(
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to a return at position `pos` in the
|
||||
* context `innercc`, and the path did not flow through a parameter.
|
||||
*/
|
||||
private predicate flowOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
|
||||
pos.getAReturnNode() = mid.getNode() and
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
innercc = mid.getCallContext() and
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowOutOfCallable1(
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
flowOutOfCallable0(mid, pos, innercc) and
|
||||
c = pos.getCallable() and
|
||||
kind = pos.getKind() and
|
||||
resolveReturn(innercc, c, call)
|
||||
|
|
||||
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to `out`. The last step of this path
|
||||
* is a return from a callable and is recorded by `cc`, if needed.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, DataFlowCall call, CallContext innercc |
|
||||
flowOutOfCallable0(mid, pos, innercc) and
|
||||
out = getAViableOutNode(pos) and
|
||||
c = pos.getCallable() and
|
||||
call = out.getCall() and
|
||||
resolveReturn(innercc, c, call)
|
||||
|
|
||||
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
|
||||
exists(ReturnKind kind, DataFlowCall call | flowOutOfCallable1(mid, call, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1560,20 +1593,35 @@ private predicate flowIntoCallable(
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if data may flow from `p` to a return at position `pos`. */
|
||||
/** Holds if data may flow from `p` to a return of kind `kind`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnPosition pos, AccessPath ap, CallContextCall cc, Configuration config
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid |
|
||||
mid.getNode() = pos.getAReturnNode() and
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
config = mid.getConfiguration() and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
|
|
||||
cc = TSomeCall(p, true)
|
||||
or
|
||||
exists(int i | cc = TSpecificCall(_, i, true) | p.isParameterOf(pos.getCallable(), i))
|
||||
exists(int i | cc = TSpecificCall(_, i, true) |
|
||||
p.isParameterOf(returnNodeGetEnclosingCallable(ret), i)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, kind, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc) and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1582,11 +1630,24 @@ private predicate paramFlowsThrough(
|
||||
* The context `cc` is restored to its value prior to entering the callable.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowThroughCallable(PathNodeMid mid, OutNode out, AccessPath ap, CallContext cc) {
|
||||
exists(ParameterNode p, ReturnPosition pos, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, out.getCall()) and
|
||||
paramFlowsThrough(p, pos, ap, innercc, unbind(mid.getConfiguration())) and
|
||||
out = getAViableOutNode(pos)
|
||||
private predicate flowThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind | flowThroughCallable0(call, mid, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate valueFlowThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc | flowIntoCallable(mid, p, cc, innercc, call) |
|
||||
parameterValueFlowsThrough(p, kind, innercc)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlowThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnKind kind | valueFlowThroughCallable0(out.getCall(), mid, kind, cc) |
|
||||
out = getAnOutNode(_, kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -153,6 +153,12 @@ private predicate localFlowStep(Node node1, Node node2, boolean preservesValue,
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnKind viableReturnKind(DataFlowCall call, ReturnPosition pos) {
|
||||
viableImpl(call) = pos.getCallable() and
|
||||
result = pos.getKind()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is reachable from a source in the given configuration
|
||||
* ignoring call contexts.
|
||||
@@ -193,20 +199,21 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
// flow into a callable
|
||||
exists(Node arg |
|
||||
nodeCandFwd1(arg, stored, config) and
|
||||
viableParamArg(node, arg)
|
||||
viableParamArg(_, node, arg)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
nodeCandFwd1(mid, stored, config) and
|
||||
parameterValueFlowsToUpdate(p, mid) and
|
||||
viableParamArg(p, node.(PostUpdateNode).getPreUpdateNode())
|
||||
viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
|
||||
nodeCandFwd1(ret, stored, config) and
|
||||
node = getAViableOutNode(ret.getPosition())
|
||||
kind = viableReturnKind(call, getReturnPosition(ret)) and
|
||||
node = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -268,21 +275,22 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
or
|
||||
// flow into a callable
|
||||
exists(Node param |
|
||||
viableParamArg(param, node) and
|
||||
viableParamArg(_, param, node) and
|
||||
nodeCand1(param, stored, config)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node) and
|
||||
viableParamArg(p, mid.getPreUpdateNode()) and
|
||||
viableParamArg(_, p, mid.getPreUpdateNode()) and
|
||||
nodeCand1(mid, stored, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(Node out |
|
||||
exists(DataFlowCall call, ReturnKind kind, OutNode out |
|
||||
nodeCand1(out, stored, config) and
|
||||
out = getAViableOutNode(node.(ReturnNode).getPosition())
|
||||
kind = viableReturnKind(call, getReturnPosition(node)) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -314,7 +322,12 @@ private predicate simpleParameterFlow(
|
||||
nodeCand1(node, false, config) and
|
||||
p = node and
|
||||
t = getErasedRepr(node.getType()) and
|
||||
not parameterValueFlowsThrough(p, _)
|
||||
exists(ReturnNode ret, CallContextCall cc |
|
||||
returnNodeGetEnclosingCallable(ret) = p.getEnclosingCallable() and
|
||||
cc = getAValidCallContextForParameter(p)
|
||||
|
|
||||
not parameterValueFlowsThrough(p, ret.getKind(), cc)
|
||||
)
|
||||
or
|
||||
nodeCand1(node, false, unbind(config)) and
|
||||
exists(Node mid |
|
||||
@@ -341,7 +354,7 @@ private predicate simpleParameterFlow(
|
||||
nodeCand1(node, false, config) and
|
||||
exists(Node arg |
|
||||
simpleParameterFlow(p, arg, t, config) and
|
||||
argumentValueFlowsThrough(arg, node) and
|
||||
argumentValueFlowsThrough(arg, node, _) and
|
||||
compatibleTypes(t, node.getType())
|
||||
)
|
||||
or
|
||||
@@ -353,21 +366,31 @@ private predicate simpleParameterFlow(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate simpleArgumentFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) |
|
||||
kind = ret.getKind() and
|
||||
viableParamArg(call, p, arg)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `arg` through the `call` taking simple call
|
||||
* contexts into consideration and that this is part of a path from a source
|
||||
* to a sink. This is restricted to paths through the `call` that does not
|
||||
* Holds if data can flow from `arg` through a call to `out`, taking simple
|
||||
* call contexts into consideration, and that this is part of a path from a
|
||||
* source to a sink. This is restricted to paths through calla that do not
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, Node out, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode param, ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
nodeCand1(out, false, unbind(config)) and
|
||||
viableParamArgOut(param, arg, ret.getPosition(), out) and
|
||||
simpleParameterFlow(param, ret, t, config)
|
||||
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -379,7 +402,7 @@ private predicate flowThroughCallableCand1(
|
||||
) {
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
|
||||
or
|
||||
argumentValueFlowsThrough(node1, node2) and preservesValue = true
|
||||
argumentValueFlowsThrough(node1, node2, _) and preservesValue = true
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -405,11 +428,15 @@ private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration c
|
||||
// flow out of an argument
|
||||
exists(ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node1) and
|
||||
viableParamArg(p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
node2 = getAViableOutNode(node1.(ReturnNode).getPosition())
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
kind = viableReturnKind(call, getReturnPosition(node1))
|
||||
|
|
||||
node2 = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -418,7 +445,7 @@ private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration c
|
||||
* path from a source to a sink.
|
||||
*/
|
||||
private predicate flowIntoCallableCand1(Node node1, Node node2, Configuration config) {
|
||||
viableParamArg(node2, node1) and
|
||||
viableParamArg(_, node2, node1) and
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config)
|
||||
}
|
||||
@@ -1438,7 +1465,9 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
flowOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowThroughCallable(mid, node, ap, cc)
|
||||
flowThroughCallable(mid, node, cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
or
|
||||
valueFlowThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
@@ -1458,30 +1487,34 @@ private predicate contentStoreStep(
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to a return at position `pos` in the
|
||||
* context `innercc`, and the path did not flow through a parameter.
|
||||
*/
|
||||
private predicate flowOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
|
||||
pos.getAReturnNode() = mid.getNode() and
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
innercc = mid.getCallContext() and
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowOutOfCallable1(
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
flowOutOfCallable0(mid, pos, innercc) and
|
||||
c = pos.getCallable() and
|
||||
kind = pos.getKind() and
|
||||
resolveReturn(innercc, c, call)
|
||||
|
|
||||
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to `out`. The last step of this path
|
||||
* is a return from a callable and is recorded by `cc`, if needed.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, DataFlowCall call, CallContext innercc |
|
||||
flowOutOfCallable0(mid, pos, innercc) and
|
||||
out = getAViableOutNode(pos) and
|
||||
c = pos.getCallable() and
|
||||
call = out.getCall() and
|
||||
resolveReturn(innercc, c, call)
|
||||
|
|
||||
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
|
||||
exists(ReturnKind kind, DataFlowCall call | flowOutOfCallable1(mid, call, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1560,20 +1593,35 @@ private predicate flowIntoCallable(
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if data may flow from `p` to a return at position `pos`. */
|
||||
/** Holds if data may flow from `p` to a return of kind `kind`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnPosition pos, AccessPath ap, CallContextCall cc, Configuration config
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid |
|
||||
mid.getNode() = pos.getAReturnNode() and
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
config = mid.getConfiguration() and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
|
|
||||
cc = TSomeCall(p, true)
|
||||
or
|
||||
exists(int i | cc = TSpecificCall(_, i, true) | p.isParameterOf(pos.getCallable(), i))
|
||||
exists(int i | cc = TSpecificCall(_, i, true) |
|
||||
p.isParameterOf(returnNodeGetEnclosingCallable(ret), i)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, kind, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc) and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1582,11 +1630,24 @@ private predicate paramFlowsThrough(
|
||||
* The context `cc` is restored to its value prior to entering the callable.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowThroughCallable(PathNodeMid mid, OutNode out, AccessPath ap, CallContext cc) {
|
||||
exists(ParameterNode p, ReturnPosition pos, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, out.getCall()) and
|
||||
paramFlowsThrough(p, pos, ap, innercc, unbind(mid.getConfiguration())) and
|
||||
out = getAViableOutNode(pos)
|
||||
private predicate flowThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind | flowThroughCallable0(call, mid, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate valueFlowThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc | flowIntoCallable(mid, p, cc, innercc, call) |
|
||||
parameterValueFlowsThrough(p, kind, innercc)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlowThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnKind kind | valueFlowThroughCallable0(out.getCall(), mid, kind, cc) |
|
||||
out = getAnOutNode(_, kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -153,6 +153,12 @@ private predicate localFlowStep(Node node1, Node node2, boolean preservesValue,
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnKind viableReturnKind(DataFlowCall call, ReturnPosition pos) {
|
||||
viableImpl(call) = pos.getCallable() and
|
||||
result = pos.getKind()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is reachable from a source in the given configuration
|
||||
* ignoring call contexts.
|
||||
@@ -193,20 +199,21 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
// flow into a callable
|
||||
exists(Node arg |
|
||||
nodeCandFwd1(arg, stored, config) and
|
||||
viableParamArg(node, arg)
|
||||
viableParamArg(_, node, arg)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
nodeCandFwd1(mid, stored, config) and
|
||||
parameterValueFlowsToUpdate(p, mid) and
|
||||
viableParamArg(p, node.(PostUpdateNode).getPreUpdateNode())
|
||||
viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
|
||||
nodeCandFwd1(ret, stored, config) and
|
||||
node = getAViableOutNode(ret.getPosition())
|
||||
kind = viableReturnKind(call, getReturnPosition(ret)) and
|
||||
node = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -268,21 +275,22 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
or
|
||||
// flow into a callable
|
||||
exists(Node param |
|
||||
viableParamArg(param, node) and
|
||||
viableParamArg(_, param, node) and
|
||||
nodeCand1(param, stored, config)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node) and
|
||||
viableParamArg(p, mid.getPreUpdateNode()) and
|
||||
viableParamArg(_, p, mid.getPreUpdateNode()) and
|
||||
nodeCand1(mid, stored, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(Node out |
|
||||
exists(DataFlowCall call, ReturnKind kind, OutNode out |
|
||||
nodeCand1(out, stored, config) and
|
||||
out = getAViableOutNode(node.(ReturnNode).getPosition())
|
||||
kind = viableReturnKind(call, getReturnPosition(node)) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -314,7 +322,12 @@ private predicate simpleParameterFlow(
|
||||
nodeCand1(node, false, config) and
|
||||
p = node and
|
||||
t = getErasedRepr(node.getType()) and
|
||||
not parameterValueFlowsThrough(p, _)
|
||||
exists(ReturnNode ret, CallContextCall cc |
|
||||
returnNodeGetEnclosingCallable(ret) = p.getEnclosingCallable() and
|
||||
cc = getAValidCallContextForParameter(p)
|
||||
|
|
||||
not parameterValueFlowsThrough(p, ret.getKind(), cc)
|
||||
)
|
||||
or
|
||||
nodeCand1(node, false, unbind(config)) and
|
||||
exists(Node mid |
|
||||
@@ -341,7 +354,7 @@ private predicate simpleParameterFlow(
|
||||
nodeCand1(node, false, config) and
|
||||
exists(Node arg |
|
||||
simpleParameterFlow(p, arg, t, config) and
|
||||
argumentValueFlowsThrough(arg, node) and
|
||||
argumentValueFlowsThrough(arg, node, _) and
|
||||
compatibleTypes(t, node.getType())
|
||||
)
|
||||
or
|
||||
@@ -353,21 +366,31 @@ private predicate simpleParameterFlow(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate simpleArgumentFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) |
|
||||
kind = ret.getKind() and
|
||||
viableParamArg(call, p, arg)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `arg` through the `call` taking simple call
|
||||
* contexts into consideration and that this is part of a path from a source
|
||||
* to a sink. This is restricted to paths through the `call` that does not
|
||||
* Holds if data can flow from `arg` through a call to `out`, taking simple
|
||||
* call contexts into consideration, and that this is part of a path from a
|
||||
* source to a sink. This is restricted to paths through calla that do not
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, Node out, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode param, ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
nodeCand1(out, false, unbind(config)) and
|
||||
viableParamArgOut(param, arg, ret.getPosition(), out) and
|
||||
simpleParameterFlow(param, ret, t, config)
|
||||
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -379,7 +402,7 @@ private predicate flowThroughCallableCand1(
|
||||
) {
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
|
||||
or
|
||||
argumentValueFlowsThrough(node1, node2) and preservesValue = true
|
||||
argumentValueFlowsThrough(node1, node2, _) and preservesValue = true
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -405,11 +428,15 @@ private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration c
|
||||
// flow out of an argument
|
||||
exists(ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node1) and
|
||||
viableParamArg(p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
node2 = getAViableOutNode(node1.(ReturnNode).getPosition())
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
kind = viableReturnKind(call, getReturnPosition(node1))
|
||||
|
|
||||
node2 = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -418,7 +445,7 @@ private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration c
|
||||
* path from a source to a sink.
|
||||
*/
|
||||
private predicate flowIntoCallableCand1(Node node1, Node node2, Configuration config) {
|
||||
viableParamArg(node2, node1) and
|
||||
viableParamArg(_, node2, node1) and
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config)
|
||||
}
|
||||
@@ -1438,7 +1465,9 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
flowOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowThroughCallable(mid, node, ap, cc)
|
||||
flowThroughCallable(mid, node, cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
or
|
||||
valueFlowThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
@@ -1458,30 +1487,34 @@ private predicate contentStoreStep(
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to a return at position `pos` in the
|
||||
* context `innercc`, and the path did not flow through a parameter.
|
||||
*/
|
||||
private predicate flowOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
|
||||
pos.getAReturnNode() = mid.getNode() and
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
innercc = mid.getCallContext() and
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowOutOfCallable1(
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
flowOutOfCallable0(mid, pos, innercc) and
|
||||
c = pos.getCallable() and
|
||||
kind = pos.getKind() and
|
||||
resolveReturn(innercc, c, call)
|
||||
|
|
||||
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to `out`. The last step of this path
|
||||
* is a return from a callable and is recorded by `cc`, if needed.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, DataFlowCall call, CallContext innercc |
|
||||
flowOutOfCallable0(mid, pos, innercc) and
|
||||
out = getAViableOutNode(pos) and
|
||||
c = pos.getCallable() and
|
||||
call = out.getCall() and
|
||||
resolveReturn(innercc, c, call)
|
||||
|
|
||||
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
|
||||
exists(ReturnKind kind, DataFlowCall call | flowOutOfCallable1(mid, call, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1560,20 +1593,35 @@ private predicate flowIntoCallable(
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if data may flow from `p` to a return at position `pos`. */
|
||||
/** Holds if data may flow from `p` to a return of kind `kind`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnPosition pos, AccessPath ap, CallContextCall cc, Configuration config
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid |
|
||||
mid.getNode() = pos.getAReturnNode() and
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
config = mid.getConfiguration() and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
|
|
||||
cc = TSomeCall(p, true)
|
||||
or
|
||||
exists(int i | cc = TSpecificCall(_, i, true) | p.isParameterOf(pos.getCallable(), i))
|
||||
exists(int i | cc = TSpecificCall(_, i, true) |
|
||||
p.isParameterOf(returnNodeGetEnclosingCallable(ret), i)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, kind, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc) and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1582,11 +1630,24 @@ private predicate paramFlowsThrough(
|
||||
* The context `cc` is restored to its value prior to entering the callable.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowThroughCallable(PathNodeMid mid, OutNode out, AccessPath ap, CallContext cc) {
|
||||
exists(ParameterNode p, ReturnPosition pos, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, out.getCall()) and
|
||||
paramFlowsThrough(p, pos, ap, innercc, unbind(mid.getConfiguration())) and
|
||||
out = getAViableOutNode(pos)
|
||||
private predicate flowThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind | flowThroughCallable0(call, mid, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate valueFlowThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc | flowIntoCallable(mid, p, cc, innercc, call) |
|
||||
parameterValueFlowsThrough(p, kind, innercc)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlowThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnKind kind | valueFlowThroughCallable0(out.getCall(), mid, kind, cc) |
|
||||
out = getAnOutNode(_, kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -153,6 +153,12 @@ private predicate localFlowStep(Node node1, Node node2, boolean preservesValue,
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnKind viableReturnKind(DataFlowCall call, ReturnPosition pos) {
|
||||
viableImpl(call) = pos.getCallable() and
|
||||
result = pos.getKind()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is reachable from a source in the given configuration
|
||||
* ignoring call contexts.
|
||||
@@ -193,20 +199,21 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
// flow into a callable
|
||||
exists(Node arg |
|
||||
nodeCandFwd1(arg, stored, config) and
|
||||
viableParamArg(node, arg)
|
||||
viableParamArg(_, node, arg)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
nodeCandFwd1(mid, stored, config) and
|
||||
parameterValueFlowsToUpdate(p, mid) and
|
||||
viableParamArg(p, node.(PostUpdateNode).getPreUpdateNode())
|
||||
viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
|
||||
nodeCandFwd1(ret, stored, config) and
|
||||
node = getAViableOutNode(ret.getPosition())
|
||||
kind = viableReturnKind(call, getReturnPosition(ret)) and
|
||||
node = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -268,21 +275,22 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
or
|
||||
// flow into a callable
|
||||
exists(Node param |
|
||||
viableParamArg(param, node) and
|
||||
viableParamArg(_, param, node) and
|
||||
nodeCand1(param, stored, config)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node) and
|
||||
viableParamArg(p, mid.getPreUpdateNode()) and
|
||||
viableParamArg(_, p, mid.getPreUpdateNode()) and
|
||||
nodeCand1(mid, stored, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(Node out |
|
||||
exists(DataFlowCall call, ReturnKind kind, OutNode out |
|
||||
nodeCand1(out, stored, config) and
|
||||
out = getAViableOutNode(node.(ReturnNode).getPosition())
|
||||
kind = viableReturnKind(call, getReturnPosition(node)) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -314,7 +322,12 @@ private predicate simpleParameterFlow(
|
||||
nodeCand1(node, false, config) and
|
||||
p = node and
|
||||
t = getErasedRepr(node.getType()) and
|
||||
not parameterValueFlowsThrough(p, _)
|
||||
exists(ReturnNode ret, CallContextCall cc |
|
||||
returnNodeGetEnclosingCallable(ret) = p.getEnclosingCallable() and
|
||||
cc = getAValidCallContextForParameter(p)
|
||||
|
|
||||
not parameterValueFlowsThrough(p, ret.getKind(), cc)
|
||||
)
|
||||
or
|
||||
nodeCand1(node, false, unbind(config)) and
|
||||
exists(Node mid |
|
||||
@@ -341,7 +354,7 @@ private predicate simpleParameterFlow(
|
||||
nodeCand1(node, false, config) and
|
||||
exists(Node arg |
|
||||
simpleParameterFlow(p, arg, t, config) and
|
||||
argumentValueFlowsThrough(arg, node) and
|
||||
argumentValueFlowsThrough(arg, node, _) and
|
||||
compatibleTypes(t, node.getType())
|
||||
)
|
||||
or
|
||||
@@ -353,21 +366,31 @@ private predicate simpleParameterFlow(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate simpleArgumentFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) |
|
||||
kind = ret.getKind() and
|
||||
viableParamArg(call, p, arg)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `arg` through the `call` taking simple call
|
||||
* contexts into consideration and that this is part of a path from a source
|
||||
* to a sink. This is restricted to paths through the `call` that does not
|
||||
* Holds if data can flow from `arg` through a call to `out`, taking simple
|
||||
* call contexts into consideration, and that this is part of a path from a
|
||||
* source to a sink. This is restricted to paths through calla that do not
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, Node out, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode param, ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
nodeCand1(out, false, unbind(config)) and
|
||||
viableParamArgOut(param, arg, ret.getPosition(), out) and
|
||||
simpleParameterFlow(param, ret, t, config)
|
||||
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -379,7 +402,7 @@ private predicate flowThroughCallableCand1(
|
||||
) {
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
|
||||
or
|
||||
argumentValueFlowsThrough(node1, node2) and preservesValue = true
|
||||
argumentValueFlowsThrough(node1, node2, _) and preservesValue = true
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -405,11 +428,15 @@ private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration c
|
||||
// flow out of an argument
|
||||
exists(ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node1) and
|
||||
viableParamArg(p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
node2 = getAViableOutNode(node1.(ReturnNode).getPosition())
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
kind = viableReturnKind(call, getReturnPosition(node1))
|
||||
|
|
||||
node2 = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -418,7 +445,7 @@ private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration c
|
||||
* path from a source to a sink.
|
||||
*/
|
||||
private predicate flowIntoCallableCand1(Node node1, Node node2, Configuration config) {
|
||||
viableParamArg(node2, node1) and
|
||||
viableParamArg(_, node2, node1) and
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config)
|
||||
}
|
||||
@@ -1438,7 +1465,9 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
flowOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowThroughCallable(mid, node, ap, cc)
|
||||
flowThroughCallable(mid, node, cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
or
|
||||
valueFlowThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
@@ -1458,30 +1487,34 @@ private predicate contentStoreStep(
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to a return at position `pos` in the
|
||||
* context `innercc`, and the path did not flow through a parameter.
|
||||
*/
|
||||
private predicate flowOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
|
||||
pos.getAReturnNode() = mid.getNode() and
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
innercc = mid.getCallContext() and
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowOutOfCallable1(
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
flowOutOfCallable0(mid, pos, innercc) and
|
||||
c = pos.getCallable() and
|
||||
kind = pos.getKind() and
|
||||
resolveReturn(innercc, c, call)
|
||||
|
|
||||
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to `out`. The last step of this path
|
||||
* is a return from a callable and is recorded by `cc`, if needed.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, DataFlowCall call, CallContext innercc |
|
||||
flowOutOfCallable0(mid, pos, innercc) and
|
||||
out = getAViableOutNode(pos) and
|
||||
c = pos.getCallable() and
|
||||
call = out.getCall() and
|
||||
resolveReturn(innercc, c, call)
|
||||
|
|
||||
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
|
||||
exists(ReturnKind kind, DataFlowCall call | flowOutOfCallable1(mid, call, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1560,20 +1593,35 @@ private predicate flowIntoCallable(
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if data may flow from `p` to a return at position `pos`. */
|
||||
/** Holds if data may flow from `p` to a return of kind `kind`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnPosition pos, AccessPath ap, CallContextCall cc, Configuration config
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid |
|
||||
mid.getNode() = pos.getAReturnNode() and
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
config = mid.getConfiguration() and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
|
|
||||
cc = TSomeCall(p, true)
|
||||
or
|
||||
exists(int i | cc = TSpecificCall(_, i, true) | p.isParameterOf(pos.getCallable(), i))
|
||||
exists(int i | cc = TSpecificCall(_, i, true) |
|
||||
p.isParameterOf(returnNodeGetEnclosingCallable(ret), i)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, kind, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc) and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1582,11 +1630,24 @@ private predicate paramFlowsThrough(
|
||||
* The context `cc` is restored to its value prior to entering the callable.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowThroughCallable(PathNodeMid mid, OutNode out, AccessPath ap, CallContext cc) {
|
||||
exists(ParameterNode p, ReturnPosition pos, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, out.getCall()) and
|
||||
paramFlowsThrough(p, pos, ap, innercc, unbind(mid.getConfiguration())) and
|
||||
out = getAViableOutNode(pos)
|
||||
private predicate flowThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind | flowThroughCallable0(call, mid, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate valueFlowThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc | flowIntoCallable(mid, p, cc, innercc, call) |
|
||||
parameterValueFlowsThrough(p, kind, innercc)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlowThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnKind kind | valueFlowThroughCallable0(out.getCall(), mid, kind, cc) |
|
||||
out = getAnOutNode(_, kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -13,77 +13,179 @@ private module ImplCommon {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` is a possible argument to `p` taking virtual dispatch into account.
|
||||
* Holds if `arg` is a possible argument to `p` in `call`, taking virtual
|
||||
* dispatch into account.
|
||||
*/
|
||||
cached
|
||||
predicate viableParamArg(ParameterNode p, ArgumentNode arg) {
|
||||
exists(int i, DataFlowCall call |
|
||||
predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
|
||||
exists(int i |
|
||||
viableParam(call, i, p) and
|
||||
arg.argumentOf(call, i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `p` is the `i`th parameter of a viable dispatch target of a call
|
||||
* that can return values at position `pos` out to the node `out`. The instance
|
||||
* parameter is considered to have index `-1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate viableParamOut(int i, ParameterNode p, OutNode out, ReturnPosition ret) {
|
||||
out = getAViableOutNode(ret) and
|
||||
p.isParameterOf(ret.getCallable(), i)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` is a possible argument to `p` taking virtual dispatch into account,
|
||||
* and the callable can return values at position `pos` out to the node `out`.
|
||||
*/
|
||||
cached
|
||||
predicate viableParamArgOut(ParameterNode p, ArgumentNode arg, ReturnPosition ret, OutNode out) {
|
||||
exists(int i |
|
||||
viableParamOut(i, p, out, ret) and
|
||||
arg.argumentOf(out.getCall(), i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `p` can flow to `node` in the same callable using only
|
||||
* value-preserving steps.
|
||||
* value-preserving steps, not taking call contexts into account.
|
||||
*/
|
||||
private predicate parameterValueFlow(ParameterNode p, Node node) {
|
||||
private predicate parameterValueFlowNoCtx(ParameterNode p, Node node) {
|
||||
p = node
|
||||
or
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid) and
|
||||
parameterValueFlowNoCtx(p, mid) and
|
||||
localFlowStep(mid, node) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(Node arg |
|
||||
parameterValueFlow(p, arg) and
|
||||
argumentValueFlowsThrough(arg, node) and
|
||||
parameterValueFlowNoCtx(p, arg) and
|
||||
argumentValueFlowsThroughNoCtx(arg, node) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `p` can flow to a return node with position `pos` in the same
|
||||
* callable using only value-preserving steps.
|
||||
* Holds if `p` can flow to a return node of kind `kind` in the same
|
||||
* callable using only value-preserving steps, not taking call contexts
|
||||
* into account.
|
||||
*/
|
||||
cached
|
||||
predicate parameterValueFlowsThrough(ParameterNode p, ReturnPosition pos) {
|
||||
parameterValueFlow(p, pos.getAReturnNode())
|
||||
private predicate parameterValueFlowsThroughNoCtx(ParameterNode p, ReturnKind kind) {
|
||||
parameterValueFlowNoCtx(p, getAReturnNodeOfKind(kind))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate argumentValueFlowsThroughNoCtx0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind
|
||||
) {
|
||||
exists(ParameterNode param | viableParamArg(call, param, arg) |
|
||||
parameterValueFlowsThroughNoCtx(param, kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` flows through a call to `out` using only value-preserving steps.
|
||||
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
|
||||
* not taking call contexts into account.
|
||||
*/
|
||||
private predicate argumentValueFlowsThroughNoCtx(ArgumentNode arg, OutNode out) {
|
||||
exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughNoCtx0(call, arg, kind) |
|
||||
out = getAnOutNode(call, kind) and
|
||||
compatibleTypes(arg.getType(), out.getType())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` is the `i`th argument of `call` inside the callable
|
||||
* `enclosing`, and `arg` may flow through `call`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate argumentOf(
|
||||
DataFlowCall call, int i, ArgumentNode arg, DataFlowCallable enclosing
|
||||
) {
|
||||
arg.argumentOf(call, i) and
|
||||
argumentValueFlowsThroughNoCtx0(call, arg, _) and
|
||||
enclosing = arg.getEnclosingCallable()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate viableParamArg0(int i, ArgumentNode arg, CallContext outercc, DataFlowCall call) {
|
||||
exists(DataFlowCallable c | argumentOf(call, i, arg, c) |
|
||||
outercc = TAnyCallContext()
|
||||
or
|
||||
exists(ParameterNode p | outercc = TSomeCall(p, _) | c = p.getEnclosingCallable())
|
||||
or
|
||||
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) | c = viableCallable(other))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate viableParamArg1(
|
||||
ParameterNode p, DataFlowCallable callable, int i, ArgumentNode arg, CallContext outercc,
|
||||
DataFlowCall call
|
||||
) {
|
||||
viableParamArg0(i, arg, outercc, call) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
p.isParameterOf(callable, any(int j | j <= i and j >= i))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` is a possible argument to `p`, in the call `call`, and
|
||||
* `arg` may flow through `call`. The possible contexts before and after
|
||||
* entering the callable are `outercc` and `innercc`, respectively.
|
||||
*/
|
||||
private predicate viableParamArg(
|
||||
DataFlowCall call, ParameterNode p, ArgumentNode arg, CallContext outercc,
|
||||
CallContextCall innercc
|
||||
) {
|
||||
exists(int i, DataFlowCallable callable | viableParamArg1(p, callable, i, arg, outercc, call) |
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
then innercc = TSpecificCall(call, i, true)
|
||||
else innercc = TSomeCall(p, true)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
CallContextCall getAValidCallContextForParameter(ParameterNode p) {
|
||||
result = TSomeCall(p, _)
|
||||
or
|
||||
exists(DataFlowCall call, int i | result = TSpecificCall(call, i, _) |
|
||||
p.isParameterOf(_, i) and p.getEnclosingCallable() = viableCallable(call)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `p` can flow to `node` in the same callable using only
|
||||
* value-preserving steps, in call context `cc`.
|
||||
*/
|
||||
private predicate parameterValueFlow(ParameterNode p, Node node, CallContextCall cc) {
|
||||
p = node and
|
||||
parameterValueFlowsThroughNoCtx(p, _) and
|
||||
cc = getAValidCallContextForParameter(p)
|
||||
or
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid, cc) and
|
||||
localFlowStep(mid, node) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(Node arg |
|
||||
parameterValueFlow(p, arg, cc) and
|
||||
argumentValueFlowsThrough(arg, node, cc) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
)
|
||||
}
|
||||
|
||||
private ReturnNode getAReturnNodeOfKind(ReturnKind kind) { result.getKind() = kind }
|
||||
|
||||
/**
|
||||
* Holds if `p` can flow to a return node of kind `kind` in the same
|
||||
* callable using only value-preserving steps, in call context `cc`.
|
||||
*/
|
||||
cached
|
||||
predicate argumentValueFlowsThrough(ArgumentNode arg, OutNode out) {
|
||||
exists(ParameterNode param, ReturnPosition ret |
|
||||
viableParamArgOut(param, arg, ret, out) and
|
||||
parameterValueFlowsThrough(param, ret) and
|
||||
predicate parameterValueFlowsThrough(ParameterNode p, ReturnKind kind, CallContextCall cc) {
|
||||
parameterValueFlow(p, getAReturnNodeOfKind(kind), cc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate argumentValueFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode param, CallContext innercc |
|
||||
viableParamArg(call, param, arg, cc, innercc)
|
||||
|
|
||||
parameterValueFlowsThrough(param, kind, innercc)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
|
||||
* in call context cc.
|
||||
*/
|
||||
cached
|
||||
predicate argumentValueFlowsThrough(ArgumentNode arg, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, cc) |
|
||||
out = getAnOutNode(call, kind) and
|
||||
compatibleTypes(arg.getType(), out.getType())
|
||||
)
|
||||
}
|
||||
@@ -94,7 +196,7 @@ private module ImplCommon {
|
||||
*/
|
||||
cached
|
||||
predicate parameterValueFlowsToUpdate(ParameterNode p, PostUpdateNode n) {
|
||||
parameterValueFlow(p, n.getPreUpdateNode())
|
||||
parameterValueFlowNoCtx(p, n.getPreUpdateNode())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,7 +205,7 @@ private module ImplCommon {
|
||||
*/
|
||||
private predicate localValueStep(Node node1, Node node2) {
|
||||
localFlowStep(node1, node2) or
|
||||
argumentValueFlowsThrough(node1, node2)
|
||||
argumentValueFlowsThrough(node1, node2, _)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -143,9 +245,9 @@ private module ImplCommon {
|
||||
pragma[nomagic]
|
||||
private predicate setterInParam(ParameterNode p1, Content f, ParameterNode p2) {
|
||||
exists(Node n1, PostUpdateNode n2 |
|
||||
parameterValueFlow(p1, n1) and
|
||||
parameterValueFlowNoCtx(p1, n1) and
|
||||
storeViaSideEffect(n1, f, n2) and
|
||||
parameterValueFlow(p2, n2.getPreUpdateNode()) and
|
||||
parameterValueFlowNoCtx(p2, n2.getPreUpdateNode()) and
|
||||
p1 != p2
|
||||
)
|
||||
}
|
||||
@@ -160,21 +262,35 @@ private module ImplCommon {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate storeReturn0(DataFlowCall call, ReturnKind kind, ArgumentNode arg, Content f) {
|
||||
exists(ParameterNode p |
|
||||
viableParamArg(call, p, arg) and
|
||||
setterReturn(p, f, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate storeReturn(Node node1, Content f, Node node2) {
|
||||
exists(ParameterNode p, ArgumentNode arg, ReturnPosition ret |
|
||||
arg = node1 and
|
||||
viableParamArgOut(p, arg, ret, node2) and
|
||||
setterReturn(p, f, ret) and
|
||||
exists(DataFlowCall call, ReturnKind kind | storeReturn0(call, kind, node1, f) |
|
||||
node2 = getAnOutNode(call, kind) and
|
||||
compatibleTypes(node1.getTypeBound(), f.getType()) and
|
||||
compatibleTypes(node2.getTypeBound(), f.getContainerType())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate setterReturn(ParameterNode p, Content f, ReturnPosition ret) {
|
||||
private predicate setterReturn(ParameterNode p, Content f, ReturnKind kind) {
|
||||
exists(Node n1, Node n2 |
|
||||
parameterValueFlow(p, n1) and
|
||||
parameterValueFlowNoCtx(p, n1) and
|
||||
store(n1, f, n2) and
|
||||
localValueStep*(n2, ret.getAReturnNode())
|
||||
localValueStep*(n2, getAReturnNodeOfKind(kind))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate read0(DataFlowCall call, ReturnKind kind, ArgumentNode arg, Content f) {
|
||||
exists(ParameterNode p |
|
||||
viableParamArg(call, p, arg) and
|
||||
getter(p, f, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -186,20 +302,18 @@ private module ImplCommon {
|
||||
predicate read(Node node1, Content f, Node node2) {
|
||||
readStep(node1, f, node2) and storeStep(_, f, _)
|
||||
or
|
||||
exists(ParameterNode p, ArgumentNode arg, ReturnPosition ret |
|
||||
arg = node1 and
|
||||
viableParamArgOut(p, arg, ret, node2) and
|
||||
getter(p, f, ret) and
|
||||
exists(DataFlowCall call, ReturnKind kind | read0(call, kind, node1, f) |
|
||||
node2 = getAnOutNode(call, kind) and
|
||||
compatibleTypes(node1.getTypeBound(), f.getContainerType()) and
|
||||
compatibleTypes(node2.getTypeBound(), f.getType())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate getter(ParameterNode p, Content f, ReturnPosition ret) {
|
||||
private predicate getter(ParameterNode p, Content f, ReturnKind kind) {
|
||||
exists(Node n1, Node n2 |
|
||||
parameterValueFlow(p, n1) and
|
||||
parameterValueFlowNoCtx(p, n1) and
|
||||
read(n1, f, n2) and
|
||||
localValueStep*(n2, ret.getAReturnNode())
|
||||
localValueStep*(n2, getAReturnNodeOfKind(kind))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -234,9 +348,19 @@ private module ImplCommon {
|
||||
} or
|
||||
TSomeCall(ParameterNode p, boolean emptyAp) { emptyAp = true or emptyAp = false } or
|
||||
TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) }
|
||||
|
||||
cached
|
||||
newtype TReturnPosition =
|
||||
TReturnPosition0(DataFlowCallable c, ReturnKind kind) { returnPosition(_, c, kind) }
|
||||
}
|
||||
import ImplCommon
|
||||
|
||||
pragma[noinline]
|
||||
private predicate returnPosition(ReturnNode ret, DataFlowCallable c, ReturnKind kind) {
|
||||
c = returnNodeGetEnclosingCallable(ret) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
/**
|
||||
* A call context to restrict the targets of virtual dispatch and match the
|
||||
* call sites of flow into a method with flow out of a method.
|
||||
@@ -281,6 +405,36 @@ class CallContextReturn extends CallContext, TReturn {
|
||||
}
|
||||
}
|
||||
|
||||
/** A callable tagged with a relevant return kind. */
|
||||
class ReturnPosition extends TReturnPosition0 {
|
||||
private DataFlowCallable c;
|
||||
|
||||
private ReturnKind kind;
|
||||
|
||||
ReturnPosition() { this = TReturnPosition0(c, kind) }
|
||||
|
||||
/** Gets the callable. */
|
||||
DataFlowCallable getCallable() { result = c }
|
||||
|
||||
/** Gets the return kind. */
|
||||
ReturnKind getKind() { result = kind }
|
||||
|
||||
/** Gets a textual representation of this return position. */
|
||||
string toString() { result = "[" + kind + "] " + c }
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
DataFlowCallable returnNodeGetEnclosingCallable(ReturnNode ret) {
|
||||
result = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
ReturnPosition getReturnPosition(ReturnNode ret) {
|
||||
exists(DataFlowCallable c, ReturnKind k | returnPosition(ret, c, k) |
|
||||
result = TReturnPosition0(c, k)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[cc, callable]
|
||||
predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
|
||||
cc instanceof CallContextAny and callable = viableCallable(call)
|
||||
|
||||
@@ -50,29 +50,26 @@ class ArgumentNode extends Node {
|
||||
DataFlowCall getCall() { this.argumentOf(result, _) }
|
||||
}
|
||||
|
||||
private newtype TReturnKind = TNormalReturnKind()
|
||||
|
||||
/**
|
||||
* A return position. A return position describes how a value can return
|
||||
* from a given callable. For C++, this is simply a function return.
|
||||
* A return kind. A return kind describes how a value can be returned
|
||||
* from a callable. For C++, this is simply a function return.
|
||||
*/
|
||||
class ReturnPosition extends Function {
|
||||
ReturnPosition() { exists(ReturnNode ret | ret.getEnclosingCallable() = this) }
|
||||
|
||||
/** Gets the callable that a value can be returned from. */
|
||||
Function getCallable() { result = this }
|
||||
|
||||
/** Gets a return node that can return a value at this position. */
|
||||
ReturnNode getAReturnNode() { result.getEnclosingCallable() = this }
|
||||
class ReturnKind extends TReturnKind {
|
||||
/** Gets a textual representation of this return kind. */
|
||||
string toString() { result = "return" }
|
||||
}
|
||||
|
||||
/** A data flow node that occurs as the result of a `ReturnStmt`. */
|
||||
class ReturnNode extends ExprNode {
|
||||
ReturnNode() { exists(ReturnStmt ret | this.getExpr() = ret.getExpr()) }
|
||||
|
||||
/** Gets the position at which this value is returned. */
|
||||
ReturnPosition getPosition() { this = result.getAReturnNode() }
|
||||
/** Gets the kind of this returned value. */
|
||||
ReturnKind getKind() { result = TNormalReturnKind() }
|
||||
}
|
||||
|
||||
/** A data flow node that represents a call. */
|
||||
/** A data flow node that represents the output of a call. */
|
||||
class OutNode extends ExprNode {
|
||||
OutNode() { this.getExpr() instanceof Call }
|
||||
|
||||
@@ -80,9 +77,13 @@ class OutNode extends ExprNode {
|
||||
DataFlowCall getCall() { result = this.getExpr() }
|
||||
}
|
||||
|
||||
/** Gets a node that can read the value returned at position `pos`. */
|
||||
OutNode getAViableOutNode(ReturnPosition pos) {
|
||||
pos.getCallable() = viableCallable(result.getCall())
|
||||
/**
|
||||
* Gets a node that can read the value returned from `call` with return kind
|
||||
* `kind`.
|
||||
*/
|
||||
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
|
||||
result = call.getNode() and
|
||||
kind = TNormalReturnKind()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -153,6 +153,12 @@ private predicate localFlowStep(Node node1, Node node2, boolean preservesValue,
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnKind viableReturnKind(DataFlowCall call, ReturnPosition pos) {
|
||||
viableImpl(call) = pos.getCallable() and
|
||||
result = pos.getKind()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is reachable from a source in the given configuration
|
||||
* ignoring call contexts.
|
||||
@@ -193,20 +199,21 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
// flow into a callable
|
||||
exists(Node arg |
|
||||
nodeCandFwd1(arg, stored, config) and
|
||||
viableParamArg(node, arg)
|
||||
viableParamArg(_, node, arg)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
nodeCandFwd1(mid, stored, config) and
|
||||
parameterValueFlowsToUpdate(p, mid) and
|
||||
viableParamArg(p, node.(PostUpdateNode).getPreUpdateNode())
|
||||
viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
|
||||
nodeCandFwd1(ret, stored, config) and
|
||||
node = getAViableOutNode(ret.getPosition())
|
||||
kind = viableReturnKind(call, getReturnPosition(ret)) and
|
||||
node = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -268,21 +275,22 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
or
|
||||
// flow into a callable
|
||||
exists(Node param |
|
||||
viableParamArg(param, node) and
|
||||
viableParamArg(_, param, node) and
|
||||
nodeCand1(param, stored, config)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node) and
|
||||
viableParamArg(p, mid.getPreUpdateNode()) and
|
||||
viableParamArg(_, p, mid.getPreUpdateNode()) and
|
||||
nodeCand1(mid, stored, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(Node out |
|
||||
exists(DataFlowCall call, ReturnKind kind, OutNode out |
|
||||
nodeCand1(out, stored, config) and
|
||||
out = getAViableOutNode(node.(ReturnNode).getPosition())
|
||||
kind = viableReturnKind(call, getReturnPosition(node)) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -314,7 +322,12 @@ private predicate simpleParameterFlow(
|
||||
nodeCand1(node, false, config) and
|
||||
p = node and
|
||||
t = getErasedRepr(node.getType()) and
|
||||
not parameterValueFlowsThrough(p, _)
|
||||
exists(ReturnNode ret, CallContextCall cc |
|
||||
returnNodeGetEnclosingCallable(ret) = p.getEnclosingCallable() and
|
||||
cc = getAValidCallContextForParameter(p)
|
||||
|
|
||||
not parameterValueFlowsThrough(p, ret.getKind(), cc)
|
||||
)
|
||||
or
|
||||
nodeCand1(node, false, unbind(config)) and
|
||||
exists(Node mid |
|
||||
@@ -341,7 +354,7 @@ private predicate simpleParameterFlow(
|
||||
nodeCand1(node, false, config) and
|
||||
exists(Node arg |
|
||||
simpleParameterFlow(p, arg, t, config) and
|
||||
argumentValueFlowsThrough(arg, node) and
|
||||
argumentValueFlowsThrough(arg, node, _) and
|
||||
compatibleTypes(t, node.getType())
|
||||
)
|
||||
or
|
||||
@@ -353,21 +366,31 @@ private predicate simpleParameterFlow(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate simpleArgumentFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) |
|
||||
kind = ret.getKind() and
|
||||
viableParamArg(call, p, arg)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `arg` through the `call` taking simple call
|
||||
* contexts into consideration and that this is part of a path from a source
|
||||
* to a sink. This is restricted to paths through the `call` that does not
|
||||
* Holds if data can flow from `arg` through a call to `out`, taking simple
|
||||
* call contexts into consideration, and that this is part of a path from a
|
||||
* source to a sink. This is restricted to paths through calla that do not
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, Node out, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode param, ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
nodeCand1(out, false, unbind(config)) and
|
||||
viableParamArgOut(param, arg, ret.getPosition(), out) and
|
||||
simpleParameterFlow(param, ret, t, config)
|
||||
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -379,7 +402,7 @@ private predicate flowThroughCallableCand1(
|
||||
) {
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
|
||||
or
|
||||
argumentValueFlowsThrough(node1, node2) and preservesValue = true
|
||||
argumentValueFlowsThrough(node1, node2, _) and preservesValue = true
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -405,11 +428,15 @@ private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration c
|
||||
// flow out of an argument
|
||||
exists(ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node1) and
|
||||
viableParamArg(p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
node2 = getAViableOutNode(node1.(ReturnNode).getPosition())
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
kind = viableReturnKind(call, getReturnPosition(node1))
|
||||
|
|
||||
node2 = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -418,7 +445,7 @@ private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration c
|
||||
* path from a source to a sink.
|
||||
*/
|
||||
private predicate flowIntoCallableCand1(Node node1, Node node2, Configuration config) {
|
||||
viableParamArg(node2, node1) and
|
||||
viableParamArg(_, node2, node1) and
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config)
|
||||
}
|
||||
@@ -1438,7 +1465,9 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
flowOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowThroughCallable(mid, node, ap, cc)
|
||||
flowThroughCallable(mid, node, cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
or
|
||||
valueFlowThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
@@ -1458,30 +1487,34 @@ private predicate contentStoreStep(
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to a return at position `pos` in the
|
||||
* context `innercc`, and the path did not flow through a parameter.
|
||||
*/
|
||||
private predicate flowOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
|
||||
pos.getAReturnNode() = mid.getNode() and
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
innercc = mid.getCallContext() and
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowOutOfCallable1(
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
flowOutOfCallable0(mid, pos, innercc) and
|
||||
c = pos.getCallable() and
|
||||
kind = pos.getKind() and
|
||||
resolveReturn(innercc, c, call)
|
||||
|
|
||||
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to `out`. The last step of this path
|
||||
* is a return from a callable and is recorded by `cc`, if needed.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, DataFlowCall call, CallContext innercc |
|
||||
flowOutOfCallable0(mid, pos, innercc) and
|
||||
out = getAViableOutNode(pos) and
|
||||
c = pos.getCallable() and
|
||||
call = out.getCall() and
|
||||
resolveReturn(innercc, c, call)
|
||||
|
|
||||
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
|
||||
exists(ReturnKind kind, DataFlowCall call | flowOutOfCallable1(mid, call, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1560,20 +1593,35 @@ private predicate flowIntoCallable(
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if data may flow from `p` to a return at position `pos`. */
|
||||
/** Holds if data may flow from `p` to a return of kind `kind`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnPosition pos, AccessPath ap, CallContextCall cc, Configuration config
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid |
|
||||
mid.getNode() = pos.getAReturnNode() and
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
config = mid.getConfiguration() and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
|
|
||||
cc = TSomeCall(p, true)
|
||||
or
|
||||
exists(int i | cc = TSpecificCall(_, i, true) | p.isParameterOf(pos.getCallable(), i))
|
||||
exists(int i | cc = TSpecificCall(_, i, true) |
|
||||
p.isParameterOf(returnNodeGetEnclosingCallable(ret), i)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, kind, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc) and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1582,11 +1630,24 @@ private predicate paramFlowsThrough(
|
||||
* The context `cc` is restored to its value prior to entering the callable.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowThroughCallable(PathNodeMid mid, OutNode out, AccessPath ap, CallContext cc) {
|
||||
exists(ParameterNode p, ReturnPosition pos, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, out.getCall()) and
|
||||
paramFlowsThrough(p, pos, ap, innercc, unbind(mid.getConfiguration())) and
|
||||
out = getAViableOutNode(pos)
|
||||
private predicate flowThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind | flowThroughCallable0(call, mid, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate valueFlowThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc | flowIntoCallable(mid, p, cc, innercc, call) |
|
||||
parameterValueFlowsThrough(p, kind, innercc)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlowThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnKind kind | valueFlowThroughCallable0(out.getCall(), mid, kind, cc) |
|
||||
out = getAnOutNode(_, kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -153,6 +153,12 @@ private predicate localFlowStep(Node node1, Node node2, boolean preservesValue,
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnKind viableReturnKind(DataFlowCall call, ReturnPosition pos) {
|
||||
viableImpl(call) = pos.getCallable() and
|
||||
result = pos.getKind()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is reachable from a source in the given configuration
|
||||
* ignoring call contexts.
|
||||
@@ -193,20 +199,21 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
// flow into a callable
|
||||
exists(Node arg |
|
||||
nodeCandFwd1(arg, stored, config) and
|
||||
viableParamArg(node, arg)
|
||||
viableParamArg(_, node, arg)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
nodeCandFwd1(mid, stored, config) and
|
||||
parameterValueFlowsToUpdate(p, mid) and
|
||||
viableParamArg(p, node.(PostUpdateNode).getPreUpdateNode())
|
||||
viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
|
||||
nodeCandFwd1(ret, stored, config) and
|
||||
node = getAViableOutNode(ret.getPosition())
|
||||
kind = viableReturnKind(call, getReturnPosition(ret)) and
|
||||
node = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -268,21 +275,22 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
or
|
||||
// flow into a callable
|
||||
exists(Node param |
|
||||
viableParamArg(param, node) and
|
||||
viableParamArg(_, param, node) and
|
||||
nodeCand1(param, stored, config)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node) and
|
||||
viableParamArg(p, mid.getPreUpdateNode()) and
|
||||
viableParamArg(_, p, mid.getPreUpdateNode()) and
|
||||
nodeCand1(mid, stored, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(Node out |
|
||||
exists(DataFlowCall call, ReturnKind kind, OutNode out |
|
||||
nodeCand1(out, stored, config) and
|
||||
out = getAViableOutNode(node.(ReturnNode).getPosition())
|
||||
kind = viableReturnKind(call, getReturnPosition(node)) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -314,7 +322,12 @@ private predicate simpleParameterFlow(
|
||||
nodeCand1(node, false, config) and
|
||||
p = node and
|
||||
t = getErasedRepr(node.getType()) and
|
||||
not parameterValueFlowsThrough(p, _)
|
||||
exists(ReturnNode ret, CallContextCall cc |
|
||||
returnNodeGetEnclosingCallable(ret) = p.getEnclosingCallable() and
|
||||
cc = getAValidCallContextForParameter(p)
|
||||
|
|
||||
not parameterValueFlowsThrough(p, ret.getKind(), cc)
|
||||
)
|
||||
or
|
||||
nodeCand1(node, false, unbind(config)) and
|
||||
exists(Node mid |
|
||||
@@ -341,7 +354,7 @@ private predicate simpleParameterFlow(
|
||||
nodeCand1(node, false, config) and
|
||||
exists(Node arg |
|
||||
simpleParameterFlow(p, arg, t, config) and
|
||||
argumentValueFlowsThrough(arg, node) and
|
||||
argumentValueFlowsThrough(arg, node, _) and
|
||||
compatibleTypes(t, node.getType())
|
||||
)
|
||||
or
|
||||
@@ -353,21 +366,31 @@ private predicate simpleParameterFlow(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate simpleArgumentFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) |
|
||||
kind = ret.getKind() and
|
||||
viableParamArg(call, p, arg)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `arg` through the `call` taking simple call
|
||||
* contexts into consideration and that this is part of a path from a source
|
||||
* to a sink. This is restricted to paths through the `call` that does not
|
||||
* Holds if data can flow from `arg` through a call to `out`, taking simple
|
||||
* call contexts into consideration, and that this is part of a path from a
|
||||
* source to a sink. This is restricted to paths through calla that do not
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, Node out, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode param, ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
nodeCand1(out, false, unbind(config)) and
|
||||
viableParamArgOut(param, arg, ret.getPosition(), out) and
|
||||
simpleParameterFlow(param, ret, t, config)
|
||||
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -379,7 +402,7 @@ private predicate flowThroughCallableCand1(
|
||||
) {
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
|
||||
or
|
||||
argumentValueFlowsThrough(node1, node2) and preservesValue = true
|
||||
argumentValueFlowsThrough(node1, node2, _) and preservesValue = true
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -405,11 +428,15 @@ private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration c
|
||||
// flow out of an argument
|
||||
exists(ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node1) and
|
||||
viableParamArg(p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
node2 = getAViableOutNode(node1.(ReturnNode).getPosition())
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
kind = viableReturnKind(call, getReturnPosition(node1))
|
||||
|
|
||||
node2 = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -418,7 +445,7 @@ private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration c
|
||||
* path from a source to a sink.
|
||||
*/
|
||||
private predicate flowIntoCallableCand1(Node node1, Node node2, Configuration config) {
|
||||
viableParamArg(node2, node1) and
|
||||
viableParamArg(_, node2, node1) and
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config)
|
||||
}
|
||||
@@ -1438,7 +1465,9 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
flowOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowThroughCallable(mid, node, ap, cc)
|
||||
flowThroughCallable(mid, node, cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
or
|
||||
valueFlowThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
@@ -1458,30 +1487,34 @@ private predicate contentStoreStep(
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to a return at position `pos` in the
|
||||
* context `innercc`, and the path did not flow through a parameter.
|
||||
*/
|
||||
private predicate flowOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
|
||||
pos.getAReturnNode() = mid.getNode() and
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
innercc = mid.getCallContext() and
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowOutOfCallable1(
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
flowOutOfCallable0(mid, pos, innercc) and
|
||||
c = pos.getCallable() and
|
||||
kind = pos.getKind() and
|
||||
resolveReturn(innercc, c, call)
|
||||
|
|
||||
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to `out`. The last step of this path
|
||||
* is a return from a callable and is recorded by `cc`, if needed.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, DataFlowCall call, CallContext innercc |
|
||||
flowOutOfCallable0(mid, pos, innercc) and
|
||||
out = getAViableOutNode(pos) and
|
||||
c = pos.getCallable() and
|
||||
call = out.getCall() and
|
||||
resolveReturn(innercc, c, call)
|
||||
|
|
||||
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
|
||||
exists(ReturnKind kind, DataFlowCall call | flowOutOfCallable1(mid, call, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1560,20 +1593,35 @@ private predicate flowIntoCallable(
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if data may flow from `p` to a return at position `pos`. */
|
||||
/** Holds if data may flow from `p` to a return of kind `kind`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnPosition pos, AccessPath ap, CallContextCall cc, Configuration config
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid |
|
||||
mid.getNode() = pos.getAReturnNode() and
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
config = mid.getConfiguration() and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
|
|
||||
cc = TSomeCall(p, true)
|
||||
or
|
||||
exists(int i | cc = TSpecificCall(_, i, true) | p.isParameterOf(pos.getCallable(), i))
|
||||
exists(int i | cc = TSpecificCall(_, i, true) |
|
||||
p.isParameterOf(returnNodeGetEnclosingCallable(ret), i)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, kind, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc) and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1582,11 +1630,24 @@ private predicate paramFlowsThrough(
|
||||
* The context `cc` is restored to its value prior to entering the callable.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowThroughCallable(PathNodeMid mid, OutNode out, AccessPath ap, CallContext cc) {
|
||||
exists(ParameterNode p, ReturnPosition pos, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, out.getCall()) and
|
||||
paramFlowsThrough(p, pos, ap, innercc, unbind(mid.getConfiguration())) and
|
||||
out = getAViableOutNode(pos)
|
||||
private predicate flowThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind | flowThroughCallable0(call, mid, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate valueFlowThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc | flowIntoCallable(mid, p, cc, innercc, call) |
|
||||
parameterValueFlowsThrough(p, kind, innercc)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlowThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnKind kind | valueFlowThroughCallable0(out.getCall(), mid, kind, cc) |
|
||||
out = getAnOutNode(_, kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -153,6 +153,12 @@ private predicate localFlowStep(Node node1, Node node2, boolean preservesValue,
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnKind viableReturnKind(DataFlowCall call, ReturnPosition pos) {
|
||||
viableImpl(call) = pos.getCallable() and
|
||||
result = pos.getKind()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is reachable from a source in the given configuration
|
||||
* ignoring call contexts.
|
||||
@@ -193,20 +199,21 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
// flow into a callable
|
||||
exists(Node arg |
|
||||
nodeCandFwd1(arg, stored, config) and
|
||||
viableParamArg(node, arg)
|
||||
viableParamArg(_, node, arg)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
nodeCandFwd1(mid, stored, config) and
|
||||
parameterValueFlowsToUpdate(p, mid) and
|
||||
viableParamArg(p, node.(PostUpdateNode).getPreUpdateNode())
|
||||
viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
|
||||
nodeCandFwd1(ret, stored, config) and
|
||||
node = getAViableOutNode(ret.getPosition())
|
||||
kind = viableReturnKind(call, getReturnPosition(ret)) and
|
||||
node = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -268,21 +275,22 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
or
|
||||
// flow into a callable
|
||||
exists(Node param |
|
||||
viableParamArg(param, node) and
|
||||
viableParamArg(_, param, node) and
|
||||
nodeCand1(param, stored, config)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node) and
|
||||
viableParamArg(p, mid.getPreUpdateNode()) and
|
||||
viableParamArg(_, p, mid.getPreUpdateNode()) and
|
||||
nodeCand1(mid, stored, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(Node out |
|
||||
exists(DataFlowCall call, ReturnKind kind, OutNode out |
|
||||
nodeCand1(out, stored, config) and
|
||||
out = getAViableOutNode(node.(ReturnNode).getPosition())
|
||||
kind = viableReturnKind(call, getReturnPosition(node)) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -314,7 +322,12 @@ private predicate simpleParameterFlow(
|
||||
nodeCand1(node, false, config) and
|
||||
p = node and
|
||||
t = getErasedRepr(node.getType()) and
|
||||
not parameterValueFlowsThrough(p, _)
|
||||
exists(ReturnNode ret, CallContextCall cc |
|
||||
returnNodeGetEnclosingCallable(ret) = p.getEnclosingCallable() and
|
||||
cc = getAValidCallContextForParameter(p)
|
||||
|
|
||||
not parameterValueFlowsThrough(p, ret.getKind(), cc)
|
||||
)
|
||||
or
|
||||
nodeCand1(node, false, unbind(config)) and
|
||||
exists(Node mid |
|
||||
@@ -341,7 +354,7 @@ private predicate simpleParameterFlow(
|
||||
nodeCand1(node, false, config) and
|
||||
exists(Node arg |
|
||||
simpleParameterFlow(p, arg, t, config) and
|
||||
argumentValueFlowsThrough(arg, node) and
|
||||
argumentValueFlowsThrough(arg, node, _) and
|
||||
compatibleTypes(t, node.getType())
|
||||
)
|
||||
or
|
||||
@@ -353,21 +366,31 @@ private predicate simpleParameterFlow(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate simpleArgumentFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) |
|
||||
kind = ret.getKind() and
|
||||
viableParamArg(call, p, arg)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `arg` through the `call` taking simple call
|
||||
* contexts into consideration and that this is part of a path from a source
|
||||
* to a sink. This is restricted to paths through the `call` that does not
|
||||
* Holds if data can flow from `arg` through a call to `out`, taking simple
|
||||
* call contexts into consideration, and that this is part of a path from a
|
||||
* source to a sink. This is restricted to paths through calla that do not
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, Node out, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode param, ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
nodeCand1(out, false, unbind(config)) and
|
||||
viableParamArgOut(param, arg, ret.getPosition(), out) and
|
||||
simpleParameterFlow(param, ret, t, config)
|
||||
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -379,7 +402,7 @@ private predicate flowThroughCallableCand1(
|
||||
) {
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
|
||||
or
|
||||
argumentValueFlowsThrough(node1, node2) and preservesValue = true
|
||||
argumentValueFlowsThrough(node1, node2, _) and preservesValue = true
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -405,11 +428,15 @@ private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration c
|
||||
// flow out of an argument
|
||||
exists(ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node1) and
|
||||
viableParamArg(p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
node2 = getAViableOutNode(node1.(ReturnNode).getPosition())
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
kind = viableReturnKind(call, getReturnPosition(node1))
|
||||
|
|
||||
node2 = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -418,7 +445,7 @@ private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration c
|
||||
* path from a source to a sink.
|
||||
*/
|
||||
private predicate flowIntoCallableCand1(Node node1, Node node2, Configuration config) {
|
||||
viableParamArg(node2, node1) and
|
||||
viableParamArg(_, node2, node1) and
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config)
|
||||
}
|
||||
@@ -1438,7 +1465,9 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
flowOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowThroughCallable(mid, node, ap, cc)
|
||||
flowThroughCallable(mid, node, cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
or
|
||||
valueFlowThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
@@ -1458,30 +1487,34 @@ private predicate contentStoreStep(
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to a return at position `pos` in the
|
||||
* context `innercc`, and the path did not flow through a parameter.
|
||||
*/
|
||||
private predicate flowOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
|
||||
pos.getAReturnNode() = mid.getNode() and
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
innercc = mid.getCallContext() and
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowOutOfCallable1(
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
flowOutOfCallable0(mid, pos, innercc) and
|
||||
c = pos.getCallable() and
|
||||
kind = pos.getKind() and
|
||||
resolveReturn(innercc, c, call)
|
||||
|
|
||||
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to `out`. The last step of this path
|
||||
* is a return from a callable and is recorded by `cc`, if needed.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, DataFlowCall call, CallContext innercc |
|
||||
flowOutOfCallable0(mid, pos, innercc) and
|
||||
out = getAViableOutNode(pos) and
|
||||
c = pos.getCallable() and
|
||||
call = out.getCall() and
|
||||
resolveReturn(innercc, c, call)
|
||||
|
|
||||
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
|
||||
exists(ReturnKind kind, DataFlowCall call | flowOutOfCallable1(mid, call, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1560,20 +1593,35 @@ private predicate flowIntoCallable(
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if data may flow from `p` to a return at position `pos`. */
|
||||
/** Holds if data may flow from `p` to a return of kind `kind`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnPosition pos, AccessPath ap, CallContextCall cc, Configuration config
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid |
|
||||
mid.getNode() = pos.getAReturnNode() and
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
config = mid.getConfiguration() and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
|
|
||||
cc = TSomeCall(p, true)
|
||||
or
|
||||
exists(int i | cc = TSpecificCall(_, i, true) | p.isParameterOf(pos.getCallable(), i))
|
||||
exists(int i | cc = TSpecificCall(_, i, true) |
|
||||
p.isParameterOf(returnNodeGetEnclosingCallable(ret), i)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, kind, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc) and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1582,11 +1630,24 @@ private predicate paramFlowsThrough(
|
||||
* The context `cc` is restored to its value prior to entering the callable.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowThroughCallable(PathNodeMid mid, OutNode out, AccessPath ap, CallContext cc) {
|
||||
exists(ParameterNode p, ReturnPosition pos, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, out.getCall()) and
|
||||
paramFlowsThrough(p, pos, ap, innercc, unbind(mid.getConfiguration())) and
|
||||
out = getAViableOutNode(pos)
|
||||
private predicate flowThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind | flowThroughCallable0(call, mid, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate valueFlowThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc | flowIntoCallable(mid, p, cc, innercc, call) |
|
||||
parameterValueFlowsThrough(p, kind, innercc)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlowThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnKind kind | valueFlowThroughCallable0(out.getCall(), mid, kind, cc) |
|
||||
out = getAnOutNode(_, kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -153,6 +153,12 @@ private predicate localFlowStep(Node node1, Node node2, boolean preservesValue,
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnKind viableReturnKind(DataFlowCall call, ReturnPosition pos) {
|
||||
viableImpl(call) = pos.getCallable() and
|
||||
result = pos.getKind()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is reachable from a source in the given configuration
|
||||
* ignoring call contexts.
|
||||
@@ -193,20 +199,21 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
// flow into a callable
|
||||
exists(Node arg |
|
||||
nodeCandFwd1(arg, stored, config) and
|
||||
viableParamArg(node, arg)
|
||||
viableParamArg(_, node, arg)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
nodeCandFwd1(mid, stored, config) and
|
||||
parameterValueFlowsToUpdate(p, mid) and
|
||||
viableParamArg(p, node.(PostUpdateNode).getPreUpdateNode())
|
||||
viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
|
||||
nodeCandFwd1(ret, stored, config) and
|
||||
node = getAViableOutNode(ret.getPosition())
|
||||
kind = viableReturnKind(call, getReturnPosition(ret)) and
|
||||
node = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -268,21 +275,22 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
|
||||
or
|
||||
// flow into a callable
|
||||
exists(Node param |
|
||||
viableParamArg(param, node) and
|
||||
viableParamArg(_, param, node) and
|
||||
nodeCand1(param, stored, config)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node) and
|
||||
viableParamArg(p, mid.getPreUpdateNode()) and
|
||||
viableParamArg(_, p, mid.getPreUpdateNode()) and
|
||||
nodeCand1(mid, stored, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(Node out |
|
||||
exists(DataFlowCall call, ReturnKind kind, OutNode out |
|
||||
nodeCand1(out, stored, config) and
|
||||
out = getAViableOutNode(node.(ReturnNode).getPosition())
|
||||
kind = viableReturnKind(call, getReturnPosition(node)) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -314,7 +322,12 @@ private predicate simpleParameterFlow(
|
||||
nodeCand1(node, false, config) and
|
||||
p = node and
|
||||
t = getErasedRepr(node.getType()) and
|
||||
not parameterValueFlowsThrough(p, _)
|
||||
exists(ReturnNode ret, CallContextCall cc |
|
||||
returnNodeGetEnclosingCallable(ret) = p.getEnclosingCallable() and
|
||||
cc = getAValidCallContextForParameter(p)
|
||||
|
|
||||
not parameterValueFlowsThrough(p, ret.getKind(), cc)
|
||||
)
|
||||
or
|
||||
nodeCand1(node, false, unbind(config)) and
|
||||
exists(Node mid |
|
||||
@@ -341,7 +354,7 @@ private predicate simpleParameterFlow(
|
||||
nodeCand1(node, false, config) and
|
||||
exists(Node arg |
|
||||
simpleParameterFlow(p, arg, t, config) and
|
||||
argumentValueFlowsThrough(arg, node) and
|
||||
argumentValueFlowsThrough(arg, node, _) and
|
||||
compatibleTypes(t, node.getType())
|
||||
)
|
||||
or
|
||||
@@ -353,21 +366,31 @@ private predicate simpleParameterFlow(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate simpleArgumentFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) |
|
||||
kind = ret.getKind() and
|
||||
viableParamArg(call, p, arg)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `arg` through the `call` taking simple call
|
||||
* contexts into consideration and that this is part of a path from a source
|
||||
* to a sink. This is restricted to paths through the `call` that does not
|
||||
* Holds if data can flow from `arg` through a call to `out`, taking simple
|
||||
* call contexts into consideration, and that this is part of a path from a
|
||||
* source to a sink. This is restricted to paths through calla that do not
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, Node out, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode param, ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
nodeCand1(out, false, unbind(config)) and
|
||||
viableParamArgOut(param, arg, ret.getPosition(), out) and
|
||||
simpleParameterFlow(param, ret, t, config)
|
||||
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -379,7 +402,7 @@ private predicate flowThroughCallableCand1(
|
||||
) {
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
|
||||
or
|
||||
argumentValueFlowsThrough(node1, node2) and preservesValue = true
|
||||
argumentValueFlowsThrough(node1, node2, _) and preservesValue = true
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -405,11 +428,15 @@ private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration c
|
||||
// flow out of an argument
|
||||
exists(ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node1) and
|
||||
viableParamArg(p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
node2 = getAViableOutNode(node1.(ReturnNode).getPosition())
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
kind = viableReturnKind(call, getReturnPosition(node1))
|
||||
|
|
||||
node2 = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -418,7 +445,7 @@ private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration c
|
||||
* path from a source to a sink.
|
||||
*/
|
||||
private predicate flowIntoCallableCand1(Node node1, Node node2, Configuration config) {
|
||||
viableParamArg(node2, node1) and
|
||||
viableParamArg(_, node2, node1) and
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config)
|
||||
}
|
||||
@@ -1438,7 +1465,9 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
flowOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowThroughCallable(mid, node, ap, cc)
|
||||
flowThroughCallable(mid, node, cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
or
|
||||
valueFlowThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
@@ -1458,30 +1487,34 @@ private predicate contentStoreStep(
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to a return at position `pos` in the
|
||||
* context `innercc`, and the path did not flow through a parameter.
|
||||
*/
|
||||
private predicate flowOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
|
||||
pos.getAReturnNode() = mid.getNode() and
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
innercc = mid.getCallContext() and
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowOutOfCallable1(
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
flowOutOfCallable0(mid, pos, innercc) and
|
||||
c = pos.getCallable() and
|
||||
kind = pos.getKind() and
|
||||
resolveReturn(innercc, c, call)
|
||||
|
|
||||
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to `out`. The last step of this path
|
||||
* is a return from a callable and is recorded by `cc`, if needed.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, DataFlowCall call, CallContext innercc |
|
||||
flowOutOfCallable0(mid, pos, innercc) and
|
||||
out = getAViableOutNode(pos) and
|
||||
c = pos.getCallable() and
|
||||
call = out.getCall() and
|
||||
resolveReturn(innercc, c, call)
|
||||
|
|
||||
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
|
||||
exists(ReturnKind kind, DataFlowCall call | flowOutOfCallable1(mid, call, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1560,20 +1593,35 @@ private predicate flowIntoCallable(
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if data may flow from `p` to a return at position `pos`. */
|
||||
/** Holds if data may flow from `p` to a return of kind `kind`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnPosition pos, AccessPath ap, CallContextCall cc, Configuration config
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid |
|
||||
mid.getNode() = pos.getAReturnNode() and
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
config = mid.getConfiguration() and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
|
|
||||
cc = TSomeCall(p, true)
|
||||
or
|
||||
exists(int i | cc = TSpecificCall(_, i, true) | p.isParameterOf(pos.getCallable(), i))
|
||||
exists(int i | cc = TSpecificCall(_, i, true) |
|
||||
p.isParameterOf(returnNodeGetEnclosingCallable(ret), i)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, kind, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc) and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1582,11 +1630,24 @@ private predicate paramFlowsThrough(
|
||||
* The context `cc` is restored to its value prior to entering the callable.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowThroughCallable(PathNodeMid mid, OutNode out, AccessPath ap, CallContext cc) {
|
||||
exists(ParameterNode p, ReturnPosition pos, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, out.getCall()) and
|
||||
paramFlowsThrough(p, pos, ap, innercc, unbind(mid.getConfiguration())) and
|
||||
out = getAViableOutNode(pos)
|
||||
private predicate flowThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind | flowThroughCallable0(call, mid, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate valueFlowThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc | flowIntoCallable(mid, p, cc, innercc, call) |
|
||||
parameterValueFlowsThrough(p, kind, innercc)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlowThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnKind kind | valueFlowThroughCallable0(out.getCall(), mid, kind, cc) |
|
||||
out = getAnOutNode(_, kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -13,77 +13,179 @@ private module ImplCommon {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` is a possible argument to `p` taking virtual dispatch into account.
|
||||
* Holds if `arg` is a possible argument to `p` in `call`, taking virtual
|
||||
* dispatch into account.
|
||||
*/
|
||||
cached
|
||||
predicate viableParamArg(ParameterNode p, ArgumentNode arg) {
|
||||
exists(int i, DataFlowCall call |
|
||||
predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
|
||||
exists(int i |
|
||||
viableParam(call, i, p) and
|
||||
arg.argumentOf(call, i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `p` is the `i`th parameter of a viable dispatch target of a call
|
||||
* that can return values at position `pos` out to the node `out`. The instance
|
||||
* parameter is considered to have index `-1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate viableParamOut(int i, ParameterNode p, OutNode out, ReturnPosition ret) {
|
||||
out = getAViableOutNode(ret) and
|
||||
p.isParameterOf(ret.getCallable(), i)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` is a possible argument to `p` taking virtual dispatch into account,
|
||||
* and the callable can return values at position `pos` out to the node `out`.
|
||||
*/
|
||||
cached
|
||||
predicate viableParamArgOut(ParameterNode p, ArgumentNode arg, ReturnPosition ret, OutNode out) {
|
||||
exists(int i |
|
||||
viableParamOut(i, p, out, ret) and
|
||||
arg.argumentOf(out.getCall(), i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `p` can flow to `node` in the same callable using only
|
||||
* value-preserving steps.
|
||||
* value-preserving steps, not taking call contexts into account.
|
||||
*/
|
||||
private predicate parameterValueFlow(ParameterNode p, Node node) {
|
||||
private predicate parameterValueFlowNoCtx(ParameterNode p, Node node) {
|
||||
p = node
|
||||
or
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid) and
|
||||
parameterValueFlowNoCtx(p, mid) and
|
||||
localFlowStep(mid, node) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(Node arg |
|
||||
parameterValueFlow(p, arg) and
|
||||
argumentValueFlowsThrough(arg, node) and
|
||||
parameterValueFlowNoCtx(p, arg) and
|
||||
argumentValueFlowsThroughNoCtx(arg, node) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `p` can flow to a return node with position `pos` in the same
|
||||
* callable using only value-preserving steps.
|
||||
* Holds if `p` can flow to a return node of kind `kind` in the same
|
||||
* callable using only value-preserving steps, not taking call contexts
|
||||
* into account.
|
||||
*/
|
||||
cached
|
||||
predicate parameterValueFlowsThrough(ParameterNode p, ReturnPosition pos) {
|
||||
parameterValueFlow(p, pos.getAReturnNode())
|
||||
private predicate parameterValueFlowsThroughNoCtx(ParameterNode p, ReturnKind kind) {
|
||||
parameterValueFlowNoCtx(p, getAReturnNodeOfKind(kind))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate argumentValueFlowsThroughNoCtx0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind
|
||||
) {
|
||||
exists(ParameterNode param | viableParamArg(call, param, arg) |
|
||||
parameterValueFlowsThroughNoCtx(param, kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` flows through a call to `out` using only value-preserving steps.
|
||||
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
|
||||
* not taking call contexts into account.
|
||||
*/
|
||||
private predicate argumentValueFlowsThroughNoCtx(ArgumentNode arg, OutNode out) {
|
||||
exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughNoCtx0(call, arg, kind) |
|
||||
out = getAnOutNode(call, kind) and
|
||||
compatibleTypes(arg.getType(), out.getType())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` is the `i`th argument of `call` inside the callable
|
||||
* `enclosing`, and `arg` may flow through `call`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate argumentOf(
|
||||
DataFlowCall call, int i, ArgumentNode arg, DataFlowCallable enclosing
|
||||
) {
|
||||
arg.argumentOf(call, i) and
|
||||
argumentValueFlowsThroughNoCtx0(call, arg, _) and
|
||||
enclosing = arg.getEnclosingCallable()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate viableParamArg0(int i, ArgumentNode arg, CallContext outercc, DataFlowCall call) {
|
||||
exists(DataFlowCallable c | argumentOf(call, i, arg, c) |
|
||||
outercc = TAnyCallContext()
|
||||
or
|
||||
exists(ParameterNode p | outercc = TSomeCall(p, _) | c = p.getEnclosingCallable())
|
||||
or
|
||||
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) | c = viableCallable(other))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate viableParamArg1(
|
||||
ParameterNode p, DataFlowCallable callable, int i, ArgumentNode arg, CallContext outercc,
|
||||
DataFlowCall call
|
||||
) {
|
||||
viableParamArg0(i, arg, outercc, call) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
p.isParameterOf(callable, any(int j | j <= i and j >= i))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` is a possible argument to `p`, in the call `call`, and
|
||||
* `arg` may flow through `call`. The possible contexts before and after
|
||||
* entering the callable are `outercc` and `innercc`, respectively.
|
||||
*/
|
||||
private predicate viableParamArg(
|
||||
DataFlowCall call, ParameterNode p, ArgumentNode arg, CallContext outercc,
|
||||
CallContextCall innercc
|
||||
) {
|
||||
exists(int i, DataFlowCallable callable | viableParamArg1(p, callable, i, arg, outercc, call) |
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
then innercc = TSpecificCall(call, i, true)
|
||||
else innercc = TSomeCall(p, true)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
CallContextCall getAValidCallContextForParameter(ParameterNode p) {
|
||||
result = TSomeCall(p, _)
|
||||
or
|
||||
exists(DataFlowCall call, int i | result = TSpecificCall(call, i, _) |
|
||||
p.isParameterOf(_, i) and p.getEnclosingCallable() = viableCallable(call)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `p` can flow to `node` in the same callable using only
|
||||
* value-preserving steps, in call context `cc`.
|
||||
*/
|
||||
private predicate parameterValueFlow(ParameterNode p, Node node, CallContextCall cc) {
|
||||
p = node and
|
||||
parameterValueFlowsThroughNoCtx(p, _) and
|
||||
cc = getAValidCallContextForParameter(p)
|
||||
or
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid, cc) and
|
||||
localFlowStep(mid, node) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(Node arg |
|
||||
parameterValueFlow(p, arg, cc) and
|
||||
argumentValueFlowsThrough(arg, node, cc) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
)
|
||||
}
|
||||
|
||||
private ReturnNode getAReturnNodeOfKind(ReturnKind kind) { result.getKind() = kind }
|
||||
|
||||
/**
|
||||
* Holds if `p` can flow to a return node of kind `kind` in the same
|
||||
* callable using only value-preserving steps, in call context `cc`.
|
||||
*/
|
||||
cached
|
||||
predicate argumentValueFlowsThrough(ArgumentNode arg, OutNode out) {
|
||||
exists(ParameterNode param, ReturnPosition ret |
|
||||
viableParamArgOut(param, arg, ret, out) and
|
||||
parameterValueFlowsThrough(param, ret) and
|
||||
predicate parameterValueFlowsThrough(ParameterNode p, ReturnKind kind, CallContextCall cc) {
|
||||
parameterValueFlow(p, getAReturnNodeOfKind(kind), cc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate argumentValueFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode param, CallContext innercc |
|
||||
viableParamArg(call, param, arg, cc, innercc)
|
||||
|
|
||||
parameterValueFlowsThrough(param, kind, innercc)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
|
||||
* in call context cc.
|
||||
*/
|
||||
cached
|
||||
predicate argumentValueFlowsThrough(ArgumentNode arg, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, cc) |
|
||||
out = getAnOutNode(call, kind) and
|
||||
compatibleTypes(arg.getType(), out.getType())
|
||||
)
|
||||
}
|
||||
@@ -94,7 +196,7 @@ private module ImplCommon {
|
||||
*/
|
||||
cached
|
||||
predicate parameterValueFlowsToUpdate(ParameterNode p, PostUpdateNode n) {
|
||||
parameterValueFlow(p, n.getPreUpdateNode())
|
||||
parameterValueFlowNoCtx(p, n.getPreUpdateNode())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,7 +205,7 @@ private module ImplCommon {
|
||||
*/
|
||||
private predicate localValueStep(Node node1, Node node2) {
|
||||
localFlowStep(node1, node2) or
|
||||
argumentValueFlowsThrough(node1, node2)
|
||||
argumentValueFlowsThrough(node1, node2, _)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -143,9 +245,9 @@ private module ImplCommon {
|
||||
pragma[nomagic]
|
||||
private predicate setterInParam(ParameterNode p1, Content f, ParameterNode p2) {
|
||||
exists(Node n1, PostUpdateNode n2 |
|
||||
parameterValueFlow(p1, n1) and
|
||||
parameterValueFlowNoCtx(p1, n1) and
|
||||
storeViaSideEffect(n1, f, n2) and
|
||||
parameterValueFlow(p2, n2.getPreUpdateNode()) and
|
||||
parameterValueFlowNoCtx(p2, n2.getPreUpdateNode()) and
|
||||
p1 != p2
|
||||
)
|
||||
}
|
||||
@@ -160,21 +262,35 @@ private module ImplCommon {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate storeReturn0(DataFlowCall call, ReturnKind kind, ArgumentNode arg, Content f) {
|
||||
exists(ParameterNode p |
|
||||
viableParamArg(call, p, arg) and
|
||||
setterReturn(p, f, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate storeReturn(Node node1, Content f, Node node2) {
|
||||
exists(ParameterNode p, ArgumentNode arg, ReturnPosition ret |
|
||||
arg = node1 and
|
||||
viableParamArgOut(p, arg, ret, node2) and
|
||||
setterReturn(p, f, ret) and
|
||||
exists(DataFlowCall call, ReturnKind kind | storeReturn0(call, kind, node1, f) |
|
||||
node2 = getAnOutNode(call, kind) and
|
||||
compatibleTypes(node1.getTypeBound(), f.getType()) and
|
||||
compatibleTypes(node2.getTypeBound(), f.getContainerType())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate setterReturn(ParameterNode p, Content f, ReturnPosition ret) {
|
||||
private predicate setterReturn(ParameterNode p, Content f, ReturnKind kind) {
|
||||
exists(Node n1, Node n2 |
|
||||
parameterValueFlow(p, n1) and
|
||||
parameterValueFlowNoCtx(p, n1) and
|
||||
store(n1, f, n2) and
|
||||
localValueStep*(n2, ret.getAReturnNode())
|
||||
localValueStep*(n2, getAReturnNodeOfKind(kind))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate read0(DataFlowCall call, ReturnKind kind, ArgumentNode arg, Content f) {
|
||||
exists(ParameterNode p |
|
||||
viableParamArg(call, p, arg) and
|
||||
getter(p, f, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -186,20 +302,18 @@ private module ImplCommon {
|
||||
predicate read(Node node1, Content f, Node node2) {
|
||||
readStep(node1, f, node2) and storeStep(_, f, _)
|
||||
or
|
||||
exists(ParameterNode p, ArgumentNode arg, ReturnPosition ret |
|
||||
arg = node1 and
|
||||
viableParamArgOut(p, arg, ret, node2) and
|
||||
getter(p, f, ret) and
|
||||
exists(DataFlowCall call, ReturnKind kind | read0(call, kind, node1, f) |
|
||||
node2 = getAnOutNode(call, kind) and
|
||||
compatibleTypes(node1.getTypeBound(), f.getContainerType()) and
|
||||
compatibleTypes(node2.getTypeBound(), f.getType())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate getter(ParameterNode p, Content f, ReturnPosition ret) {
|
||||
private predicate getter(ParameterNode p, Content f, ReturnKind kind) {
|
||||
exists(Node n1, Node n2 |
|
||||
parameterValueFlow(p, n1) and
|
||||
parameterValueFlowNoCtx(p, n1) and
|
||||
read(n1, f, n2) and
|
||||
localValueStep*(n2, ret.getAReturnNode())
|
||||
localValueStep*(n2, getAReturnNodeOfKind(kind))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -234,9 +348,19 @@ private module ImplCommon {
|
||||
} or
|
||||
TSomeCall(ParameterNode p, boolean emptyAp) { emptyAp = true or emptyAp = false } or
|
||||
TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) }
|
||||
|
||||
cached
|
||||
newtype TReturnPosition =
|
||||
TReturnPosition0(DataFlowCallable c, ReturnKind kind) { returnPosition(_, c, kind) }
|
||||
}
|
||||
import ImplCommon
|
||||
|
||||
pragma[noinline]
|
||||
private predicate returnPosition(ReturnNode ret, DataFlowCallable c, ReturnKind kind) {
|
||||
c = returnNodeGetEnclosingCallable(ret) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
/**
|
||||
* A call context to restrict the targets of virtual dispatch and match the
|
||||
* call sites of flow into a method with flow out of a method.
|
||||
@@ -281,6 +405,36 @@ class CallContextReturn extends CallContext, TReturn {
|
||||
}
|
||||
}
|
||||
|
||||
/** A callable tagged with a relevant return kind. */
|
||||
class ReturnPosition extends TReturnPosition0 {
|
||||
private DataFlowCallable c;
|
||||
|
||||
private ReturnKind kind;
|
||||
|
||||
ReturnPosition() { this = TReturnPosition0(c, kind) }
|
||||
|
||||
/** Gets the callable. */
|
||||
DataFlowCallable getCallable() { result = c }
|
||||
|
||||
/** Gets the return kind. */
|
||||
ReturnKind getKind() { result = kind }
|
||||
|
||||
/** Gets a textual representation of this return position. */
|
||||
string toString() { result = "[" + kind + "] " + c }
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
DataFlowCallable returnNodeGetEnclosingCallable(ReturnNode ret) {
|
||||
result = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
ReturnPosition getReturnPosition(ReturnNode ret) {
|
||||
exists(DataFlowCallable c, ReturnKind k | returnPosition(ret, c, k) |
|
||||
result = TReturnPosition0(c, k)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[cc, callable]
|
||||
predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
|
||||
cc instanceof CallContextAny and callable = viableCallable(call)
|
||||
|
||||
@@ -24,37 +24,38 @@ class ArgumentNode extends Node {
|
||||
DataFlowCall getCall() { this.argumentOf(result, _) }
|
||||
}
|
||||
|
||||
private newtype TReturnKind = TNormalReturnKind()
|
||||
|
||||
/**
|
||||
* A return position. A return position describes how a value can return
|
||||
* from a given callable. For C++, this is simply a function return.
|
||||
* A return kind. A return kind describes how a value can be returned
|
||||
* from a callable. For C++, this is simply a function return.
|
||||
*/
|
||||
class ReturnPosition extends Function {
|
||||
ReturnPosition() { exists(ReturnNode ret | ret.getEnclosingCallable() = this) }
|
||||
|
||||
/** Gets the callable that a value can be returned from. */
|
||||
Function getCallable() { result = this }
|
||||
|
||||
/** Gets a return node that can return a value at this position. */
|
||||
ReturnNode getAReturnNode() { result.getEnclosingCallable() = this }
|
||||
class ReturnKind extends TReturnKind {
|
||||
/** Gets a textual representation of this return kind. */
|
||||
string toString() { result = "return" }
|
||||
}
|
||||
|
||||
/** A data flow node that occurs as the result of a `ReturnStmt`. */
|
||||
class ReturnNode extends Node {
|
||||
ReturnNode() { exists(ReturnValueInstruction ret | this = ret.getReturnValue()) }
|
||||
|
||||
/** Gets the position at which this value is returned. */
|
||||
ReturnPosition getPosition() { this = result.getAReturnNode() }
|
||||
/** Gets the kind of this returned value. */
|
||||
ReturnKind getKind() { result = TNormalReturnKind() }
|
||||
}
|
||||
|
||||
/** A data flow node that represents a call. */
|
||||
/** A data flow node that represents the output of a call. */
|
||||
class OutNode extends Node, CallInstruction {
|
||||
/** Gets the underlying call. */
|
||||
DataFlowCall getCall() { result = this }
|
||||
}
|
||||
|
||||
/** Gets a node that can read the value returned at position `pos`. */
|
||||
OutNode getAViableOutNode(ReturnPosition pos) {
|
||||
pos.getCallable() = viableCallable(result.getCall())
|
||||
/**
|
||||
* Gets a node that can read the value returned from `call` with return kind
|
||||
* `kind`.
|
||||
*/
|
||||
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
|
||||
result = call and
|
||||
kind = TNormalReturnKind()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user