Data flow: Introduce ReturnKind

This commit is contained in:
Tom Hvitved
2019-05-02 13:40:47 +02:00
parent 42f2c88516
commit b6206d7370
21 changed files with 2194 additions and 876 deletions

View File

@@ -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)
)
}

View File

@@ -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)
)
}

View File

@@ -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)
)
}

View File

@@ -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)
)
}

View File

@@ -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)

View File

@@ -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()
}
/**

View File

@@ -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)
)
}

View File

@@ -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)
)
}

View File

@@ -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)
)
}

View File

@@ -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)
)
}

View File

@@ -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)

View File

@@ -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()
}
/**