mirror of
https://github.com/github/codeql.git
synced 2026-04-29 02:35:15 +02:00
Merge pull request #1226 from hvitved/dataflow/prepare-for-csharp
Generalize data-flow library in preparation for C# adoption
This commit is contained in:
@@ -1,9 +1,6 @@
|
||||
private import cpp
|
||||
private import DataFlowPrivate
|
||||
|
||||
Function viableImpl(MethodAccess ma) {
|
||||
result = viableCallable(ma)
|
||||
}
|
||||
Function viableImpl(Call call) { result = viableCallable(call) }
|
||||
|
||||
/**
|
||||
* Gets a function that might be called by `call`.
|
||||
@@ -61,31 +58,23 @@ private predicate functionSignature(Function f, string qualifiedName, int nparam
|
||||
* Holds if the call context `ctx` reduces the set of viable dispatch
|
||||
* targets of `ma` in `c`.
|
||||
*/
|
||||
predicate reducedViableImplInCallContext(MethodAccess ma, Callable c, Call ctx) {
|
||||
none()
|
||||
}
|
||||
predicate reducedViableImplInCallContext(Call call, Function f, Call ctx) { none() }
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
|
||||
* restricted to those `ma`s for which the context makes a difference.
|
||||
*/
|
||||
Method prunedViableImplInCallContext(MethodAccess ma, Call ctx) {
|
||||
none()
|
||||
}
|
||||
Function prunedViableImplInCallContext(Call call, Call ctx) { none() }
|
||||
|
||||
/**
|
||||
* Holds if flow returning from `m` to `ma` might return further and if
|
||||
* this path restricts the set of call sites that can be returned to.
|
||||
*/
|
||||
predicate reducedViableImplInReturn(Method m, MethodAccess ma) {
|
||||
none()
|
||||
}
|
||||
predicate reducedViableImplInReturn(Function f, Call call) { none() }
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
|
||||
* restricted to those `ma`s and results for which the return flow from the
|
||||
* result to `ma` restricts the possible context `ctx`.
|
||||
*/
|
||||
Method prunedViableImplInCallContextReverse(MethodAccess ma, Call ctx) {
|
||||
none()
|
||||
}
|
||||
Function prunedViableImplInCallContextReverse(Call call, Call ctx) { none() }
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
/**
|
||||
* Provides an implementation of global (interprocedural) data flow. This file
|
||||
* re-exports the local (intraprocedural) data flow analysis from `DataFlowUtil`
|
||||
* and adds a global analysis, mainly exposed through the `Configuration` class.
|
||||
* This file exists in several identical copies, allowing queries to use
|
||||
* multiple `Configuration` classes that depend on each other without
|
||||
* introducing mutual recursion among those configurations.
|
||||
* re-exports the local (intraprocedural) data flow analysis from
|
||||
* `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed
|
||||
* through the `Configuration` class. This file exists in several identical
|
||||
* copies, allowing queries to use multiple `Configuration` classes that depend
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowDispatch
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
@@ -95,7 +94,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(Expr sink) { hasFlowTo(exprNode(sink)) }
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/** DEPRECATED: use `hasFlow` instead. */
|
||||
deprecated predicate hasFlowForward(Node source, Node sink) { hasFlow(source, sink) }
|
||||
@@ -114,7 +113,8 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
|
||||
|
||||
pragma[noinline]
|
||||
private predicate isAdditionalFlowStep(
|
||||
Node node1, Node node2, Callable callable1, Callable callable2, Configuration config
|
||||
Node node1, Node node2, DataFlowCallable callable1, DataFlowCallable callable2,
|
||||
Configuration config
|
||||
) {
|
||||
config.isAdditionalFlowStep(node1, node2) and
|
||||
callable1 = node1.getEnclosingCallable() and
|
||||
@@ -125,7 +125,7 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if the additional step from `node1` to `node2` does not jump between callables.
|
||||
*/
|
||||
private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
|
||||
exists(Callable callable | isAdditionalFlowStep(node1, node2, callable, callable, config))
|
||||
exists(DataFlowCallable callable | isAdditionalFlowStep(node1, node2, callable, callable, config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,16 +148,17 @@ private predicate localFlowStep(Node node1, Node node2, boolean preservesValue,
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private Method returnNodeGetEnclosingCallable(ReturnNode ret) {
|
||||
result = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if field flow should be used for the given configuration.
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
|
||||
viableImpl(call) = result.getCallable() and
|
||||
kind = result.getKind()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is reachable from a source in the given configuration
|
||||
* ignoring call contexts.
|
||||
@@ -198,22 +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(Method m, MethodAccess ma, ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
|
||||
nodeCandFwd1(ret, stored, config) and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
m = viableImpl(ma) and
|
||||
node.asExpr() = ma
|
||||
getReturnPosition(ret) = viableReturnPos(call, kind) and
|
||||
node = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -275,22 +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(Method m, ExprNode ma |
|
||||
nodeCand1(ma, stored, config) and
|
||||
m = returnNodeGetEnclosingCallable(node) and
|
||||
m = viableImpl(ma.getExpr())
|
||||
exists(DataFlowCall call, ReturnKind kind, OutNode out |
|
||||
nodeCand1(out, stored, config) and
|
||||
getReturnPosition(node) = viableReturnPos(call, kind) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -316,11 +316,17 @@ private predicate readCand1(Content f, Configuration config) {
|
||||
* from the configuration.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Configuration config) {
|
||||
private predicate simpleParameterFlow(
|
||||
ParameterNode p, Node node, DataFlowType t, Configuration config
|
||||
) {
|
||||
nodeCand1(node, false, config) and
|
||||
p = node and
|
||||
t = getErasedRepr(node.getType()) and
|
||||
not parameterValueFlowsThrough(p)
|
||||
exists(ReturnNode ret, ReturnKind kind |
|
||||
returnNodeGetEnclosingCallable(ret) = p.getEnclosingCallable() and
|
||||
kind = ret.getKind() and
|
||||
not parameterValueFlowsThrough(p, kind, _)
|
||||
)
|
||||
or
|
||||
nodeCand1(node, false, unbind(config)) and
|
||||
exists(Node mid |
|
||||
@@ -347,7 +353,7 @@ private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Con
|
||||
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
|
||||
@@ -359,45 +365,55 @@ private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Con
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, ExprNode call, RefType t, Configuration config
|
||||
pragma[noinline]
|
||||
private predicate simpleArgumentFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode param, ReturnNode ret |
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
nodeCand1(call, false, unbind(config)) and
|
||||
viableParamArg(param, arg) and
|
||||
simpleParameterFlow(param, ret, t, config) and
|
||||
arg.argumentOf(call.getExpr(), _)
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
exists(ParameterNode p, ReturnNode ret |
|
||||
simpleParameterFlow(p, ret, t, config) and
|
||||
kind = ret.getKind() and
|
||||
viableParamArg(call, p, arg)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a method.
|
||||
* Holds if data can flow from `arg` to `out` through a 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 calls that do not
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate flowThroughMethod(
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, Node out, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
nodeCand1(out, false, unbind(config)) and
|
||||
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a callable.
|
||||
*/
|
||||
private predicate flowThroughCallable(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
) {
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
|
||||
or
|
||||
argumentValueFlowsThrough(node1, node2) and preservesValue = true
|
||||
argumentValueFlowsThrough(node1, node2, _) and preservesValue = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in one local step or a step
|
||||
* through a method.
|
||||
* through a callable.
|
||||
*/
|
||||
private predicate localFlowStepOrFlowThroughMethod(
|
||||
private predicate localFlowStepOrFlowThroughCallable(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
) {
|
||||
localFlowStep(node1, node2, preservesValue, config) or
|
||||
flowThroughMethod(node1, node2, preservesValue, config)
|
||||
flowThroughCallable(node1, node2, preservesValue, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,15 +428,13 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
// 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 method
|
||||
exists(Method m, MethodAccess ma, ReturnNode ret |
|
||||
ret = node1 and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
m = viableImpl(ma) and
|
||||
node2.asExpr() = ma
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
getReturnPosition(node1) = viableReturnPos(call, kind) and
|
||||
node2 = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -430,7 +444,7 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
* path from a source to a sink.
|
||||
*/
|
||||
private predicate flowIntoCallable(Node node1, Node node2, Configuration config) {
|
||||
viableParamArg(node2, node1) and
|
||||
viableParamArg(_, node2, node1) and
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config)
|
||||
}
|
||||
@@ -505,7 +519,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
(
|
||||
exists(Node mid, boolean preservesValue |
|
||||
nodeCandFwd2(mid, fromArg, stored, config) and
|
||||
localFlowStepOrFlowThroughMethod(mid, node, preservesValue, config) and
|
||||
localFlowStepOrFlowThroughCallable(mid, node, preservesValue, config) and
|
||||
(stored = false or preservesValue = true)
|
||||
)
|
||||
or
|
||||
@@ -574,7 +588,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
nodeCandFwd2(node, _, unbindBool(stored), unbind(config)) and
|
||||
(
|
||||
exists(Node mid, boolean preservesValue |
|
||||
localFlowStepOrFlowThroughMethod(node, mid, preservesValue, config) and
|
||||
localFlowStepOrFlowThroughCallable(node, mid, preservesValue, config) and
|
||||
nodeCand2(mid, toReturn, stored, config) and
|
||||
(stored = false or preservesValue = true)
|
||||
)
|
||||
@@ -663,10 +677,10 @@ private predicate localFlowEntry(Node node, Configuration config) {
|
||||
config.isSource(node) or
|
||||
jumpStep(_, node, _, config) or
|
||||
node instanceof ParameterNode or
|
||||
node.asExpr() instanceof MethodAccess or
|
||||
node instanceof OutNode or
|
||||
node instanceof PostUpdateNode or
|
||||
read(_, _, node) or
|
||||
node.asExpr() instanceof CastExpr
|
||||
node instanceof CastNode
|
||||
)
|
||||
}
|
||||
|
||||
@@ -679,12 +693,12 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
jumpStep(node, next, _, config) or
|
||||
flowIntoCallable(node, next, config) or
|
||||
flowOutOfCallable(node, next, config) or
|
||||
flowThroughMethod(node, next, _, config) or
|
||||
flowThroughCallable(node, next, _, config) or
|
||||
store(node, _, next) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
node.asExpr() instanceof CastExpr
|
||||
node instanceof CastNode
|
||||
or
|
||||
config.isSink(node)
|
||||
}
|
||||
@@ -706,7 +720,7 @@ private predicate localFlowStepPlus(
|
||||
exists(Node mid, boolean pv1, boolean pv2 |
|
||||
localFlowStepPlus(node1, mid, pv1, config) and
|
||||
localFlowStep(mid, node2, pv2, config) and
|
||||
not mid.asExpr() instanceof CastExpr and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = pv1.booleanAnd(pv2) and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
@@ -725,7 +739,7 @@ private predicate localFlowBigStep(
|
||||
}
|
||||
|
||||
private newtype TAccessPathFront =
|
||||
TFrontNil(Type t) or
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(Content f)
|
||||
|
||||
/**
|
||||
@@ -733,12 +747,12 @@ private newtype TAccessPathFront =
|
||||
*/
|
||||
private class AccessPathFront extends TAccessPathFront {
|
||||
string toString() {
|
||||
exists(Type t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
or
|
||||
exists(Content f | this = TFrontHead(f) | result = f.toString())
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
DataFlowType getType() {
|
||||
this = TFrontNil(result)
|
||||
or
|
||||
exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
|
||||
@@ -755,8 +769,8 @@ private class AccessPathFrontNil extends AccessPathFront, TFrontNil { }
|
||||
private class CastingNode extends Node {
|
||||
CastingNode() {
|
||||
this instanceof ParameterNode or
|
||||
this.asExpr() instanceof CastExpr or
|
||||
this.asExpr() instanceof MethodAccess or
|
||||
this instanceof CastNode or
|
||||
this instanceof OutNode or
|
||||
this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode
|
||||
}
|
||||
}
|
||||
@@ -819,7 +833,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallable(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
or
|
||||
@@ -915,7 +929,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallable(node, mid, preservesValue, config) and
|
||||
flowCand(mid, toReturn, apf0, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
@@ -954,7 +968,7 @@ private predicate consCand(Content f, AccessPathFront apf, Configuration config)
|
||||
}
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(Type t) or
|
||||
TNil(DataFlowType t) or
|
||||
TCons(Content f, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
@@ -975,7 +989,7 @@ private class AccessPath extends TAccessPath {
|
||||
this = TCons(_, result)
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
DataFlowType getType() {
|
||||
this = TNil(result)
|
||||
or
|
||||
exists(Content head | this = TCons(head, _) | result = head.getContainerType())
|
||||
@@ -985,9 +999,11 @@ private class AccessPath extends TAccessPath {
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
override string toString() { exists(Type t | this = TNil(t) | result = ppReprType(t)) }
|
||||
override string toString() { exists(DataFlowType t | this = TNil(t) | result = ppReprType(t)) }
|
||||
|
||||
override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) }
|
||||
override AccessPathFront getFront() {
|
||||
exists(DataFlowType t | this = TNil(t) | result = TFrontNil(t))
|
||||
}
|
||||
}
|
||||
|
||||
private class AccessPathCons extends AccessPath, TCons {
|
||||
@@ -1083,7 +1099,7 @@ private predicate flowFwd0(
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0, AccessPath ap0 |
|
||||
flowFwd(mid, fromArg, apf0, ap0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallable(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0 and apf = apf0
|
||||
or
|
||||
@@ -1196,7 +1212,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPath ap0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallable(node, mid, preservesValue, config) and
|
||||
flow(mid, toReturn, ap0, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0
|
||||
@@ -1255,7 +1271,7 @@ private newtype TPathNode =
|
||||
or
|
||||
// ... or a step from an existing PathNode to another node.
|
||||
exists(PathNodeMid mid |
|
||||
flowStep(mid, node, cc, ap) and
|
||||
pathStep(mid, node, cc, ap) and
|
||||
config = mid.getConfiguration() and
|
||||
flow(node, _, ap, unbind(config))
|
||||
)
|
||||
@@ -1274,8 +1290,14 @@ abstract class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = getNode().toString() + ppAp() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { result = getNode().toString() + ppAp() + ppCtx() }
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() { result = getNode().getLocation() }
|
||||
DataFlowLocation getLocation() { result = getNode().getLocation() }
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
abstract Node getNode();
|
||||
@@ -1284,20 +1306,31 @@ abstract class PathNode extends TPathNode {
|
||||
abstract Configuration getConfiguration();
|
||||
|
||||
/** Gets a successor. */
|
||||
abstract PathNode getSucc();
|
||||
deprecated final PathNode getSucc() { result = this.getASuccessor() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
abstract PathNode getASuccessor();
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
result = " [" + this.(PathNodeMid).getAp().toString() + "]"
|
||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
)
|
||||
}
|
||||
|
||||
private string ppCtx() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getSucc()) }
|
||||
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
|
||||
|
||||
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getSucc() = n2 and reach(n2) }
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -1333,11 +1366,11 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
private PathNodeMid getSuccMid() {
|
||||
flowStep(this, result.getNode(), result.getCallContext(), result.getAp()) and
|
||||
pathStep(this, result.getNode(), result.getCallContext(), result.getAp()) and
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNode getSucc() {
|
||||
override PathNode getASuccessor() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -1391,14 +1424,14 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getSucc() { none() }
|
||||
override PathNode getASuccessor() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
@@ -1421,15 +1454,15 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap))
|
||||
or
|
||||
flowOutOfArgument(mid, node, cc) and ap = mid.getAp()
|
||||
pathOutOfArgument(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
pathIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
or
|
||||
flowOutOfMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
pathOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowThroughMethod(mid, node.asExpr(), cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
pathThroughCallable(mid, node, cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
or
|
||||
valueFlowThroughMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
valuePathThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
@@ -1449,37 +1482,41 @@ private predicate contentStoreStep(
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to an exit of `m` in the context
|
||||
* `innercc`, and the path did not flow through a parameter of `m`.
|
||||
*/
|
||||
private predicate flowOutOfMethod0(PathNodeMid mid, Method m, CallContext innercc) {
|
||||
exists(ReturnNode ret |
|
||||
ret = mid.getNode() and
|
||||
innercc = mid.getCallContext() and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
not innercc instanceof CallContextCall
|
||||
private predicate pathOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
innercc = mid.getCallContext() and
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate pathOutOfCallable1(
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
pathOutOfCallable0(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 `ma`. The last step of this path
|
||||
* is a return from a method and is recorded by `cc`, if needed.
|
||||
* 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 flowOutOfMethod(PathNodeMid mid, MethodAccess ma, CallContext cc) {
|
||||
exists(Method m, CallContext innercc |
|
||||
flowOutOfMethod0(mid, m, innercc) and
|
||||
resolveReturn(innercc, m, ma)
|
||||
|
|
||||
if reducedViableImplInReturn(m, ma) then cc = TReturn(m, ma) else cc = TAnyCallContext()
|
||||
private predicate pathOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnKind kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate flowOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
|
||||
private predicate pathOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
|
||||
exists(
|
||||
PostUpdateNode n, ParameterNode p, Callable callable, CallContext innercc, int i, Call call,
|
||||
ArgumentNode arg
|
||||
PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
|
||||
DataFlowCall call, ArgumentNode arg
|
||||
|
|
||||
mid.getNode() = n and
|
||||
parameterValueFlowsToUpdate(p, n) and
|
||||
@@ -1500,7 +1537,9 @@ private predicate flowOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallCo
|
||||
* Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowIntoArg(PathNodeMid mid, int i, CallContext cc, Call call, boolean emptyAp) {
|
||||
private predicate pathIntoArg(
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, boolean emptyAp
|
||||
) {
|
||||
exists(ArgumentNode arg, AccessPath ap |
|
||||
arg = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -1514,7 +1553,7 @@ private predicate flowIntoArg(PathNodeMid mid, int i, CallContext cc, Call call,
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterCand(Callable callable, int i, Configuration config) {
|
||||
private predicate parameterCand(DataFlowCallable callable, int i, Configuration config) {
|
||||
exists(ParameterNode p |
|
||||
flow(p, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
@@ -1522,10 +1561,11 @@ private predicate parameterCand(Callable callable, int i, Configuration config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallable0(
|
||||
PathNodeMid mid, Callable callable, int i, CallContext outercc, Call call, boolean emptyAp
|
||||
private predicate pathIntoCallable0(
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
boolean emptyAp
|
||||
) {
|
||||
flowIntoArg(mid, i, outercc, call, emptyAp) and
|
||||
pathIntoArg(mid, i, outercc, call, emptyAp) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
parameterCand(callable, any(int j | j <= i and j >= i), mid.getConfiguration())
|
||||
}
|
||||
@@ -1535,11 +1575,11 @@ private predicate flowIntoCallable0(
|
||||
* before and after entering the callable are `outercc` and `innercc`,
|
||||
* respectively.
|
||||
*/
|
||||
private predicate flowIntoCallable(
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, Call call
|
||||
private predicate pathIntoCallable(
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, DataFlowCall call
|
||||
) {
|
||||
exists(int i, Callable callable, boolean emptyAp |
|
||||
flowIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
exists(int i, DataFlowCallable callable, boolean emptyAp |
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
@@ -1548,11 +1588,14 @@ private predicate flowIntoCallable(
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if data may flow from `p` to a return statement in the callable. */
|
||||
/** Holds if data may flow from `p` to a return of kind `kind`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configuration config) {
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
config = mid.getConfiguration() and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
@@ -1565,25 +1608,44 @@ private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configu
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to an argument of `methodcall`,
|
||||
* through a called method `m`, and back out through a return statement in
|
||||
* `m`. The context `cc` is restored to its value prior to entering `m`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) {
|
||||
private predicate pathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, methodcall) and
|
||||
paramFlowsThrough(p, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p) and
|
||||
pathIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, kind, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc) and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) {
|
||||
exists(ParameterNode p |
|
||||
flowIntoCallable(mid, p, cc, _, methodcall) and
|
||||
parameterValueFlowsThrough(p)
|
||||
/**
|
||||
* Holds if data may flow from `mid` through a callable to the node `out`.
|
||||
* The context `cc` is restored to its value prior to entering the callable.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
pathThroughCallable0(call, mid, kind, cc) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate valuePathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
pathIntoCallable(mid, p, cc, innercc, call) and
|
||||
parameterValueFlowsThrough(p, kind, innercc)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valuePathThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
valuePathThroughCallable0(call, mid, kind, cc) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
/**
|
||||
* Provides an implementation of global (interprocedural) data flow. This file
|
||||
* re-exports the local (intraprocedural) data flow analysis from `DataFlowUtil`
|
||||
* and adds a global analysis, mainly exposed through the `Configuration` class.
|
||||
* This file exists in several identical copies, allowing queries to use
|
||||
* multiple `Configuration` classes that depend on each other without
|
||||
* introducing mutual recursion among those configurations.
|
||||
* re-exports the local (intraprocedural) data flow analysis from
|
||||
* `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed
|
||||
* through the `Configuration` class. This file exists in several identical
|
||||
* copies, allowing queries to use multiple `Configuration` classes that depend
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowDispatch
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
@@ -95,7 +94,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(Expr sink) { hasFlowTo(exprNode(sink)) }
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/** DEPRECATED: use `hasFlow` instead. */
|
||||
deprecated predicate hasFlowForward(Node source, Node sink) { hasFlow(source, sink) }
|
||||
@@ -114,7 +113,8 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
|
||||
|
||||
pragma[noinline]
|
||||
private predicate isAdditionalFlowStep(
|
||||
Node node1, Node node2, Callable callable1, Callable callable2, Configuration config
|
||||
Node node1, Node node2, DataFlowCallable callable1, DataFlowCallable callable2,
|
||||
Configuration config
|
||||
) {
|
||||
config.isAdditionalFlowStep(node1, node2) and
|
||||
callable1 = node1.getEnclosingCallable() and
|
||||
@@ -125,7 +125,7 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if the additional step from `node1` to `node2` does not jump between callables.
|
||||
*/
|
||||
private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
|
||||
exists(Callable callable | isAdditionalFlowStep(node1, node2, callable, callable, config))
|
||||
exists(DataFlowCallable callable | isAdditionalFlowStep(node1, node2, callable, callable, config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,16 +148,17 @@ private predicate localFlowStep(Node node1, Node node2, boolean preservesValue,
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private Method returnNodeGetEnclosingCallable(ReturnNode ret) {
|
||||
result = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if field flow should be used for the given configuration.
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
|
||||
viableImpl(call) = result.getCallable() and
|
||||
kind = result.getKind()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is reachable from a source in the given configuration
|
||||
* ignoring call contexts.
|
||||
@@ -198,22 +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(Method m, MethodAccess ma, ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
|
||||
nodeCandFwd1(ret, stored, config) and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
m = viableImpl(ma) and
|
||||
node.asExpr() = ma
|
||||
getReturnPosition(ret) = viableReturnPos(call, kind) and
|
||||
node = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -275,22 +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(Method m, ExprNode ma |
|
||||
nodeCand1(ma, stored, config) and
|
||||
m = returnNodeGetEnclosingCallable(node) and
|
||||
m = viableImpl(ma.getExpr())
|
||||
exists(DataFlowCall call, ReturnKind kind, OutNode out |
|
||||
nodeCand1(out, stored, config) and
|
||||
getReturnPosition(node) = viableReturnPos(call, kind) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -316,11 +316,17 @@ private predicate readCand1(Content f, Configuration config) {
|
||||
* from the configuration.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Configuration config) {
|
||||
private predicate simpleParameterFlow(
|
||||
ParameterNode p, Node node, DataFlowType t, Configuration config
|
||||
) {
|
||||
nodeCand1(node, false, config) and
|
||||
p = node and
|
||||
t = getErasedRepr(node.getType()) and
|
||||
not parameterValueFlowsThrough(p)
|
||||
exists(ReturnNode ret, ReturnKind kind |
|
||||
returnNodeGetEnclosingCallable(ret) = p.getEnclosingCallable() and
|
||||
kind = ret.getKind() and
|
||||
not parameterValueFlowsThrough(p, kind, _)
|
||||
)
|
||||
or
|
||||
nodeCand1(node, false, unbind(config)) and
|
||||
exists(Node mid |
|
||||
@@ -347,7 +353,7 @@ private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Con
|
||||
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
|
||||
@@ -359,45 +365,55 @@ private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Con
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, ExprNode call, RefType t, Configuration config
|
||||
pragma[noinline]
|
||||
private predicate simpleArgumentFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode param, ReturnNode ret |
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
nodeCand1(call, false, unbind(config)) and
|
||||
viableParamArg(param, arg) and
|
||||
simpleParameterFlow(param, ret, t, config) and
|
||||
arg.argumentOf(call.getExpr(), _)
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
exists(ParameterNode p, ReturnNode ret |
|
||||
simpleParameterFlow(p, ret, t, config) and
|
||||
kind = ret.getKind() and
|
||||
viableParamArg(call, p, arg)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a method.
|
||||
* Holds if data can flow from `arg` to `out` through a 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 calls that do not
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate flowThroughMethod(
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, Node out, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
nodeCand1(out, false, unbind(config)) and
|
||||
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a callable.
|
||||
*/
|
||||
private predicate flowThroughCallable(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
) {
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
|
||||
or
|
||||
argumentValueFlowsThrough(node1, node2) and preservesValue = true
|
||||
argumentValueFlowsThrough(node1, node2, _) and preservesValue = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in one local step or a step
|
||||
* through a method.
|
||||
* through a callable.
|
||||
*/
|
||||
private predicate localFlowStepOrFlowThroughMethod(
|
||||
private predicate localFlowStepOrFlowThroughCallable(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
) {
|
||||
localFlowStep(node1, node2, preservesValue, config) or
|
||||
flowThroughMethod(node1, node2, preservesValue, config)
|
||||
flowThroughCallable(node1, node2, preservesValue, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,15 +428,13 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
// 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 method
|
||||
exists(Method m, MethodAccess ma, ReturnNode ret |
|
||||
ret = node1 and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
m = viableImpl(ma) and
|
||||
node2.asExpr() = ma
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
getReturnPosition(node1) = viableReturnPos(call, kind) and
|
||||
node2 = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -430,7 +444,7 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
* path from a source to a sink.
|
||||
*/
|
||||
private predicate flowIntoCallable(Node node1, Node node2, Configuration config) {
|
||||
viableParamArg(node2, node1) and
|
||||
viableParamArg(_, node2, node1) and
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config)
|
||||
}
|
||||
@@ -505,7 +519,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
(
|
||||
exists(Node mid, boolean preservesValue |
|
||||
nodeCandFwd2(mid, fromArg, stored, config) and
|
||||
localFlowStepOrFlowThroughMethod(mid, node, preservesValue, config) and
|
||||
localFlowStepOrFlowThroughCallable(mid, node, preservesValue, config) and
|
||||
(stored = false or preservesValue = true)
|
||||
)
|
||||
or
|
||||
@@ -574,7 +588,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
nodeCandFwd2(node, _, unbindBool(stored), unbind(config)) and
|
||||
(
|
||||
exists(Node mid, boolean preservesValue |
|
||||
localFlowStepOrFlowThroughMethod(node, mid, preservesValue, config) and
|
||||
localFlowStepOrFlowThroughCallable(node, mid, preservesValue, config) and
|
||||
nodeCand2(mid, toReturn, stored, config) and
|
||||
(stored = false or preservesValue = true)
|
||||
)
|
||||
@@ -663,10 +677,10 @@ private predicate localFlowEntry(Node node, Configuration config) {
|
||||
config.isSource(node) or
|
||||
jumpStep(_, node, _, config) or
|
||||
node instanceof ParameterNode or
|
||||
node.asExpr() instanceof MethodAccess or
|
||||
node instanceof OutNode or
|
||||
node instanceof PostUpdateNode or
|
||||
read(_, _, node) or
|
||||
node.asExpr() instanceof CastExpr
|
||||
node instanceof CastNode
|
||||
)
|
||||
}
|
||||
|
||||
@@ -679,12 +693,12 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
jumpStep(node, next, _, config) or
|
||||
flowIntoCallable(node, next, config) or
|
||||
flowOutOfCallable(node, next, config) or
|
||||
flowThroughMethod(node, next, _, config) or
|
||||
flowThroughCallable(node, next, _, config) or
|
||||
store(node, _, next) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
node.asExpr() instanceof CastExpr
|
||||
node instanceof CastNode
|
||||
or
|
||||
config.isSink(node)
|
||||
}
|
||||
@@ -706,7 +720,7 @@ private predicate localFlowStepPlus(
|
||||
exists(Node mid, boolean pv1, boolean pv2 |
|
||||
localFlowStepPlus(node1, mid, pv1, config) and
|
||||
localFlowStep(mid, node2, pv2, config) and
|
||||
not mid.asExpr() instanceof CastExpr and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = pv1.booleanAnd(pv2) and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
@@ -725,7 +739,7 @@ private predicate localFlowBigStep(
|
||||
}
|
||||
|
||||
private newtype TAccessPathFront =
|
||||
TFrontNil(Type t) or
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(Content f)
|
||||
|
||||
/**
|
||||
@@ -733,12 +747,12 @@ private newtype TAccessPathFront =
|
||||
*/
|
||||
private class AccessPathFront extends TAccessPathFront {
|
||||
string toString() {
|
||||
exists(Type t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
or
|
||||
exists(Content f | this = TFrontHead(f) | result = f.toString())
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
DataFlowType getType() {
|
||||
this = TFrontNil(result)
|
||||
or
|
||||
exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
|
||||
@@ -755,8 +769,8 @@ private class AccessPathFrontNil extends AccessPathFront, TFrontNil { }
|
||||
private class CastingNode extends Node {
|
||||
CastingNode() {
|
||||
this instanceof ParameterNode or
|
||||
this.asExpr() instanceof CastExpr or
|
||||
this.asExpr() instanceof MethodAccess or
|
||||
this instanceof CastNode or
|
||||
this instanceof OutNode or
|
||||
this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode
|
||||
}
|
||||
}
|
||||
@@ -819,7 +833,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallable(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
or
|
||||
@@ -915,7 +929,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallable(node, mid, preservesValue, config) and
|
||||
flowCand(mid, toReturn, apf0, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
@@ -954,7 +968,7 @@ private predicate consCand(Content f, AccessPathFront apf, Configuration config)
|
||||
}
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(Type t) or
|
||||
TNil(DataFlowType t) or
|
||||
TCons(Content f, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
@@ -975,7 +989,7 @@ private class AccessPath extends TAccessPath {
|
||||
this = TCons(_, result)
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
DataFlowType getType() {
|
||||
this = TNil(result)
|
||||
or
|
||||
exists(Content head | this = TCons(head, _) | result = head.getContainerType())
|
||||
@@ -985,9 +999,11 @@ private class AccessPath extends TAccessPath {
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
override string toString() { exists(Type t | this = TNil(t) | result = ppReprType(t)) }
|
||||
override string toString() { exists(DataFlowType t | this = TNil(t) | result = ppReprType(t)) }
|
||||
|
||||
override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) }
|
||||
override AccessPathFront getFront() {
|
||||
exists(DataFlowType t | this = TNil(t) | result = TFrontNil(t))
|
||||
}
|
||||
}
|
||||
|
||||
private class AccessPathCons extends AccessPath, TCons {
|
||||
@@ -1083,7 +1099,7 @@ private predicate flowFwd0(
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0, AccessPath ap0 |
|
||||
flowFwd(mid, fromArg, apf0, ap0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallable(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0 and apf = apf0
|
||||
or
|
||||
@@ -1196,7 +1212,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPath ap0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallable(node, mid, preservesValue, config) and
|
||||
flow(mid, toReturn, ap0, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0
|
||||
@@ -1255,7 +1271,7 @@ private newtype TPathNode =
|
||||
or
|
||||
// ... or a step from an existing PathNode to another node.
|
||||
exists(PathNodeMid mid |
|
||||
flowStep(mid, node, cc, ap) and
|
||||
pathStep(mid, node, cc, ap) and
|
||||
config = mid.getConfiguration() and
|
||||
flow(node, _, ap, unbind(config))
|
||||
)
|
||||
@@ -1274,8 +1290,14 @@ abstract class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = getNode().toString() + ppAp() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { result = getNode().toString() + ppAp() + ppCtx() }
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() { result = getNode().getLocation() }
|
||||
DataFlowLocation getLocation() { result = getNode().getLocation() }
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
abstract Node getNode();
|
||||
@@ -1284,20 +1306,31 @@ abstract class PathNode extends TPathNode {
|
||||
abstract Configuration getConfiguration();
|
||||
|
||||
/** Gets a successor. */
|
||||
abstract PathNode getSucc();
|
||||
deprecated final PathNode getSucc() { result = this.getASuccessor() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
abstract PathNode getASuccessor();
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
result = " [" + this.(PathNodeMid).getAp().toString() + "]"
|
||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
)
|
||||
}
|
||||
|
||||
private string ppCtx() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getSucc()) }
|
||||
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
|
||||
|
||||
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getSucc() = n2 and reach(n2) }
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -1333,11 +1366,11 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
private PathNodeMid getSuccMid() {
|
||||
flowStep(this, result.getNode(), result.getCallContext(), result.getAp()) and
|
||||
pathStep(this, result.getNode(), result.getCallContext(), result.getAp()) and
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNode getSucc() {
|
||||
override PathNode getASuccessor() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -1391,14 +1424,14 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getSucc() { none() }
|
||||
override PathNode getASuccessor() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
@@ -1421,15 +1454,15 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap))
|
||||
or
|
||||
flowOutOfArgument(mid, node, cc) and ap = mid.getAp()
|
||||
pathOutOfArgument(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
pathIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
or
|
||||
flowOutOfMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
pathOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowThroughMethod(mid, node.asExpr(), cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
pathThroughCallable(mid, node, cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
or
|
||||
valueFlowThroughMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
valuePathThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
@@ -1449,37 +1482,41 @@ private predicate contentStoreStep(
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to an exit of `m` in the context
|
||||
* `innercc`, and the path did not flow through a parameter of `m`.
|
||||
*/
|
||||
private predicate flowOutOfMethod0(PathNodeMid mid, Method m, CallContext innercc) {
|
||||
exists(ReturnNode ret |
|
||||
ret = mid.getNode() and
|
||||
innercc = mid.getCallContext() and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
not innercc instanceof CallContextCall
|
||||
private predicate pathOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
innercc = mid.getCallContext() and
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate pathOutOfCallable1(
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
pathOutOfCallable0(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 `ma`. The last step of this path
|
||||
* is a return from a method and is recorded by `cc`, if needed.
|
||||
* 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 flowOutOfMethod(PathNodeMid mid, MethodAccess ma, CallContext cc) {
|
||||
exists(Method m, CallContext innercc |
|
||||
flowOutOfMethod0(mid, m, innercc) and
|
||||
resolveReturn(innercc, m, ma)
|
||||
|
|
||||
if reducedViableImplInReturn(m, ma) then cc = TReturn(m, ma) else cc = TAnyCallContext()
|
||||
private predicate pathOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnKind kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate flowOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
|
||||
private predicate pathOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
|
||||
exists(
|
||||
PostUpdateNode n, ParameterNode p, Callable callable, CallContext innercc, int i, Call call,
|
||||
ArgumentNode arg
|
||||
PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
|
||||
DataFlowCall call, ArgumentNode arg
|
||||
|
|
||||
mid.getNode() = n and
|
||||
parameterValueFlowsToUpdate(p, n) and
|
||||
@@ -1500,7 +1537,9 @@ private predicate flowOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallCo
|
||||
* Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowIntoArg(PathNodeMid mid, int i, CallContext cc, Call call, boolean emptyAp) {
|
||||
private predicate pathIntoArg(
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, boolean emptyAp
|
||||
) {
|
||||
exists(ArgumentNode arg, AccessPath ap |
|
||||
arg = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -1514,7 +1553,7 @@ private predicate flowIntoArg(PathNodeMid mid, int i, CallContext cc, Call call,
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterCand(Callable callable, int i, Configuration config) {
|
||||
private predicate parameterCand(DataFlowCallable callable, int i, Configuration config) {
|
||||
exists(ParameterNode p |
|
||||
flow(p, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
@@ -1522,10 +1561,11 @@ private predicate parameterCand(Callable callable, int i, Configuration config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallable0(
|
||||
PathNodeMid mid, Callable callable, int i, CallContext outercc, Call call, boolean emptyAp
|
||||
private predicate pathIntoCallable0(
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
boolean emptyAp
|
||||
) {
|
||||
flowIntoArg(mid, i, outercc, call, emptyAp) and
|
||||
pathIntoArg(mid, i, outercc, call, emptyAp) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
parameterCand(callable, any(int j | j <= i and j >= i), mid.getConfiguration())
|
||||
}
|
||||
@@ -1535,11 +1575,11 @@ private predicate flowIntoCallable0(
|
||||
* before and after entering the callable are `outercc` and `innercc`,
|
||||
* respectively.
|
||||
*/
|
||||
private predicate flowIntoCallable(
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, Call call
|
||||
private predicate pathIntoCallable(
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, DataFlowCall call
|
||||
) {
|
||||
exists(int i, Callable callable, boolean emptyAp |
|
||||
flowIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
exists(int i, DataFlowCallable callable, boolean emptyAp |
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
@@ -1548,11 +1588,14 @@ private predicate flowIntoCallable(
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if data may flow from `p` to a return statement in the callable. */
|
||||
/** Holds if data may flow from `p` to a return of kind `kind`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configuration config) {
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
config = mid.getConfiguration() and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
@@ -1565,25 +1608,44 @@ private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configu
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to an argument of `methodcall`,
|
||||
* through a called method `m`, and back out through a return statement in
|
||||
* `m`. The context `cc` is restored to its value prior to entering `m`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) {
|
||||
private predicate pathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, methodcall) and
|
||||
paramFlowsThrough(p, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p) and
|
||||
pathIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, kind, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc) and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) {
|
||||
exists(ParameterNode p |
|
||||
flowIntoCallable(mid, p, cc, _, methodcall) and
|
||||
parameterValueFlowsThrough(p)
|
||||
/**
|
||||
* Holds if data may flow from `mid` through a callable to the node `out`.
|
||||
* The context `cc` is restored to its value prior to entering the callable.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
pathThroughCallable0(call, mid, kind, cc) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate valuePathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
pathIntoCallable(mid, p, cc, innercc, call) and
|
||||
parameterValueFlowsThrough(p, kind, innercc)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valuePathThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
valuePathThroughCallable0(call, mid, kind, cc) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
/**
|
||||
* Provides an implementation of global (interprocedural) data flow. This file
|
||||
* re-exports the local (intraprocedural) data flow analysis from `DataFlowUtil`
|
||||
* and adds a global analysis, mainly exposed through the `Configuration` class.
|
||||
* This file exists in several identical copies, allowing queries to use
|
||||
* multiple `Configuration` classes that depend on each other without
|
||||
* introducing mutual recursion among those configurations.
|
||||
* re-exports the local (intraprocedural) data flow analysis from
|
||||
* `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed
|
||||
* through the `Configuration` class. This file exists in several identical
|
||||
* copies, allowing queries to use multiple `Configuration` classes that depend
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowDispatch
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
@@ -95,7 +94,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(Expr sink) { hasFlowTo(exprNode(sink)) }
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/** DEPRECATED: use `hasFlow` instead. */
|
||||
deprecated predicate hasFlowForward(Node source, Node sink) { hasFlow(source, sink) }
|
||||
@@ -114,7 +113,8 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
|
||||
|
||||
pragma[noinline]
|
||||
private predicate isAdditionalFlowStep(
|
||||
Node node1, Node node2, Callable callable1, Callable callable2, Configuration config
|
||||
Node node1, Node node2, DataFlowCallable callable1, DataFlowCallable callable2,
|
||||
Configuration config
|
||||
) {
|
||||
config.isAdditionalFlowStep(node1, node2) and
|
||||
callable1 = node1.getEnclosingCallable() and
|
||||
@@ -125,7 +125,7 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if the additional step from `node1` to `node2` does not jump between callables.
|
||||
*/
|
||||
private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
|
||||
exists(Callable callable | isAdditionalFlowStep(node1, node2, callable, callable, config))
|
||||
exists(DataFlowCallable callable | isAdditionalFlowStep(node1, node2, callable, callable, config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,16 +148,17 @@ private predicate localFlowStep(Node node1, Node node2, boolean preservesValue,
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private Method returnNodeGetEnclosingCallable(ReturnNode ret) {
|
||||
result = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if field flow should be used for the given configuration.
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
|
||||
viableImpl(call) = result.getCallable() and
|
||||
kind = result.getKind()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is reachable from a source in the given configuration
|
||||
* ignoring call contexts.
|
||||
@@ -198,22 +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(Method m, MethodAccess ma, ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
|
||||
nodeCandFwd1(ret, stored, config) and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
m = viableImpl(ma) and
|
||||
node.asExpr() = ma
|
||||
getReturnPosition(ret) = viableReturnPos(call, kind) and
|
||||
node = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -275,22 +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(Method m, ExprNode ma |
|
||||
nodeCand1(ma, stored, config) and
|
||||
m = returnNodeGetEnclosingCallable(node) and
|
||||
m = viableImpl(ma.getExpr())
|
||||
exists(DataFlowCall call, ReturnKind kind, OutNode out |
|
||||
nodeCand1(out, stored, config) and
|
||||
getReturnPosition(node) = viableReturnPos(call, kind) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -316,11 +316,17 @@ private predicate readCand1(Content f, Configuration config) {
|
||||
* from the configuration.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Configuration config) {
|
||||
private predicate simpleParameterFlow(
|
||||
ParameterNode p, Node node, DataFlowType t, Configuration config
|
||||
) {
|
||||
nodeCand1(node, false, config) and
|
||||
p = node and
|
||||
t = getErasedRepr(node.getType()) and
|
||||
not parameterValueFlowsThrough(p)
|
||||
exists(ReturnNode ret, ReturnKind kind |
|
||||
returnNodeGetEnclosingCallable(ret) = p.getEnclosingCallable() and
|
||||
kind = ret.getKind() and
|
||||
not parameterValueFlowsThrough(p, kind, _)
|
||||
)
|
||||
or
|
||||
nodeCand1(node, false, unbind(config)) and
|
||||
exists(Node mid |
|
||||
@@ -347,7 +353,7 @@ private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Con
|
||||
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
|
||||
@@ -359,45 +365,55 @@ private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Con
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, ExprNode call, RefType t, Configuration config
|
||||
pragma[noinline]
|
||||
private predicate simpleArgumentFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode param, ReturnNode ret |
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
nodeCand1(call, false, unbind(config)) and
|
||||
viableParamArg(param, arg) and
|
||||
simpleParameterFlow(param, ret, t, config) and
|
||||
arg.argumentOf(call.getExpr(), _)
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
exists(ParameterNode p, ReturnNode ret |
|
||||
simpleParameterFlow(p, ret, t, config) and
|
||||
kind = ret.getKind() and
|
||||
viableParamArg(call, p, arg)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a method.
|
||||
* Holds if data can flow from `arg` to `out` through a 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 calls that do not
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate flowThroughMethod(
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, Node out, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
nodeCand1(out, false, unbind(config)) and
|
||||
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a callable.
|
||||
*/
|
||||
private predicate flowThroughCallable(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
) {
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
|
||||
or
|
||||
argumentValueFlowsThrough(node1, node2) and preservesValue = true
|
||||
argumentValueFlowsThrough(node1, node2, _) and preservesValue = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in one local step or a step
|
||||
* through a method.
|
||||
* through a callable.
|
||||
*/
|
||||
private predicate localFlowStepOrFlowThroughMethod(
|
||||
private predicate localFlowStepOrFlowThroughCallable(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
) {
|
||||
localFlowStep(node1, node2, preservesValue, config) or
|
||||
flowThroughMethod(node1, node2, preservesValue, config)
|
||||
flowThroughCallable(node1, node2, preservesValue, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,15 +428,13 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
// 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 method
|
||||
exists(Method m, MethodAccess ma, ReturnNode ret |
|
||||
ret = node1 and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
m = viableImpl(ma) and
|
||||
node2.asExpr() = ma
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
getReturnPosition(node1) = viableReturnPos(call, kind) and
|
||||
node2 = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -430,7 +444,7 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
* path from a source to a sink.
|
||||
*/
|
||||
private predicate flowIntoCallable(Node node1, Node node2, Configuration config) {
|
||||
viableParamArg(node2, node1) and
|
||||
viableParamArg(_, node2, node1) and
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config)
|
||||
}
|
||||
@@ -505,7 +519,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
(
|
||||
exists(Node mid, boolean preservesValue |
|
||||
nodeCandFwd2(mid, fromArg, stored, config) and
|
||||
localFlowStepOrFlowThroughMethod(mid, node, preservesValue, config) and
|
||||
localFlowStepOrFlowThroughCallable(mid, node, preservesValue, config) and
|
||||
(stored = false or preservesValue = true)
|
||||
)
|
||||
or
|
||||
@@ -574,7 +588,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
nodeCandFwd2(node, _, unbindBool(stored), unbind(config)) and
|
||||
(
|
||||
exists(Node mid, boolean preservesValue |
|
||||
localFlowStepOrFlowThroughMethod(node, mid, preservesValue, config) and
|
||||
localFlowStepOrFlowThroughCallable(node, mid, preservesValue, config) and
|
||||
nodeCand2(mid, toReturn, stored, config) and
|
||||
(stored = false or preservesValue = true)
|
||||
)
|
||||
@@ -663,10 +677,10 @@ private predicate localFlowEntry(Node node, Configuration config) {
|
||||
config.isSource(node) or
|
||||
jumpStep(_, node, _, config) or
|
||||
node instanceof ParameterNode or
|
||||
node.asExpr() instanceof MethodAccess or
|
||||
node instanceof OutNode or
|
||||
node instanceof PostUpdateNode or
|
||||
read(_, _, node) or
|
||||
node.asExpr() instanceof CastExpr
|
||||
node instanceof CastNode
|
||||
)
|
||||
}
|
||||
|
||||
@@ -679,12 +693,12 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
jumpStep(node, next, _, config) or
|
||||
flowIntoCallable(node, next, config) or
|
||||
flowOutOfCallable(node, next, config) or
|
||||
flowThroughMethod(node, next, _, config) or
|
||||
flowThroughCallable(node, next, _, config) or
|
||||
store(node, _, next) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
node.asExpr() instanceof CastExpr
|
||||
node instanceof CastNode
|
||||
or
|
||||
config.isSink(node)
|
||||
}
|
||||
@@ -706,7 +720,7 @@ private predicate localFlowStepPlus(
|
||||
exists(Node mid, boolean pv1, boolean pv2 |
|
||||
localFlowStepPlus(node1, mid, pv1, config) and
|
||||
localFlowStep(mid, node2, pv2, config) and
|
||||
not mid.asExpr() instanceof CastExpr and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = pv1.booleanAnd(pv2) and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
@@ -725,7 +739,7 @@ private predicate localFlowBigStep(
|
||||
}
|
||||
|
||||
private newtype TAccessPathFront =
|
||||
TFrontNil(Type t) or
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(Content f)
|
||||
|
||||
/**
|
||||
@@ -733,12 +747,12 @@ private newtype TAccessPathFront =
|
||||
*/
|
||||
private class AccessPathFront extends TAccessPathFront {
|
||||
string toString() {
|
||||
exists(Type t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
or
|
||||
exists(Content f | this = TFrontHead(f) | result = f.toString())
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
DataFlowType getType() {
|
||||
this = TFrontNil(result)
|
||||
or
|
||||
exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
|
||||
@@ -755,8 +769,8 @@ private class AccessPathFrontNil extends AccessPathFront, TFrontNil { }
|
||||
private class CastingNode extends Node {
|
||||
CastingNode() {
|
||||
this instanceof ParameterNode or
|
||||
this.asExpr() instanceof CastExpr or
|
||||
this.asExpr() instanceof MethodAccess or
|
||||
this instanceof CastNode or
|
||||
this instanceof OutNode or
|
||||
this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode
|
||||
}
|
||||
}
|
||||
@@ -819,7 +833,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallable(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
or
|
||||
@@ -915,7 +929,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallable(node, mid, preservesValue, config) and
|
||||
flowCand(mid, toReturn, apf0, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
@@ -954,7 +968,7 @@ private predicate consCand(Content f, AccessPathFront apf, Configuration config)
|
||||
}
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(Type t) or
|
||||
TNil(DataFlowType t) or
|
||||
TCons(Content f, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
@@ -975,7 +989,7 @@ private class AccessPath extends TAccessPath {
|
||||
this = TCons(_, result)
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
DataFlowType getType() {
|
||||
this = TNil(result)
|
||||
or
|
||||
exists(Content head | this = TCons(head, _) | result = head.getContainerType())
|
||||
@@ -985,9 +999,11 @@ private class AccessPath extends TAccessPath {
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
override string toString() { exists(Type t | this = TNil(t) | result = ppReprType(t)) }
|
||||
override string toString() { exists(DataFlowType t | this = TNil(t) | result = ppReprType(t)) }
|
||||
|
||||
override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) }
|
||||
override AccessPathFront getFront() {
|
||||
exists(DataFlowType t | this = TNil(t) | result = TFrontNil(t))
|
||||
}
|
||||
}
|
||||
|
||||
private class AccessPathCons extends AccessPath, TCons {
|
||||
@@ -1083,7 +1099,7 @@ private predicate flowFwd0(
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0, AccessPath ap0 |
|
||||
flowFwd(mid, fromArg, apf0, ap0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallable(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0 and apf = apf0
|
||||
or
|
||||
@@ -1196,7 +1212,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPath ap0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallable(node, mid, preservesValue, config) and
|
||||
flow(mid, toReturn, ap0, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0
|
||||
@@ -1255,7 +1271,7 @@ private newtype TPathNode =
|
||||
or
|
||||
// ... or a step from an existing PathNode to another node.
|
||||
exists(PathNodeMid mid |
|
||||
flowStep(mid, node, cc, ap) and
|
||||
pathStep(mid, node, cc, ap) and
|
||||
config = mid.getConfiguration() and
|
||||
flow(node, _, ap, unbind(config))
|
||||
)
|
||||
@@ -1274,8 +1290,14 @@ abstract class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = getNode().toString() + ppAp() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { result = getNode().toString() + ppAp() + ppCtx() }
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() { result = getNode().getLocation() }
|
||||
DataFlowLocation getLocation() { result = getNode().getLocation() }
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
abstract Node getNode();
|
||||
@@ -1284,20 +1306,31 @@ abstract class PathNode extends TPathNode {
|
||||
abstract Configuration getConfiguration();
|
||||
|
||||
/** Gets a successor. */
|
||||
abstract PathNode getSucc();
|
||||
deprecated final PathNode getSucc() { result = this.getASuccessor() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
abstract PathNode getASuccessor();
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
result = " [" + this.(PathNodeMid).getAp().toString() + "]"
|
||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
)
|
||||
}
|
||||
|
||||
private string ppCtx() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getSucc()) }
|
||||
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
|
||||
|
||||
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getSucc() = n2 and reach(n2) }
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -1333,11 +1366,11 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
private PathNodeMid getSuccMid() {
|
||||
flowStep(this, result.getNode(), result.getCallContext(), result.getAp()) and
|
||||
pathStep(this, result.getNode(), result.getCallContext(), result.getAp()) and
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNode getSucc() {
|
||||
override PathNode getASuccessor() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -1391,14 +1424,14 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getSucc() { none() }
|
||||
override PathNode getASuccessor() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
@@ -1421,15 +1454,15 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap))
|
||||
or
|
||||
flowOutOfArgument(mid, node, cc) and ap = mid.getAp()
|
||||
pathOutOfArgument(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
pathIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
or
|
||||
flowOutOfMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
pathOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowThroughMethod(mid, node.asExpr(), cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
pathThroughCallable(mid, node, cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
or
|
||||
valueFlowThroughMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
valuePathThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
@@ -1449,37 +1482,41 @@ private predicate contentStoreStep(
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to an exit of `m` in the context
|
||||
* `innercc`, and the path did not flow through a parameter of `m`.
|
||||
*/
|
||||
private predicate flowOutOfMethod0(PathNodeMid mid, Method m, CallContext innercc) {
|
||||
exists(ReturnNode ret |
|
||||
ret = mid.getNode() and
|
||||
innercc = mid.getCallContext() and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
not innercc instanceof CallContextCall
|
||||
private predicate pathOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
innercc = mid.getCallContext() and
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate pathOutOfCallable1(
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
pathOutOfCallable0(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 `ma`. The last step of this path
|
||||
* is a return from a method and is recorded by `cc`, if needed.
|
||||
* 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 flowOutOfMethod(PathNodeMid mid, MethodAccess ma, CallContext cc) {
|
||||
exists(Method m, CallContext innercc |
|
||||
flowOutOfMethod0(mid, m, innercc) and
|
||||
resolveReturn(innercc, m, ma)
|
||||
|
|
||||
if reducedViableImplInReturn(m, ma) then cc = TReturn(m, ma) else cc = TAnyCallContext()
|
||||
private predicate pathOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnKind kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate flowOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
|
||||
private predicate pathOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
|
||||
exists(
|
||||
PostUpdateNode n, ParameterNode p, Callable callable, CallContext innercc, int i, Call call,
|
||||
ArgumentNode arg
|
||||
PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
|
||||
DataFlowCall call, ArgumentNode arg
|
||||
|
|
||||
mid.getNode() = n and
|
||||
parameterValueFlowsToUpdate(p, n) and
|
||||
@@ -1500,7 +1537,9 @@ private predicate flowOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallCo
|
||||
* Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowIntoArg(PathNodeMid mid, int i, CallContext cc, Call call, boolean emptyAp) {
|
||||
private predicate pathIntoArg(
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, boolean emptyAp
|
||||
) {
|
||||
exists(ArgumentNode arg, AccessPath ap |
|
||||
arg = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -1514,7 +1553,7 @@ private predicate flowIntoArg(PathNodeMid mid, int i, CallContext cc, Call call,
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterCand(Callable callable, int i, Configuration config) {
|
||||
private predicate parameterCand(DataFlowCallable callable, int i, Configuration config) {
|
||||
exists(ParameterNode p |
|
||||
flow(p, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
@@ -1522,10 +1561,11 @@ private predicate parameterCand(Callable callable, int i, Configuration config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallable0(
|
||||
PathNodeMid mid, Callable callable, int i, CallContext outercc, Call call, boolean emptyAp
|
||||
private predicate pathIntoCallable0(
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
boolean emptyAp
|
||||
) {
|
||||
flowIntoArg(mid, i, outercc, call, emptyAp) and
|
||||
pathIntoArg(mid, i, outercc, call, emptyAp) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
parameterCand(callable, any(int j | j <= i and j >= i), mid.getConfiguration())
|
||||
}
|
||||
@@ -1535,11 +1575,11 @@ private predicate flowIntoCallable0(
|
||||
* before and after entering the callable are `outercc` and `innercc`,
|
||||
* respectively.
|
||||
*/
|
||||
private predicate flowIntoCallable(
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, Call call
|
||||
private predicate pathIntoCallable(
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, DataFlowCall call
|
||||
) {
|
||||
exists(int i, Callable callable, boolean emptyAp |
|
||||
flowIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
exists(int i, DataFlowCallable callable, boolean emptyAp |
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
@@ -1548,11 +1588,14 @@ private predicate flowIntoCallable(
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if data may flow from `p` to a return statement in the callable. */
|
||||
/** Holds if data may flow from `p` to a return of kind `kind`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configuration config) {
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
config = mid.getConfiguration() and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
@@ -1565,25 +1608,44 @@ private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configu
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to an argument of `methodcall`,
|
||||
* through a called method `m`, and back out through a return statement in
|
||||
* `m`. The context `cc` is restored to its value prior to entering `m`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) {
|
||||
private predicate pathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, methodcall) and
|
||||
paramFlowsThrough(p, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p) and
|
||||
pathIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, kind, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc) and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) {
|
||||
exists(ParameterNode p |
|
||||
flowIntoCallable(mid, p, cc, _, methodcall) and
|
||||
parameterValueFlowsThrough(p)
|
||||
/**
|
||||
* Holds if data may flow from `mid` through a callable to the node `out`.
|
||||
* The context `cc` is restored to its value prior to entering the callable.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
pathThroughCallable0(call, mid, kind, cc) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate valuePathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
pathIntoCallable(mid, p, cc, innercc, call) and
|
||||
parameterValueFlowsThrough(p, kind, innercc)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valuePathThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
valuePathThroughCallable0(call, mid, kind, cc) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
/**
|
||||
* Provides an implementation of global (interprocedural) data flow. This file
|
||||
* re-exports the local (intraprocedural) data flow analysis from `DataFlowUtil`
|
||||
* and adds a global analysis, mainly exposed through the `Configuration` class.
|
||||
* This file exists in several identical copies, allowing queries to use
|
||||
* multiple `Configuration` classes that depend on each other without
|
||||
* introducing mutual recursion among those configurations.
|
||||
* re-exports the local (intraprocedural) data flow analysis from
|
||||
* `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed
|
||||
* through the `Configuration` class. This file exists in several identical
|
||||
* copies, allowing queries to use multiple `Configuration` classes that depend
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowDispatch
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
@@ -95,7 +94,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(Expr sink) { hasFlowTo(exprNode(sink)) }
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/** DEPRECATED: use `hasFlow` instead. */
|
||||
deprecated predicate hasFlowForward(Node source, Node sink) { hasFlow(source, sink) }
|
||||
@@ -114,7 +113,8 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
|
||||
|
||||
pragma[noinline]
|
||||
private predicate isAdditionalFlowStep(
|
||||
Node node1, Node node2, Callable callable1, Callable callable2, Configuration config
|
||||
Node node1, Node node2, DataFlowCallable callable1, DataFlowCallable callable2,
|
||||
Configuration config
|
||||
) {
|
||||
config.isAdditionalFlowStep(node1, node2) and
|
||||
callable1 = node1.getEnclosingCallable() and
|
||||
@@ -125,7 +125,7 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if the additional step from `node1` to `node2` does not jump between callables.
|
||||
*/
|
||||
private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
|
||||
exists(Callable callable | isAdditionalFlowStep(node1, node2, callable, callable, config))
|
||||
exists(DataFlowCallable callable | isAdditionalFlowStep(node1, node2, callable, callable, config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,16 +148,17 @@ private predicate localFlowStep(Node node1, Node node2, boolean preservesValue,
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private Method returnNodeGetEnclosingCallable(ReturnNode ret) {
|
||||
result = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if field flow should be used for the given configuration.
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
|
||||
viableImpl(call) = result.getCallable() and
|
||||
kind = result.getKind()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is reachable from a source in the given configuration
|
||||
* ignoring call contexts.
|
||||
@@ -198,22 +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(Method m, MethodAccess ma, ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
|
||||
nodeCandFwd1(ret, stored, config) and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
m = viableImpl(ma) and
|
||||
node.asExpr() = ma
|
||||
getReturnPosition(ret) = viableReturnPos(call, kind) and
|
||||
node = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -275,22 +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(Method m, ExprNode ma |
|
||||
nodeCand1(ma, stored, config) and
|
||||
m = returnNodeGetEnclosingCallable(node) and
|
||||
m = viableImpl(ma.getExpr())
|
||||
exists(DataFlowCall call, ReturnKind kind, OutNode out |
|
||||
nodeCand1(out, stored, config) and
|
||||
getReturnPosition(node) = viableReturnPos(call, kind) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -316,11 +316,17 @@ private predicate readCand1(Content f, Configuration config) {
|
||||
* from the configuration.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Configuration config) {
|
||||
private predicate simpleParameterFlow(
|
||||
ParameterNode p, Node node, DataFlowType t, Configuration config
|
||||
) {
|
||||
nodeCand1(node, false, config) and
|
||||
p = node and
|
||||
t = getErasedRepr(node.getType()) and
|
||||
not parameterValueFlowsThrough(p)
|
||||
exists(ReturnNode ret, ReturnKind kind |
|
||||
returnNodeGetEnclosingCallable(ret) = p.getEnclosingCallable() and
|
||||
kind = ret.getKind() and
|
||||
not parameterValueFlowsThrough(p, kind, _)
|
||||
)
|
||||
or
|
||||
nodeCand1(node, false, unbind(config)) and
|
||||
exists(Node mid |
|
||||
@@ -347,7 +353,7 @@ private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Con
|
||||
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
|
||||
@@ -359,45 +365,55 @@ private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Con
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, ExprNode call, RefType t, Configuration config
|
||||
pragma[noinline]
|
||||
private predicate simpleArgumentFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode param, ReturnNode ret |
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
nodeCand1(call, false, unbind(config)) and
|
||||
viableParamArg(param, arg) and
|
||||
simpleParameterFlow(param, ret, t, config) and
|
||||
arg.argumentOf(call.getExpr(), _)
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
exists(ParameterNode p, ReturnNode ret |
|
||||
simpleParameterFlow(p, ret, t, config) and
|
||||
kind = ret.getKind() and
|
||||
viableParamArg(call, p, arg)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a method.
|
||||
* Holds if data can flow from `arg` to `out` through a 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 calls that do not
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate flowThroughMethod(
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, Node out, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
nodeCand1(out, false, unbind(config)) and
|
||||
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a callable.
|
||||
*/
|
||||
private predicate flowThroughCallable(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
) {
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
|
||||
or
|
||||
argumentValueFlowsThrough(node1, node2) and preservesValue = true
|
||||
argumentValueFlowsThrough(node1, node2, _) and preservesValue = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in one local step or a step
|
||||
* through a method.
|
||||
* through a callable.
|
||||
*/
|
||||
private predicate localFlowStepOrFlowThroughMethod(
|
||||
private predicate localFlowStepOrFlowThroughCallable(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
) {
|
||||
localFlowStep(node1, node2, preservesValue, config) or
|
||||
flowThroughMethod(node1, node2, preservesValue, config)
|
||||
flowThroughCallable(node1, node2, preservesValue, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,15 +428,13 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
// 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 method
|
||||
exists(Method m, MethodAccess ma, ReturnNode ret |
|
||||
ret = node1 and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
m = viableImpl(ma) and
|
||||
node2.asExpr() = ma
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
getReturnPosition(node1) = viableReturnPos(call, kind) and
|
||||
node2 = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -430,7 +444,7 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
* path from a source to a sink.
|
||||
*/
|
||||
private predicate flowIntoCallable(Node node1, Node node2, Configuration config) {
|
||||
viableParamArg(node2, node1) and
|
||||
viableParamArg(_, node2, node1) and
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config)
|
||||
}
|
||||
@@ -505,7 +519,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
(
|
||||
exists(Node mid, boolean preservesValue |
|
||||
nodeCandFwd2(mid, fromArg, stored, config) and
|
||||
localFlowStepOrFlowThroughMethod(mid, node, preservesValue, config) and
|
||||
localFlowStepOrFlowThroughCallable(mid, node, preservesValue, config) and
|
||||
(stored = false or preservesValue = true)
|
||||
)
|
||||
or
|
||||
@@ -574,7 +588,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
nodeCandFwd2(node, _, unbindBool(stored), unbind(config)) and
|
||||
(
|
||||
exists(Node mid, boolean preservesValue |
|
||||
localFlowStepOrFlowThroughMethod(node, mid, preservesValue, config) and
|
||||
localFlowStepOrFlowThroughCallable(node, mid, preservesValue, config) and
|
||||
nodeCand2(mid, toReturn, stored, config) and
|
||||
(stored = false or preservesValue = true)
|
||||
)
|
||||
@@ -663,10 +677,10 @@ private predicate localFlowEntry(Node node, Configuration config) {
|
||||
config.isSource(node) or
|
||||
jumpStep(_, node, _, config) or
|
||||
node instanceof ParameterNode or
|
||||
node.asExpr() instanceof MethodAccess or
|
||||
node instanceof OutNode or
|
||||
node instanceof PostUpdateNode or
|
||||
read(_, _, node) or
|
||||
node.asExpr() instanceof CastExpr
|
||||
node instanceof CastNode
|
||||
)
|
||||
}
|
||||
|
||||
@@ -679,12 +693,12 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
jumpStep(node, next, _, config) or
|
||||
flowIntoCallable(node, next, config) or
|
||||
flowOutOfCallable(node, next, config) or
|
||||
flowThroughMethod(node, next, _, config) or
|
||||
flowThroughCallable(node, next, _, config) or
|
||||
store(node, _, next) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
node.asExpr() instanceof CastExpr
|
||||
node instanceof CastNode
|
||||
or
|
||||
config.isSink(node)
|
||||
}
|
||||
@@ -706,7 +720,7 @@ private predicate localFlowStepPlus(
|
||||
exists(Node mid, boolean pv1, boolean pv2 |
|
||||
localFlowStepPlus(node1, mid, pv1, config) and
|
||||
localFlowStep(mid, node2, pv2, config) and
|
||||
not mid.asExpr() instanceof CastExpr and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = pv1.booleanAnd(pv2) and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
@@ -725,7 +739,7 @@ private predicate localFlowBigStep(
|
||||
}
|
||||
|
||||
private newtype TAccessPathFront =
|
||||
TFrontNil(Type t) or
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(Content f)
|
||||
|
||||
/**
|
||||
@@ -733,12 +747,12 @@ private newtype TAccessPathFront =
|
||||
*/
|
||||
private class AccessPathFront extends TAccessPathFront {
|
||||
string toString() {
|
||||
exists(Type t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
or
|
||||
exists(Content f | this = TFrontHead(f) | result = f.toString())
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
DataFlowType getType() {
|
||||
this = TFrontNil(result)
|
||||
or
|
||||
exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
|
||||
@@ -755,8 +769,8 @@ private class AccessPathFrontNil extends AccessPathFront, TFrontNil { }
|
||||
private class CastingNode extends Node {
|
||||
CastingNode() {
|
||||
this instanceof ParameterNode or
|
||||
this.asExpr() instanceof CastExpr or
|
||||
this.asExpr() instanceof MethodAccess or
|
||||
this instanceof CastNode or
|
||||
this instanceof OutNode or
|
||||
this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode
|
||||
}
|
||||
}
|
||||
@@ -819,7 +833,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallable(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
or
|
||||
@@ -915,7 +929,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallable(node, mid, preservesValue, config) and
|
||||
flowCand(mid, toReturn, apf0, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
@@ -954,7 +968,7 @@ private predicate consCand(Content f, AccessPathFront apf, Configuration config)
|
||||
}
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(Type t) or
|
||||
TNil(DataFlowType t) or
|
||||
TCons(Content f, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
@@ -975,7 +989,7 @@ private class AccessPath extends TAccessPath {
|
||||
this = TCons(_, result)
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
DataFlowType getType() {
|
||||
this = TNil(result)
|
||||
or
|
||||
exists(Content head | this = TCons(head, _) | result = head.getContainerType())
|
||||
@@ -985,9 +999,11 @@ private class AccessPath extends TAccessPath {
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
override string toString() { exists(Type t | this = TNil(t) | result = ppReprType(t)) }
|
||||
override string toString() { exists(DataFlowType t | this = TNil(t) | result = ppReprType(t)) }
|
||||
|
||||
override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) }
|
||||
override AccessPathFront getFront() {
|
||||
exists(DataFlowType t | this = TNil(t) | result = TFrontNil(t))
|
||||
}
|
||||
}
|
||||
|
||||
private class AccessPathCons extends AccessPath, TCons {
|
||||
@@ -1083,7 +1099,7 @@ private predicate flowFwd0(
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0, AccessPath ap0 |
|
||||
flowFwd(mid, fromArg, apf0, ap0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallable(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0 and apf = apf0
|
||||
or
|
||||
@@ -1196,7 +1212,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPath ap0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallable(node, mid, preservesValue, config) and
|
||||
flow(mid, toReturn, ap0, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0
|
||||
@@ -1255,7 +1271,7 @@ private newtype TPathNode =
|
||||
or
|
||||
// ... or a step from an existing PathNode to another node.
|
||||
exists(PathNodeMid mid |
|
||||
flowStep(mid, node, cc, ap) and
|
||||
pathStep(mid, node, cc, ap) and
|
||||
config = mid.getConfiguration() and
|
||||
flow(node, _, ap, unbind(config))
|
||||
)
|
||||
@@ -1274,8 +1290,14 @@ abstract class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = getNode().toString() + ppAp() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { result = getNode().toString() + ppAp() + ppCtx() }
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() { result = getNode().getLocation() }
|
||||
DataFlowLocation getLocation() { result = getNode().getLocation() }
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
abstract Node getNode();
|
||||
@@ -1284,20 +1306,31 @@ abstract class PathNode extends TPathNode {
|
||||
abstract Configuration getConfiguration();
|
||||
|
||||
/** Gets a successor. */
|
||||
abstract PathNode getSucc();
|
||||
deprecated final PathNode getSucc() { result = this.getASuccessor() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
abstract PathNode getASuccessor();
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
result = " [" + this.(PathNodeMid).getAp().toString() + "]"
|
||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
)
|
||||
}
|
||||
|
||||
private string ppCtx() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getSucc()) }
|
||||
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
|
||||
|
||||
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getSucc() = n2 and reach(n2) }
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -1333,11 +1366,11 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
private PathNodeMid getSuccMid() {
|
||||
flowStep(this, result.getNode(), result.getCallContext(), result.getAp()) and
|
||||
pathStep(this, result.getNode(), result.getCallContext(), result.getAp()) and
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNode getSucc() {
|
||||
override PathNode getASuccessor() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -1391,14 +1424,14 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getSucc() { none() }
|
||||
override PathNode getASuccessor() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
@@ -1421,15 +1454,15 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap))
|
||||
or
|
||||
flowOutOfArgument(mid, node, cc) and ap = mid.getAp()
|
||||
pathOutOfArgument(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
pathIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
or
|
||||
flowOutOfMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
pathOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowThroughMethod(mid, node.asExpr(), cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
pathThroughCallable(mid, node, cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
or
|
||||
valueFlowThroughMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
valuePathThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
@@ -1449,37 +1482,41 @@ private predicate contentStoreStep(
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to an exit of `m` in the context
|
||||
* `innercc`, and the path did not flow through a parameter of `m`.
|
||||
*/
|
||||
private predicate flowOutOfMethod0(PathNodeMid mid, Method m, CallContext innercc) {
|
||||
exists(ReturnNode ret |
|
||||
ret = mid.getNode() and
|
||||
innercc = mid.getCallContext() and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
not innercc instanceof CallContextCall
|
||||
private predicate pathOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
innercc = mid.getCallContext() and
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate pathOutOfCallable1(
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
pathOutOfCallable0(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 `ma`. The last step of this path
|
||||
* is a return from a method and is recorded by `cc`, if needed.
|
||||
* 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 flowOutOfMethod(PathNodeMid mid, MethodAccess ma, CallContext cc) {
|
||||
exists(Method m, CallContext innercc |
|
||||
flowOutOfMethod0(mid, m, innercc) and
|
||||
resolveReturn(innercc, m, ma)
|
||||
|
|
||||
if reducedViableImplInReturn(m, ma) then cc = TReturn(m, ma) else cc = TAnyCallContext()
|
||||
private predicate pathOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnKind kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate flowOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
|
||||
private predicate pathOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
|
||||
exists(
|
||||
PostUpdateNode n, ParameterNode p, Callable callable, CallContext innercc, int i, Call call,
|
||||
ArgumentNode arg
|
||||
PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
|
||||
DataFlowCall call, ArgumentNode arg
|
||||
|
|
||||
mid.getNode() = n and
|
||||
parameterValueFlowsToUpdate(p, n) and
|
||||
@@ -1500,7 +1537,9 @@ private predicate flowOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallCo
|
||||
* Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowIntoArg(PathNodeMid mid, int i, CallContext cc, Call call, boolean emptyAp) {
|
||||
private predicate pathIntoArg(
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, boolean emptyAp
|
||||
) {
|
||||
exists(ArgumentNode arg, AccessPath ap |
|
||||
arg = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -1514,7 +1553,7 @@ private predicate flowIntoArg(PathNodeMid mid, int i, CallContext cc, Call call,
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterCand(Callable callable, int i, Configuration config) {
|
||||
private predicate parameterCand(DataFlowCallable callable, int i, Configuration config) {
|
||||
exists(ParameterNode p |
|
||||
flow(p, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
@@ -1522,10 +1561,11 @@ private predicate parameterCand(Callable callable, int i, Configuration config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallable0(
|
||||
PathNodeMid mid, Callable callable, int i, CallContext outercc, Call call, boolean emptyAp
|
||||
private predicate pathIntoCallable0(
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
boolean emptyAp
|
||||
) {
|
||||
flowIntoArg(mid, i, outercc, call, emptyAp) and
|
||||
pathIntoArg(mid, i, outercc, call, emptyAp) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
parameterCand(callable, any(int j | j <= i and j >= i), mid.getConfiguration())
|
||||
}
|
||||
@@ -1535,11 +1575,11 @@ private predicate flowIntoCallable0(
|
||||
* before and after entering the callable are `outercc` and `innercc`,
|
||||
* respectively.
|
||||
*/
|
||||
private predicate flowIntoCallable(
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, Call call
|
||||
private predicate pathIntoCallable(
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, DataFlowCall call
|
||||
) {
|
||||
exists(int i, Callable callable, boolean emptyAp |
|
||||
flowIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
exists(int i, DataFlowCallable callable, boolean emptyAp |
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
@@ -1548,11 +1588,14 @@ private predicate flowIntoCallable(
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if data may flow from `p` to a return statement in the callable. */
|
||||
/** Holds if data may flow from `p` to a return of kind `kind`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configuration config) {
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
config = mid.getConfiguration() and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
@@ -1565,25 +1608,44 @@ private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configu
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to an argument of `methodcall`,
|
||||
* through a called method `m`, and back out through a return statement in
|
||||
* `m`. The context `cc` is restored to its value prior to entering `m`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) {
|
||||
private predicate pathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, methodcall) and
|
||||
paramFlowsThrough(p, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p) and
|
||||
pathIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, kind, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc) and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) {
|
||||
exists(ParameterNode p |
|
||||
flowIntoCallable(mid, p, cc, _, methodcall) and
|
||||
parameterValueFlowsThrough(p)
|
||||
/**
|
||||
* Holds if data may flow from `mid` through a callable to the node `out`.
|
||||
* The context `cc` is restored to its value prior to entering the callable.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
pathThroughCallable0(call, mid, kind, cc) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate valuePathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
pathIntoCallable(mid, p, cc, innercc, call) and
|
||||
parameterValueFlowsThrough(p, kind, innercc)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valuePathThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
valuePathThroughCallable0(call, mid, kind, cc) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowDispatch
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
private ReturnNode getAReturnNodeOfKind(ReturnKind kind) { result.getKind() = kind }
|
||||
|
||||
cached
|
||||
private module ImplCommon {
|
||||
@@ -9,19 +10,17 @@ private module ImplCommon {
|
||||
* The instance parameter is considered to have index `-1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate viableParam(Call call, int i, ParameterNode p) {
|
||||
exists(Callable callable |
|
||||
callable = viableCallable(call) and
|
||||
p.isParameterOf(callable, i)
|
||||
)
|
||||
private predicate viableParam(DataFlowCall call, int i, ParameterNode p) {
|
||||
p.isParameterOf(viableCallable(call), i)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, Call call |
|
||||
predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
|
||||
exists(int i |
|
||||
viableParam(call, i, p) and
|
||||
arg.argumentOf(call, i)
|
||||
)
|
||||
@@ -29,44 +28,167 @@ private module ImplCommon {
|
||||
|
||||
/**
|
||||
* 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 `ReturnNode` 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) {
|
||||
exists(ReturnNode ret | parameterValueFlow(p, ret))
|
||||
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 `call` 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
|
||||
argumentValueFlowsThroughNoCtx(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, _, _) |
|
||||
reducedViableImplInCallContext(_, c, 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)
|
||||
)
|
||||
}
|
||||
|
||||
private CallContextCall getAValidCallContextForParameter(ParameterNode p) {
|
||||
result = TSomeCall(p, _)
|
||||
or
|
||||
exists(DataFlowCall call, int i, DataFlowCallable callable |
|
||||
result = TSpecificCall(call, i, _) and
|
||||
p.isParameterOf(callable, i) and
|
||||
reducedViableImplInCallContext(_, callable, 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())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, ExprNode call) {
|
||||
exists(ParameterNode param |
|
||||
viableParamArg(param, arg) and
|
||||
parameterValueFlowsThrough(param) and
|
||||
arg.argumentOf(call.getExpr(), _) and
|
||||
compatibleTypes(arg.getType(), call.getType())
|
||||
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) and
|
||||
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())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -76,7 +198,7 @@ private module ImplCommon {
|
||||
*/
|
||||
cached
|
||||
predicate parameterValueFlowsToUpdate(ParameterNode p, PostUpdateNode n) {
|
||||
parameterValueFlow(p, n.getPreUpdateNode())
|
||||
parameterValueFlowNoCtx(p, n.getPreUpdateNode())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,7 +207,7 @@ private module ImplCommon {
|
||||
*/
|
||||
private predicate localValueStep(Node node1, Node node2) {
|
||||
localFlowStep(node1, node2) or
|
||||
argumentValueFlowsThrough(node1, node2)
|
||||
argumentValueFlowsThrough(node1, node2, _)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -113,7 +235,7 @@ private module ImplCommon {
|
||||
private predicate storeViaSideEffect(Node node1, Content f, PostUpdateNode node2) {
|
||||
storeStep(node1, f, node2) and readStep(_, f, _)
|
||||
or
|
||||
exists(Call call, int i1, int i2 |
|
||||
exists(DataFlowCall call, int i1, int i2 |
|
||||
setterCall(call, i1, i2, f) and
|
||||
node1.(ArgumentNode).argumentOf(call, i1) and
|
||||
node2.getPreUpdateNode().(ArgumentNode).argumentOf(call, i2) and
|
||||
@@ -125,16 +247,16 @@ 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
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate setterCall(Call call, int i1, int i2, Content f) {
|
||||
exists(Callable callable, ParameterNode p1, ParameterNode p2 |
|
||||
private predicate setterCall(DataFlowCall call, int i1, int i2, Content f) {
|
||||
exists(DataFlowCallable callable, ParameterNode p1, ParameterNode p2 |
|
||||
setterInParam(p1, f, p2) and
|
||||
callable = viableCallable(call) and
|
||||
p1.isParameterOf(callable, i1) and
|
||||
@@ -142,22 +264,36 @@ 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 |
|
||||
arg = node1 and
|
||||
viableParamArg(p, arg) and
|
||||
setterReturn(p, f) and
|
||||
arg.argumentOf(node2.asExpr(), _) and
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
storeReturn0(call, kind, node1, f) and
|
||||
node2 = getAnOutNode(call, kind) and
|
||||
compatibleTypes(node1.getTypeBound(), f.getType()) and
|
||||
compatibleTypes(node2.getTypeBound(), f.getContainerType())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate setterReturn(ParameterNode p, Content f) {
|
||||
exists(Node n1, Node n2, ReturnNode ret |
|
||||
parameterValueFlow(p, n1) and
|
||||
private predicate setterReturn(ParameterNode p, Content f, ReturnKind kind) {
|
||||
exists(Node n1, Node n2 |
|
||||
parameterValueFlowNoCtx(p, n1) and
|
||||
store(n1, f, n2) and
|
||||
localValueStep*(n2, ret)
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -169,21 +305,19 @@ private module ImplCommon {
|
||||
predicate read(Node node1, Content f, Node node2) {
|
||||
readStep(node1, f, node2) and storeStep(_, f, _)
|
||||
or
|
||||
exists(ParameterNode p, ArgumentNode arg |
|
||||
arg = node1 and
|
||||
viableParamArg(p, arg) and
|
||||
getter(p, f) and
|
||||
arg.argumentOf(node2.asExpr(), _) and
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
read0(call, kind, node1, f) and
|
||||
node2 = getAnOutNode(call, kind) and
|
||||
compatibleTypes(node1.getTypeBound(), f.getContainerType()) and
|
||||
compatibleTypes(node2.getTypeBound(), f.getType())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate getter(ParameterNode p, Content f) {
|
||||
exists(Node n1, Node n2, ReturnNode ret |
|
||||
parameterValueFlow(p, n1) and
|
||||
private predicate getter(ParameterNode p, Content f, ReturnKind kind) {
|
||||
exists(Node n1, Node n2 |
|
||||
parameterValueFlowNoCtx(p, n1) and
|
||||
read(n1, f, n2) and
|
||||
localValueStep*(n2, ret)
|
||||
localValueStep*(n2, getAReturnNodeOfKind(kind))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -200,14 +334,14 @@ private module ImplCommon {
|
||||
* Holds if `call` passes an implicit or explicit instance argument, i.e., an
|
||||
* expression that reaches a `this` parameter.
|
||||
*/
|
||||
private predicate callHasInstanceArgument(Call call) {
|
||||
private predicate callHasInstanceArgument(DataFlowCall call) {
|
||||
exists(ArgumentNode arg | arg.argumentOf(call, -1))
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TCallContext =
|
||||
TAnyCallContext() or
|
||||
TSpecificCall(Call call, int i, boolean emptyAp) {
|
||||
TSpecificCall(DataFlowCall call, int i, boolean emptyAp) {
|
||||
reducedViableImplInCallContext(_, _, call) and
|
||||
(emptyAp = true or emptyAp = false) and
|
||||
(
|
||||
@@ -217,24 +351,34 @@ private module ImplCommon {
|
||||
)
|
||||
} or
|
||||
TSomeCall(ParameterNode p, boolean emptyAp) { emptyAp = true or emptyAp = false } or
|
||||
TReturn(Method m, MethodAccess ma) { reducedViableImplInReturn(m, ma) }
|
||||
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.
|
||||
*
|
||||
* There are four cases:
|
||||
* - `TAnyCallContext()` : No restrictions on method flow.
|
||||
* - `TSpecificCall(Call call, int i)` : Flow entered through the `i`th
|
||||
* - `TSpecificCall(DataFlowCall call, int i)` : Flow entered through the `i`th
|
||||
* parameter at the given `call`. This call improves the set of viable
|
||||
* dispatch targets for at least one method call in the current callable.
|
||||
* - `TSomeCall(ParameterNode p)` : Flow entered through parameter `p`. The
|
||||
* originating call does not improve the set of dispatch targets for any
|
||||
* method call in the current callable and was therefore not recorded.
|
||||
* - `TReturn(Method m, MethodAccess ma)` : Flow reached `ma` from `m` and
|
||||
* this dispatch target of `ma` implies a reduced set of dispatch origins
|
||||
* - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and
|
||||
* this dispatch target of `call` implies a reduced set of dispatch origins
|
||||
* to which data may flow if it should reach a `return` statement.
|
||||
*/
|
||||
abstract class CallContext extends TCallContext {
|
||||
@@ -248,7 +392,11 @@ class CallContextAny extends CallContext, TAnyCallContext {
|
||||
abstract class CallContextCall extends CallContext { }
|
||||
|
||||
class CallContextSpecificCall extends CallContextCall, TSpecificCall {
|
||||
override string toString() { result = "CcCall" }
|
||||
override string toString() {
|
||||
exists(DataFlowCall call, int i | this = TSpecificCall(call, i, _) |
|
||||
result = "CcCall(" + call + ", " + i + ")"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class CallContextSomeCall extends CallContextCall, TSomeCall {
|
||||
@@ -256,23 +404,55 @@ class CallContextSomeCall extends CallContextCall, TSomeCall {
|
||||
}
|
||||
|
||||
class CallContextReturn extends CallContext, TReturn {
|
||||
override string toString() { result = "CcReturn" }
|
||||
override string toString() {
|
||||
exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
|
||||
}
|
||||
}
|
||||
|
||||
/** 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, Callable callable, Call call) {
|
||||
predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
|
||||
cc instanceof CallContextAny and callable = viableCallable(call)
|
||||
or
|
||||
exists(Method m0, MethodAccess ma0 |
|
||||
ma0.getEnclosingCallable() = callable and
|
||||
cc = TReturn(m0, ma0) and
|
||||
m0 = prunedViableImplInCallContextReverse(ma0, call)
|
||||
exists(DataFlowCallable c0, DataFlowCall call0 |
|
||||
call0.getEnclosingCallable() = callable and
|
||||
cc = TReturn(c0, call0) and
|
||||
c0 = prunedViableImplInCallContextReverse(call0, call)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[call, cc]
|
||||
Callable resolveCall(Call call, CallContext cc) {
|
||||
exists(Call ctx | cc = TSpecificCall(ctx, _, _) |
|
||||
DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
|
||||
exists(DataFlowCall ctx | cc = TSpecificCall(ctx, _, _) |
|
||||
if reducedViableImplInCallContext(call, _, ctx)
|
||||
then result = prunedViableImplInCallContext(call, ctx)
|
||||
else result = viableCallable(call)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Provides C++-specific definitions for use in the data flow library.
|
||||
*/
|
||||
module Private {
|
||||
import DataFlowPrivate
|
||||
import DataFlowDispatch
|
||||
}
|
||||
|
||||
module Public {
|
||||
import DataFlowUtil
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
private import cpp
|
||||
private import DataFlowUtil
|
||||
private import DataFlowDispatch
|
||||
|
||||
/** Gets the instance argument of a non-static call. */
|
||||
private Node getInstanceArgument(Call call) {
|
||||
@@ -8,6 +9,21 @@ private Node getInstanceArgument(Call call) {
|
||||
// base class destructor calls as those do not have an AST element.
|
||||
}
|
||||
|
||||
/** An argument to a call. */
|
||||
private class Argument extends Expr {
|
||||
Call call;
|
||||
|
||||
int pos;
|
||||
|
||||
Argument() { call.getArgument(pos) = this }
|
||||
|
||||
/** Gets the call that has this argument. */
|
||||
Call getCall() { result = call }
|
||||
|
||||
/** Gets the position of this argument. */
|
||||
int getPosition() { result = pos }
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that occurs as the argument of a call and is passed as-is
|
||||
* to the callable. Arguments that are wrapped in an implicit varargs array
|
||||
@@ -24,17 +40,50 @@ class ArgumentNode extends Node {
|
||||
* Holds if this argument occurs at the given position in the given call.
|
||||
* The instance argument is considered to have index `-1`.
|
||||
*/
|
||||
predicate argumentOf(Call call, int pos) {
|
||||
exists(Argument arg | this.asExpr() = arg | call = arg.getCall() and pos = arg.getPosition()) or
|
||||
predicate argumentOf(DataFlowCall call, int pos) {
|
||||
exists(Argument arg | this.asExpr() = arg | call = arg.getCall() and pos = arg.getPosition())
|
||||
or
|
||||
pos = -1 and this = getInstanceArgument(call)
|
||||
}
|
||||
|
||||
/** Gets the call in which this node is an argument. */
|
||||
DataFlowCall getCall() { this.argumentOf(result, _) }
|
||||
}
|
||||
|
||||
private newtype TReturnKind = TNormalReturnKind()
|
||||
|
||||
/**
|
||||
* 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 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())
|
||||
}
|
||||
ReturnNode() { exists(ReturnStmt ret | this.getExpr() = ret.getExpr()) }
|
||||
|
||||
/** Gets the kind of this returned value. */
|
||||
ReturnKind getKind() { result = TNormalReturnKind() }
|
||||
}
|
||||
|
||||
/** A data flow node that represents the output of a call. */
|
||||
class OutNode extends ExprNode {
|
||||
OutNode() { this.getExpr() instanceof Call }
|
||||
|
||||
/** Gets the underlying call. */
|
||||
DataFlowCall getCall() { result = this.getExpr() }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,9 +91,7 @@ class ReturnNode extends ExprNode {
|
||||
* calling context. For example, this would happen with flow through a
|
||||
* global or static variable.
|
||||
*/
|
||||
predicate jumpStep(Node n1, Node n2) {
|
||||
none()
|
||||
}
|
||||
predicate jumpStep(Node n1, Node n2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `call` passes an implicit or explicit qualifier, i.e., a
|
||||
@@ -56,7 +103,10 @@ predicate callHasQualifier(Call call) {
|
||||
call.getTarget() instanceof Destructor
|
||||
}
|
||||
|
||||
private newtype TContent = TFieldContent(Field f) or TCollectionContent() or TArrayContent()
|
||||
private newtype TContent =
|
||||
TFieldContent(Field f) or
|
||||
TCollectionContent() or
|
||||
TArrayContent()
|
||||
|
||||
/**
|
||||
* A reference contained in an object. Examples include instance fields, the
|
||||
@@ -65,33 +115,49 @@ private newtype TContent = TFieldContent(Field f) or TCollectionContent() or TAr
|
||||
class Content extends TContent {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
|
||||
}
|
||||
|
||||
/** Gets the type of the object containing this content. */
|
||||
abstract RefType getContainerType();
|
||||
abstract Type getContainerType();
|
||||
|
||||
/** Gets the type of this content. */
|
||||
abstract Type getType();
|
||||
}
|
||||
|
||||
private class FieldContent extends Content, TFieldContent {
|
||||
Field f;
|
||||
|
||||
FieldContent() { this = TFieldContent(f) }
|
||||
|
||||
Field getField() { result = f }
|
||||
|
||||
override string toString() { result = f.toString() }
|
||||
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
f.getLocation().hasLocationInfo(path, sl, sc, el, ec)
|
||||
}
|
||||
override RefType getContainerType() { result = f.getDeclaringType() }
|
||||
|
||||
override Type getContainerType() { result = f.getDeclaringType() }
|
||||
|
||||
override Type getType() { result = f.getType() }
|
||||
}
|
||||
|
||||
private class CollectionContent extends Content, TCollectionContent {
|
||||
override string toString() { result = "collection" }
|
||||
override RefType getContainerType() { none() }
|
||||
|
||||
override Type getContainerType() { none() }
|
||||
|
||||
override Type getType() { none() }
|
||||
}
|
||||
|
||||
private class ArrayContent extends Content, TArrayContent {
|
||||
override string toString() { result = "array" }
|
||||
override RefType getContainerType() { none() }
|
||||
|
||||
override Type getContainerType() { none() }
|
||||
|
||||
override Type getType() { none() }
|
||||
}
|
||||
|
||||
@@ -118,15 +184,13 @@ predicate readStep(Node node1, Content f, Node node2) {
|
||||
* possible flow. A single type is used for all numeric types to account for
|
||||
* numeric conversions, and otherwise the erasure is used.
|
||||
*/
|
||||
RefType getErasedRepr(Type t) {
|
||||
Type getErasedRepr(Type t) {
|
||||
suppressUnusedType(t) and
|
||||
result instanceof VoidType // stub implementation
|
||||
}
|
||||
|
||||
/** Gets a string representation of a type returned by `getErasedRepr`. */
|
||||
string ppReprType(Type t) {
|
||||
result = t.toString()
|
||||
}
|
||||
string ppReprType(Type t) { result = t.toString() }
|
||||
|
||||
/**
|
||||
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
|
||||
@@ -142,49 +206,33 @@ private predicate suppressUnusedType(Type t) { any() }
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Java QL library compatibility wrappers
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class RefType extends Type {
|
||||
/** A node that performs a type cast. */
|
||||
class CastNode extends Node {
|
||||
CastNode() { none() } // stub implementation
|
||||
}
|
||||
|
||||
class CastExpr extends Expr {
|
||||
CastExpr() { none() } // stub implementation
|
||||
}
|
||||
class DataFlowCallable = Function;
|
||||
|
||||
/** An argument to a call. */
|
||||
class Argument extends Expr {
|
||||
Call call;
|
||||
int pos;
|
||||
class DataFlowExpr = Expr;
|
||||
|
||||
Argument() {
|
||||
call.getArgument(pos) = this
|
||||
}
|
||||
class DataFlowType = Type;
|
||||
|
||||
/** Gets the call that has this argument. */
|
||||
Call getCall() { result = call }
|
||||
class DataFlowLocation = Location;
|
||||
|
||||
/** Gets the position of this argument. */
|
||||
int getPosition() {
|
||||
result = pos
|
||||
}
|
||||
}
|
||||
/** A function call relevant for data flow. */
|
||||
class DataFlowCall extends Expr {
|
||||
DataFlowCall() { this instanceof Call }
|
||||
|
||||
class Callable extends Function { }
|
||||
|
||||
/**
|
||||
* An alias for `Function` in the C++ library. In the Java library, a `Method`
|
||||
* is any callable except a constructor.
|
||||
*/
|
||||
class Method extends Function { }
|
||||
|
||||
/**
|
||||
* An alias for `FunctionCall` in the C++ library. In the Java library, a
|
||||
* `MethodAccess` is any `Call` that does not call a constructor.
|
||||
*/
|
||||
class MethodAccess extends FunctionCall {
|
||||
/**
|
||||
* INTERNAL: Do not use. Alternative name for `getEnclosingFunction`.
|
||||
* Gets the nth argument for this call.
|
||||
*
|
||||
* The range of `n` is from `0` to `getNumberOfArguments() - 1`.
|
||||
*/
|
||||
Callable getEnclosingCallable() {
|
||||
result = this.getEnclosingFunction()
|
||||
}
|
||||
Expr getArgument(int n) { result = this.(Call).getArgument(n) }
|
||||
|
||||
/** Gets the data flow node corresponding to this call. */
|
||||
ExprNode getNode() { result.getExpr() = this }
|
||||
|
||||
/** Gets the enclosing callable of this call. */
|
||||
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Provides C++-specific definitions for use in the data flow library.
|
||||
*/
|
||||
import cpp
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.dataflow.internal.FlowVar
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
|
||||
@@ -12,9 +13,7 @@ private newtype TNode =
|
||||
TDefinitionByReferenceNode(VariableAccess va, Expr argument) {
|
||||
definitionByReference(va, argument)
|
||||
} or
|
||||
TUninitializedNode(LocalVariable v) {
|
||||
not v.hasInitializer()
|
||||
}
|
||||
TUninitializedNode(LocalVariable v) { not v.hasInitializer() }
|
||||
|
||||
/**
|
||||
* A node in a data flow graph.
|
||||
@@ -22,7 +21,7 @@ private newtype TNode =
|
||||
* A node can be either an expression, a parameter, or an uninitialized local
|
||||
* variable. Such nodes are created with `DataFlow::exprNode`,
|
||||
* `DataFlow::parameterNode`, and `DataFlow::uninitializedNode` respectively.
|
||||
*/
|
||||
*/
|
||||
class Node extends TNode {
|
||||
/** Gets the function to which this node belongs. */
|
||||
Function getFunction() { none() } // overridden in subclasses
|
||||
@@ -30,9 +29,7 @@ class Node extends TNode {
|
||||
/**
|
||||
* INTERNAL: Do not use. Alternative name for `getFunction`.
|
||||
*/
|
||||
Function getEnclosingCallable() {
|
||||
result = this.getFunction()
|
||||
}
|
||||
Function getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
/** Gets the type of this node. */
|
||||
Type getType() { none() } // overridden in subclasses
|
||||
@@ -50,9 +47,7 @@ class Node extends TNode {
|
||||
* Gets the uninitialized local variable corresponding to this node, if
|
||||
* any.
|
||||
*/
|
||||
LocalVariable asUninitialized() {
|
||||
result = this.(UninitializedNode).getLocalVariable()
|
||||
}
|
||||
LocalVariable asUninitialized() { result = this.(UninitializedNode).getLocalVariable() }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() } // overridden by subclasses
|
||||
@@ -71,11 +66,17 @@ class Node extends TNode {
|
||||
*/
|
||||
class ExprNode extends Node, TExprNode {
|
||||
Expr expr;
|
||||
|
||||
ExprNode() { this = TExprNode(expr) }
|
||||
|
||||
override Function getFunction() { result = expr.getEnclosingFunction() }
|
||||
|
||||
override Type getType() { result = expr.getType() }
|
||||
|
||||
override string toString() { result = expr.toString() }
|
||||
|
||||
override Location getLocation() { result = expr.getLocation() }
|
||||
|
||||
/** Gets the expression corresponding to this node. */
|
||||
Expr getExpr() { result = expr }
|
||||
}
|
||||
@@ -86,20 +87,25 @@ class ExprNode extends Node, TExprNode {
|
||||
*/
|
||||
class ParameterNode extends Node, TParameterNode {
|
||||
Parameter param;
|
||||
|
||||
ParameterNode() { this = TParameterNode(param) }
|
||||
|
||||
override Function getFunction() { result = param.getFunction() }
|
||||
|
||||
override Type getType() { result = param.getType() }
|
||||
|
||||
override string toString() { result = param.toString() }
|
||||
|
||||
override Location getLocation() { result = param.getLocation() }
|
||||
|
||||
/** Gets the parameter corresponding to this node. */
|
||||
Parameter getParameter() { result = param }
|
||||
|
||||
/**
|
||||
* Holds if this node is the parameter of `c` at the specified (zero-based)
|
||||
* position. The implicit `this` parameter is considered to have index `-1`.
|
||||
*/
|
||||
predicate isParameterOf(Function f, int i) {
|
||||
f.getParameter(i) = param
|
||||
}
|
||||
predicate isParameterOf(Function f, int i) { f.getParameter(i) = param }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,15 +119,22 @@ class ParameterNode extends Node, TParameterNode {
|
||||
*/
|
||||
class DefinitionByReferenceNode extends Node, TDefinitionByReferenceNode {
|
||||
VariableAccess va;
|
||||
|
||||
Expr argument;
|
||||
|
||||
DefinitionByReferenceNode() { this = TDefinitionByReferenceNode(va, argument) }
|
||||
|
||||
override Function getFunction() { result = va.getEnclosingFunction() }
|
||||
|
||||
override Type getType() { result = va.getType() }
|
||||
|
||||
override string toString() { result = "ref arg " + argument.toString() }
|
||||
|
||||
override Location getLocation() { result = argument.getLocation() }
|
||||
|
||||
/** Gets the argument corresponding to this node. */
|
||||
Expr getArgument() { result = argument }
|
||||
|
||||
/** Gets the parameter through which this value is assigned. */
|
||||
Parameter getParameter() {
|
||||
exists(FunctionCall call, int i |
|
||||
@@ -137,11 +150,17 @@ class DefinitionByReferenceNode extends Node, TDefinitionByReferenceNode {
|
||||
*/
|
||||
class UninitializedNode extends Node, TUninitializedNode {
|
||||
LocalVariable v;
|
||||
|
||||
UninitializedNode() { this = TUninitializedNode(v) }
|
||||
|
||||
override Function getFunction() { result = v.getFunction() }
|
||||
|
||||
override Type getType() { result = v.getType() }
|
||||
|
||||
override string toString() { result = v.toString() }
|
||||
|
||||
override Location getLocation() { result = v.getLocation() }
|
||||
|
||||
/** Gets the uninitialized local variable corresponding to this node. */
|
||||
LocalVariable getLocalVariable() { result = v }
|
||||
}
|
||||
@@ -160,6 +179,7 @@ class UninitializedNode extends Node, TUninitializedNode {
|
||||
*/
|
||||
class PostUpdateNode extends Node {
|
||||
PostUpdateNode() { none() } // stub implementation
|
||||
|
||||
/**
|
||||
* Gets the node before the state update.
|
||||
*/
|
||||
@@ -188,9 +208,7 @@ DefinitionByReferenceNode definitionByReferenceNodeFromArgument(Expr argument) {
|
||||
* Gets the `Node` corresponding to the value of an uninitialized local
|
||||
* variable `v`.
|
||||
*/
|
||||
UninitializedNode uninitializedNode(LocalVariable v) {
|
||||
result.getLocalVariable() = v
|
||||
}
|
||||
UninitializedNode uninitializedNode(LocalVariable v) { result.getLocalVariable() = v }
|
||||
|
||||
/**
|
||||
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
|
||||
@@ -223,16 +241,12 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
* Holds if data flows from `source` to `sink` in zero or more local
|
||||
* (intra-procedural) steps.
|
||||
*/
|
||||
predicate localFlow(Node source, Node sink) {
|
||||
localFlowStep*(source, sink)
|
||||
}
|
||||
predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
|
||||
|
||||
/**
|
||||
* Holds if the initial value of `v`, if it is a source, flows to `var`.
|
||||
*/
|
||||
private predicate varSourceBaseCase(FlowVar var, Variable v) {
|
||||
var.definedByInitialValue(v)
|
||||
}
|
||||
private predicate varSourceBaseCase(FlowVar var, Variable v) { var.definedByInitialValue(v) }
|
||||
|
||||
/**
|
||||
* Holds if `var` is defined by an assignment-like operation that causes flow
|
||||
@@ -249,9 +263,7 @@ private predicate exprToVarStep(Expr assignedExpr, FlowVar var) {
|
||||
/**
|
||||
* Holds if the expression `e` is an access of the variable `var`.
|
||||
*/
|
||||
private predicate varToExprStep(FlowVar var, Expr e) {
|
||||
e = var.getAnAccess()
|
||||
}
|
||||
private predicate varToExprStep(FlowVar var, Expr e) { e = var.getAnAccess() }
|
||||
|
||||
/**
|
||||
* Holds if data flows from `fromExpr` to `toExpr` directly, in the case
|
||||
@@ -259,35 +271,25 @@ private predicate varToExprStep(FlowVar var, Expr e) {
|
||||
* data flows from `x` and `y` to `b ? x : y`.
|
||||
*/
|
||||
private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) {
|
||||
toExpr = any(ConditionalExpr cond |
|
||||
fromExpr = cond.getThen() or fromExpr = cond.getElse()
|
||||
)
|
||||
toExpr = any(ConditionalExpr cond | fromExpr = cond.getThen() or fromExpr = cond.getElse())
|
||||
or
|
||||
toExpr = any(AssignExpr assign |
|
||||
fromExpr = assign.getRValue()
|
||||
)
|
||||
toExpr = any(AssignExpr assign | fromExpr = assign.getRValue())
|
||||
or
|
||||
toExpr = any(CommaExpr comma |
|
||||
fromExpr = comma.getRightOperand()
|
||||
)
|
||||
toExpr = any(CommaExpr comma | fromExpr = comma.getRightOperand())
|
||||
or
|
||||
toExpr = any(PostfixCrementOperation op |
|
||||
fromExpr = op.getOperand()
|
||||
)
|
||||
toExpr = any(PostfixCrementOperation op | fromExpr = op.getOperand())
|
||||
or
|
||||
toExpr = any(StmtExpr stmtExpr |
|
||||
fromExpr = stmtExpr.getResultExpr()
|
||||
)
|
||||
toExpr = any(StmtExpr stmtExpr | fromExpr = stmtExpr.getResultExpr())
|
||||
or
|
||||
toExpr = any(Call call |
|
||||
exists(DataFlowFunction f, FunctionInput inModel , FunctionOutput outModel, int iIn |
|
||||
call.getTarget() = f and
|
||||
f.hasDataFlow(inModel, outModel) and
|
||||
outModel.isOutReturnValue() and
|
||||
inModel.isInParameter(iIn) and
|
||||
fromExpr = call.getArgument(iIn)
|
||||
exists(DataFlowFunction f, FunctionInput inModel, FunctionOutput outModel, int iIn |
|
||||
call.getTarget() = f and
|
||||
f.hasDataFlow(inModel, outModel) and
|
||||
outModel.isOutReturnValue() and
|
||||
inModel.isInParameter(iIn) and
|
||||
fromExpr = call.getArgument(iIn)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
|
||||
@@ -295,9 +297,7 @@ private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
|
||||
call.getTarget() = f and
|
||||
argOut = call.getArgument(argOutIndex) and
|
||||
outModel.isOutParameterPointer(argOutIndex) and
|
||||
exists(int argInIndex, FunctionInput inModel |
|
||||
f.hasDataFlow(inModel, outModel)
|
||||
|
|
||||
exists(int argInIndex, FunctionInput inModel | f.hasDataFlow(inModel, outModel) |
|
||||
inModel.isInParameterPointer(argInIndex) and
|
||||
call.passesByReference(argInIndex, exprIn)
|
||||
or
|
||||
@@ -308,9 +308,11 @@ private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
|
||||
}
|
||||
|
||||
VariableAccess getAnAccessToAssignedVariable(Expr assign) {
|
||||
(assign instanceof Assignment
|
||||
or
|
||||
assign instanceof CrementOperation) and
|
||||
(
|
||||
assign instanceof Assignment
|
||||
or
|
||||
assign instanceof CrementOperation
|
||||
) and
|
||||
exists(FlowVar var |
|
||||
var.definedByExpr(_, assign) and
|
||||
result = var.getAnAccess()
|
||||
|
||||
@@ -1 +1,80 @@
|
||||
import semmle.code.cpp.dataflow.internal.DataFlowDispatch
|
||||
private import semmle.code.cpp.ir.IR
|
||||
|
||||
Function viableImpl(CallInstruction call) { result = viableCallable(call) }
|
||||
|
||||
/**
|
||||
* Gets a function that might be called by `call`.
|
||||
*/
|
||||
Function viableCallable(CallInstruction call) {
|
||||
result = call.getStaticCallTarget()
|
||||
or
|
||||
// If the target of the call does not have a body in the snapshot, it might
|
||||
// be because the target is just a header declaration, and the real target
|
||||
// will be determined at run time when the caller and callee are linked
|
||||
// together by the operating system's dynamic linker. In case a _unique_
|
||||
// function with the right signature is present in the database, we return
|
||||
// that as a potential callee.
|
||||
exists(string qualifiedName, int nparams |
|
||||
callSignatureWithoutBody(qualifiedName, nparams, call) and
|
||||
functionSignatureWithBody(qualifiedName, nparams, result) and
|
||||
strictcount(Function other | functionSignatureWithBody(qualifiedName, nparams, other)) = 1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is a function with a body that has name `qualifiedName` and
|
||||
* `nparams` parameter count. See `functionSignature`.
|
||||
*/
|
||||
private predicate functionSignatureWithBody(string qualifiedName, int nparams, Function f) {
|
||||
functionSignature(f, qualifiedName, nparams) and
|
||||
exists(f.getBlock())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the target of `call` is a function _with no definition_ that has
|
||||
* name `qualifiedName` and `nparams` parameter count. See `functionSignature`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate callSignatureWithoutBody(string qualifiedName, int nparams, CallInstruction call) {
|
||||
exists(Function target |
|
||||
target = call.getStaticCallTarget() and
|
||||
not exists(target.getBlock()) and
|
||||
functionSignature(target, qualifiedName, nparams)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` has name `qualifiedName` and `nparams` parameter count. This is
|
||||
* an approximation of its signature for the purpose of matching functions that
|
||||
* might be the same across link targets.
|
||||
*/
|
||||
private predicate functionSignature(Function f, string qualifiedName, int nparams) {
|
||||
qualifiedName = f.getQualifiedName() and
|
||||
nparams = f.getNumberOfParameters() and
|
||||
not f.isStatic()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `ctx` reduces the set of viable dispatch
|
||||
* targets of `ma` in `c`.
|
||||
*/
|
||||
predicate reducedViableImplInCallContext(CallInstruction call, Function f, CallInstruction ctx) { none() }
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
|
||||
* restricted to those `ma`s for which the context makes a difference.
|
||||
*/
|
||||
Function prunedViableImplInCallContext(CallInstruction call, CallInstruction ctx) { none() }
|
||||
|
||||
/**
|
||||
* Holds if flow returning from `m` to `ma` might return further and if
|
||||
* this path restricts the set of call sites that can be returned to.
|
||||
*/
|
||||
predicate reducedViableImplInReturn(Function f, CallInstruction call) { none() }
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
|
||||
* restricted to those `ma`s and results for which the return flow from the
|
||||
* result to `ma` restricts the possible context `ctx`.
|
||||
*/
|
||||
Function prunedViableImplInCallContextReverse(CallInstruction call, CallInstruction ctx) { none() }
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
/**
|
||||
* Provides an implementation of global (interprocedural) data flow. This file
|
||||
* re-exports the local (intraprocedural) data flow analysis from `DataFlowUtil`
|
||||
* and adds a global analysis, mainly exposed through the `Configuration` class.
|
||||
* This file exists in several identical copies, allowing queries to use
|
||||
* multiple `Configuration` classes that depend on each other without
|
||||
* introducing mutual recursion among those configurations.
|
||||
* re-exports the local (intraprocedural) data flow analysis from
|
||||
* `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed
|
||||
* through the `Configuration` class. This file exists in several identical
|
||||
* copies, allowing queries to use multiple `Configuration` classes that depend
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowDispatch
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
@@ -95,7 +94,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(Expr sink) { hasFlowTo(exprNode(sink)) }
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/** DEPRECATED: use `hasFlow` instead. */
|
||||
deprecated predicate hasFlowForward(Node source, Node sink) { hasFlow(source, sink) }
|
||||
@@ -114,7 +113,8 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
|
||||
|
||||
pragma[noinline]
|
||||
private predicate isAdditionalFlowStep(
|
||||
Node node1, Node node2, Callable callable1, Callable callable2, Configuration config
|
||||
Node node1, Node node2, DataFlowCallable callable1, DataFlowCallable callable2,
|
||||
Configuration config
|
||||
) {
|
||||
config.isAdditionalFlowStep(node1, node2) and
|
||||
callable1 = node1.getEnclosingCallable() and
|
||||
@@ -125,7 +125,7 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if the additional step from `node1` to `node2` does not jump between callables.
|
||||
*/
|
||||
private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
|
||||
exists(Callable callable | isAdditionalFlowStep(node1, node2, callable, callable, config))
|
||||
exists(DataFlowCallable callable | isAdditionalFlowStep(node1, node2, callable, callable, config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,16 +148,17 @@ private predicate localFlowStep(Node node1, Node node2, boolean preservesValue,
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private Method returnNodeGetEnclosingCallable(ReturnNode ret) {
|
||||
result = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if field flow should be used for the given configuration.
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
|
||||
viableImpl(call) = result.getCallable() and
|
||||
kind = result.getKind()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is reachable from a source in the given configuration
|
||||
* ignoring call contexts.
|
||||
@@ -198,22 +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(Method m, MethodAccess ma, ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
|
||||
nodeCandFwd1(ret, stored, config) and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
m = viableImpl(ma) and
|
||||
node.asExpr() = ma
|
||||
getReturnPosition(ret) = viableReturnPos(call, kind) and
|
||||
node = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -275,22 +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(Method m, ExprNode ma |
|
||||
nodeCand1(ma, stored, config) and
|
||||
m = returnNodeGetEnclosingCallable(node) and
|
||||
m = viableImpl(ma.getExpr())
|
||||
exists(DataFlowCall call, ReturnKind kind, OutNode out |
|
||||
nodeCand1(out, stored, config) and
|
||||
getReturnPosition(node) = viableReturnPos(call, kind) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -316,11 +316,17 @@ private predicate readCand1(Content f, Configuration config) {
|
||||
* from the configuration.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Configuration config) {
|
||||
private predicate simpleParameterFlow(
|
||||
ParameterNode p, Node node, DataFlowType t, Configuration config
|
||||
) {
|
||||
nodeCand1(node, false, config) and
|
||||
p = node and
|
||||
t = getErasedRepr(node.getType()) and
|
||||
not parameterValueFlowsThrough(p)
|
||||
exists(ReturnNode ret, ReturnKind kind |
|
||||
returnNodeGetEnclosingCallable(ret) = p.getEnclosingCallable() and
|
||||
kind = ret.getKind() and
|
||||
not parameterValueFlowsThrough(p, kind, _)
|
||||
)
|
||||
or
|
||||
nodeCand1(node, false, unbind(config)) and
|
||||
exists(Node mid |
|
||||
@@ -347,7 +353,7 @@ private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Con
|
||||
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
|
||||
@@ -359,45 +365,55 @@ private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Con
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, ExprNode call, RefType t, Configuration config
|
||||
pragma[noinline]
|
||||
private predicate simpleArgumentFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode param, ReturnNode ret |
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
nodeCand1(call, false, unbind(config)) and
|
||||
viableParamArg(param, arg) and
|
||||
simpleParameterFlow(param, ret, t, config) and
|
||||
arg.argumentOf(call.getExpr(), _)
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
exists(ParameterNode p, ReturnNode ret |
|
||||
simpleParameterFlow(p, ret, t, config) and
|
||||
kind = ret.getKind() and
|
||||
viableParamArg(call, p, arg)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a method.
|
||||
* Holds if data can flow from `arg` to `out` through a 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 calls that do not
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate flowThroughMethod(
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, Node out, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
nodeCand1(out, false, unbind(config)) and
|
||||
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a callable.
|
||||
*/
|
||||
private predicate flowThroughCallable(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
) {
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
|
||||
or
|
||||
argumentValueFlowsThrough(node1, node2) and preservesValue = true
|
||||
argumentValueFlowsThrough(node1, node2, _) and preservesValue = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in one local step or a step
|
||||
* through a method.
|
||||
* through a callable.
|
||||
*/
|
||||
private predicate localFlowStepOrFlowThroughMethod(
|
||||
private predicate localFlowStepOrFlowThroughCallable(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
) {
|
||||
localFlowStep(node1, node2, preservesValue, config) or
|
||||
flowThroughMethod(node1, node2, preservesValue, config)
|
||||
flowThroughCallable(node1, node2, preservesValue, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,15 +428,13 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
// 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 method
|
||||
exists(Method m, MethodAccess ma, ReturnNode ret |
|
||||
ret = node1 and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
m = viableImpl(ma) and
|
||||
node2.asExpr() = ma
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
getReturnPosition(node1) = viableReturnPos(call, kind) and
|
||||
node2 = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -430,7 +444,7 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
* path from a source to a sink.
|
||||
*/
|
||||
private predicate flowIntoCallable(Node node1, Node node2, Configuration config) {
|
||||
viableParamArg(node2, node1) and
|
||||
viableParamArg(_, node2, node1) and
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config)
|
||||
}
|
||||
@@ -505,7 +519,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
(
|
||||
exists(Node mid, boolean preservesValue |
|
||||
nodeCandFwd2(mid, fromArg, stored, config) and
|
||||
localFlowStepOrFlowThroughMethod(mid, node, preservesValue, config) and
|
||||
localFlowStepOrFlowThroughCallable(mid, node, preservesValue, config) and
|
||||
(stored = false or preservesValue = true)
|
||||
)
|
||||
or
|
||||
@@ -574,7 +588,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
nodeCandFwd2(node, _, unbindBool(stored), unbind(config)) and
|
||||
(
|
||||
exists(Node mid, boolean preservesValue |
|
||||
localFlowStepOrFlowThroughMethod(node, mid, preservesValue, config) and
|
||||
localFlowStepOrFlowThroughCallable(node, mid, preservesValue, config) and
|
||||
nodeCand2(mid, toReturn, stored, config) and
|
||||
(stored = false or preservesValue = true)
|
||||
)
|
||||
@@ -663,10 +677,10 @@ private predicate localFlowEntry(Node node, Configuration config) {
|
||||
config.isSource(node) or
|
||||
jumpStep(_, node, _, config) or
|
||||
node instanceof ParameterNode or
|
||||
node.asExpr() instanceof MethodAccess or
|
||||
node instanceof OutNode or
|
||||
node instanceof PostUpdateNode or
|
||||
read(_, _, node) or
|
||||
node.asExpr() instanceof CastExpr
|
||||
node instanceof CastNode
|
||||
)
|
||||
}
|
||||
|
||||
@@ -679,12 +693,12 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
jumpStep(node, next, _, config) or
|
||||
flowIntoCallable(node, next, config) or
|
||||
flowOutOfCallable(node, next, config) or
|
||||
flowThroughMethod(node, next, _, config) or
|
||||
flowThroughCallable(node, next, _, config) or
|
||||
store(node, _, next) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
node.asExpr() instanceof CastExpr
|
||||
node instanceof CastNode
|
||||
or
|
||||
config.isSink(node)
|
||||
}
|
||||
@@ -706,7 +720,7 @@ private predicate localFlowStepPlus(
|
||||
exists(Node mid, boolean pv1, boolean pv2 |
|
||||
localFlowStepPlus(node1, mid, pv1, config) and
|
||||
localFlowStep(mid, node2, pv2, config) and
|
||||
not mid.asExpr() instanceof CastExpr and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = pv1.booleanAnd(pv2) and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
@@ -725,7 +739,7 @@ private predicate localFlowBigStep(
|
||||
}
|
||||
|
||||
private newtype TAccessPathFront =
|
||||
TFrontNil(Type t) or
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(Content f)
|
||||
|
||||
/**
|
||||
@@ -733,12 +747,12 @@ private newtype TAccessPathFront =
|
||||
*/
|
||||
private class AccessPathFront extends TAccessPathFront {
|
||||
string toString() {
|
||||
exists(Type t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
or
|
||||
exists(Content f | this = TFrontHead(f) | result = f.toString())
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
DataFlowType getType() {
|
||||
this = TFrontNil(result)
|
||||
or
|
||||
exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
|
||||
@@ -755,8 +769,8 @@ private class AccessPathFrontNil extends AccessPathFront, TFrontNil { }
|
||||
private class CastingNode extends Node {
|
||||
CastingNode() {
|
||||
this instanceof ParameterNode or
|
||||
this.asExpr() instanceof CastExpr or
|
||||
this.asExpr() instanceof MethodAccess or
|
||||
this instanceof CastNode or
|
||||
this instanceof OutNode or
|
||||
this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode
|
||||
}
|
||||
}
|
||||
@@ -819,7 +833,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallable(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
or
|
||||
@@ -915,7 +929,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallable(node, mid, preservesValue, config) and
|
||||
flowCand(mid, toReturn, apf0, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
@@ -954,7 +968,7 @@ private predicate consCand(Content f, AccessPathFront apf, Configuration config)
|
||||
}
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(Type t) or
|
||||
TNil(DataFlowType t) or
|
||||
TCons(Content f, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
@@ -975,7 +989,7 @@ private class AccessPath extends TAccessPath {
|
||||
this = TCons(_, result)
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
DataFlowType getType() {
|
||||
this = TNil(result)
|
||||
or
|
||||
exists(Content head | this = TCons(head, _) | result = head.getContainerType())
|
||||
@@ -985,9 +999,11 @@ private class AccessPath extends TAccessPath {
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
override string toString() { exists(Type t | this = TNil(t) | result = ppReprType(t)) }
|
||||
override string toString() { exists(DataFlowType t | this = TNil(t) | result = ppReprType(t)) }
|
||||
|
||||
override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) }
|
||||
override AccessPathFront getFront() {
|
||||
exists(DataFlowType t | this = TNil(t) | result = TFrontNil(t))
|
||||
}
|
||||
}
|
||||
|
||||
private class AccessPathCons extends AccessPath, TCons {
|
||||
@@ -1083,7 +1099,7 @@ private predicate flowFwd0(
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0, AccessPath ap0 |
|
||||
flowFwd(mid, fromArg, apf0, ap0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallable(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0 and apf = apf0
|
||||
or
|
||||
@@ -1196,7 +1212,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPath ap0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallable(node, mid, preservesValue, config) and
|
||||
flow(mid, toReturn, ap0, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0
|
||||
@@ -1255,7 +1271,7 @@ private newtype TPathNode =
|
||||
or
|
||||
// ... or a step from an existing PathNode to another node.
|
||||
exists(PathNodeMid mid |
|
||||
flowStep(mid, node, cc, ap) and
|
||||
pathStep(mid, node, cc, ap) and
|
||||
config = mid.getConfiguration() and
|
||||
flow(node, _, ap, unbind(config))
|
||||
)
|
||||
@@ -1274,8 +1290,14 @@ abstract class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = getNode().toString() + ppAp() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { result = getNode().toString() + ppAp() + ppCtx() }
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() { result = getNode().getLocation() }
|
||||
DataFlowLocation getLocation() { result = getNode().getLocation() }
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
abstract Node getNode();
|
||||
@@ -1284,20 +1306,31 @@ abstract class PathNode extends TPathNode {
|
||||
abstract Configuration getConfiguration();
|
||||
|
||||
/** Gets a successor. */
|
||||
abstract PathNode getSucc();
|
||||
deprecated final PathNode getSucc() { result = this.getASuccessor() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
abstract PathNode getASuccessor();
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
result = " [" + this.(PathNodeMid).getAp().toString() + "]"
|
||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
)
|
||||
}
|
||||
|
||||
private string ppCtx() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getSucc()) }
|
||||
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
|
||||
|
||||
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getSucc() = n2 and reach(n2) }
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -1333,11 +1366,11 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
private PathNodeMid getSuccMid() {
|
||||
flowStep(this, result.getNode(), result.getCallContext(), result.getAp()) and
|
||||
pathStep(this, result.getNode(), result.getCallContext(), result.getAp()) and
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNode getSucc() {
|
||||
override PathNode getASuccessor() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -1391,14 +1424,14 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getSucc() { none() }
|
||||
override PathNode getASuccessor() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
@@ -1421,15 +1454,15 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap))
|
||||
or
|
||||
flowOutOfArgument(mid, node, cc) and ap = mid.getAp()
|
||||
pathOutOfArgument(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
pathIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
or
|
||||
flowOutOfMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
pathOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowThroughMethod(mid, node.asExpr(), cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
pathThroughCallable(mid, node, cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
or
|
||||
valueFlowThroughMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
valuePathThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
@@ -1449,37 +1482,41 @@ private predicate contentStoreStep(
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to an exit of `m` in the context
|
||||
* `innercc`, and the path did not flow through a parameter of `m`.
|
||||
*/
|
||||
private predicate flowOutOfMethod0(PathNodeMid mid, Method m, CallContext innercc) {
|
||||
exists(ReturnNode ret |
|
||||
ret = mid.getNode() and
|
||||
innercc = mid.getCallContext() and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
not innercc instanceof CallContextCall
|
||||
private predicate pathOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
innercc = mid.getCallContext() and
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate pathOutOfCallable1(
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
pathOutOfCallable0(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 `ma`. The last step of this path
|
||||
* is a return from a method and is recorded by `cc`, if needed.
|
||||
* 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 flowOutOfMethod(PathNodeMid mid, MethodAccess ma, CallContext cc) {
|
||||
exists(Method m, CallContext innercc |
|
||||
flowOutOfMethod0(mid, m, innercc) and
|
||||
resolveReturn(innercc, m, ma)
|
||||
|
|
||||
if reducedViableImplInReturn(m, ma) then cc = TReturn(m, ma) else cc = TAnyCallContext()
|
||||
private predicate pathOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnKind kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate flowOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
|
||||
private predicate pathOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
|
||||
exists(
|
||||
PostUpdateNode n, ParameterNode p, Callable callable, CallContext innercc, int i, Call call,
|
||||
ArgumentNode arg
|
||||
PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
|
||||
DataFlowCall call, ArgumentNode arg
|
||||
|
|
||||
mid.getNode() = n and
|
||||
parameterValueFlowsToUpdate(p, n) and
|
||||
@@ -1500,7 +1537,9 @@ private predicate flowOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallCo
|
||||
* Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowIntoArg(PathNodeMid mid, int i, CallContext cc, Call call, boolean emptyAp) {
|
||||
private predicate pathIntoArg(
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, boolean emptyAp
|
||||
) {
|
||||
exists(ArgumentNode arg, AccessPath ap |
|
||||
arg = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -1514,7 +1553,7 @@ private predicate flowIntoArg(PathNodeMid mid, int i, CallContext cc, Call call,
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterCand(Callable callable, int i, Configuration config) {
|
||||
private predicate parameterCand(DataFlowCallable callable, int i, Configuration config) {
|
||||
exists(ParameterNode p |
|
||||
flow(p, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
@@ -1522,10 +1561,11 @@ private predicate parameterCand(Callable callable, int i, Configuration config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallable0(
|
||||
PathNodeMid mid, Callable callable, int i, CallContext outercc, Call call, boolean emptyAp
|
||||
private predicate pathIntoCallable0(
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
boolean emptyAp
|
||||
) {
|
||||
flowIntoArg(mid, i, outercc, call, emptyAp) and
|
||||
pathIntoArg(mid, i, outercc, call, emptyAp) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
parameterCand(callable, any(int j | j <= i and j >= i), mid.getConfiguration())
|
||||
}
|
||||
@@ -1535,11 +1575,11 @@ private predicate flowIntoCallable0(
|
||||
* before and after entering the callable are `outercc` and `innercc`,
|
||||
* respectively.
|
||||
*/
|
||||
private predicate flowIntoCallable(
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, Call call
|
||||
private predicate pathIntoCallable(
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, DataFlowCall call
|
||||
) {
|
||||
exists(int i, Callable callable, boolean emptyAp |
|
||||
flowIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
exists(int i, DataFlowCallable callable, boolean emptyAp |
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
@@ -1548,11 +1588,14 @@ private predicate flowIntoCallable(
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if data may flow from `p` to a return statement in the callable. */
|
||||
/** Holds if data may flow from `p` to a return of kind `kind`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configuration config) {
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
config = mid.getConfiguration() and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
@@ -1565,25 +1608,44 @@ private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configu
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to an argument of `methodcall`,
|
||||
* through a called method `m`, and back out through a return statement in
|
||||
* `m`. The context `cc` is restored to its value prior to entering `m`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) {
|
||||
private predicate pathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, methodcall) and
|
||||
paramFlowsThrough(p, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p) and
|
||||
pathIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, kind, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc) and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) {
|
||||
exists(ParameterNode p |
|
||||
flowIntoCallable(mid, p, cc, _, methodcall) and
|
||||
parameterValueFlowsThrough(p)
|
||||
/**
|
||||
* Holds if data may flow from `mid` through a callable to the node `out`.
|
||||
* The context `cc` is restored to its value prior to entering the callable.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
pathThroughCallable0(call, mid, kind, cc) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate valuePathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
pathIntoCallable(mid, p, cc, innercc, call) and
|
||||
parameterValueFlowsThrough(p, kind, innercc)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valuePathThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
valuePathThroughCallable0(call, mid, kind, cc) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
/**
|
||||
* Provides an implementation of global (interprocedural) data flow. This file
|
||||
* re-exports the local (intraprocedural) data flow analysis from `DataFlowUtil`
|
||||
* and adds a global analysis, mainly exposed through the `Configuration` class.
|
||||
* This file exists in several identical copies, allowing queries to use
|
||||
* multiple `Configuration` classes that depend on each other without
|
||||
* introducing mutual recursion among those configurations.
|
||||
* re-exports the local (intraprocedural) data flow analysis from
|
||||
* `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed
|
||||
* through the `Configuration` class. This file exists in several identical
|
||||
* copies, allowing queries to use multiple `Configuration` classes that depend
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowDispatch
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
@@ -95,7 +94,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(Expr sink) { hasFlowTo(exprNode(sink)) }
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/** DEPRECATED: use `hasFlow` instead. */
|
||||
deprecated predicate hasFlowForward(Node source, Node sink) { hasFlow(source, sink) }
|
||||
@@ -114,7 +113,8 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
|
||||
|
||||
pragma[noinline]
|
||||
private predicate isAdditionalFlowStep(
|
||||
Node node1, Node node2, Callable callable1, Callable callable2, Configuration config
|
||||
Node node1, Node node2, DataFlowCallable callable1, DataFlowCallable callable2,
|
||||
Configuration config
|
||||
) {
|
||||
config.isAdditionalFlowStep(node1, node2) and
|
||||
callable1 = node1.getEnclosingCallable() and
|
||||
@@ -125,7 +125,7 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if the additional step from `node1` to `node2` does not jump between callables.
|
||||
*/
|
||||
private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
|
||||
exists(Callable callable | isAdditionalFlowStep(node1, node2, callable, callable, config))
|
||||
exists(DataFlowCallable callable | isAdditionalFlowStep(node1, node2, callable, callable, config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,16 +148,17 @@ private predicate localFlowStep(Node node1, Node node2, boolean preservesValue,
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private Method returnNodeGetEnclosingCallable(ReturnNode ret) {
|
||||
result = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if field flow should be used for the given configuration.
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
|
||||
viableImpl(call) = result.getCallable() and
|
||||
kind = result.getKind()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is reachable from a source in the given configuration
|
||||
* ignoring call contexts.
|
||||
@@ -198,22 +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(Method m, MethodAccess ma, ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
|
||||
nodeCandFwd1(ret, stored, config) and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
m = viableImpl(ma) and
|
||||
node.asExpr() = ma
|
||||
getReturnPosition(ret) = viableReturnPos(call, kind) and
|
||||
node = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -275,22 +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(Method m, ExprNode ma |
|
||||
nodeCand1(ma, stored, config) and
|
||||
m = returnNodeGetEnclosingCallable(node) and
|
||||
m = viableImpl(ma.getExpr())
|
||||
exists(DataFlowCall call, ReturnKind kind, OutNode out |
|
||||
nodeCand1(out, stored, config) and
|
||||
getReturnPosition(node) = viableReturnPos(call, kind) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -316,11 +316,17 @@ private predicate readCand1(Content f, Configuration config) {
|
||||
* from the configuration.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Configuration config) {
|
||||
private predicate simpleParameterFlow(
|
||||
ParameterNode p, Node node, DataFlowType t, Configuration config
|
||||
) {
|
||||
nodeCand1(node, false, config) and
|
||||
p = node and
|
||||
t = getErasedRepr(node.getType()) and
|
||||
not parameterValueFlowsThrough(p)
|
||||
exists(ReturnNode ret, ReturnKind kind |
|
||||
returnNodeGetEnclosingCallable(ret) = p.getEnclosingCallable() and
|
||||
kind = ret.getKind() and
|
||||
not parameterValueFlowsThrough(p, kind, _)
|
||||
)
|
||||
or
|
||||
nodeCand1(node, false, unbind(config)) and
|
||||
exists(Node mid |
|
||||
@@ -347,7 +353,7 @@ private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Con
|
||||
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
|
||||
@@ -359,45 +365,55 @@ private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Con
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, ExprNode call, RefType t, Configuration config
|
||||
pragma[noinline]
|
||||
private predicate simpleArgumentFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode param, ReturnNode ret |
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
nodeCand1(call, false, unbind(config)) and
|
||||
viableParamArg(param, arg) and
|
||||
simpleParameterFlow(param, ret, t, config) and
|
||||
arg.argumentOf(call.getExpr(), _)
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
exists(ParameterNode p, ReturnNode ret |
|
||||
simpleParameterFlow(p, ret, t, config) and
|
||||
kind = ret.getKind() and
|
||||
viableParamArg(call, p, arg)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a method.
|
||||
* Holds if data can flow from `arg` to `out` through a 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 calls that do not
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate flowThroughMethod(
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, Node out, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
nodeCand1(out, false, unbind(config)) and
|
||||
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a callable.
|
||||
*/
|
||||
private predicate flowThroughCallable(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
) {
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
|
||||
or
|
||||
argumentValueFlowsThrough(node1, node2) and preservesValue = true
|
||||
argumentValueFlowsThrough(node1, node2, _) and preservesValue = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in one local step or a step
|
||||
* through a method.
|
||||
* through a callable.
|
||||
*/
|
||||
private predicate localFlowStepOrFlowThroughMethod(
|
||||
private predicate localFlowStepOrFlowThroughCallable(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
) {
|
||||
localFlowStep(node1, node2, preservesValue, config) or
|
||||
flowThroughMethod(node1, node2, preservesValue, config)
|
||||
flowThroughCallable(node1, node2, preservesValue, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,15 +428,13 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
// 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 method
|
||||
exists(Method m, MethodAccess ma, ReturnNode ret |
|
||||
ret = node1 and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
m = viableImpl(ma) and
|
||||
node2.asExpr() = ma
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
getReturnPosition(node1) = viableReturnPos(call, kind) and
|
||||
node2 = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -430,7 +444,7 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
* path from a source to a sink.
|
||||
*/
|
||||
private predicate flowIntoCallable(Node node1, Node node2, Configuration config) {
|
||||
viableParamArg(node2, node1) and
|
||||
viableParamArg(_, node2, node1) and
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config)
|
||||
}
|
||||
@@ -505,7 +519,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
(
|
||||
exists(Node mid, boolean preservesValue |
|
||||
nodeCandFwd2(mid, fromArg, stored, config) and
|
||||
localFlowStepOrFlowThroughMethod(mid, node, preservesValue, config) and
|
||||
localFlowStepOrFlowThroughCallable(mid, node, preservesValue, config) and
|
||||
(stored = false or preservesValue = true)
|
||||
)
|
||||
or
|
||||
@@ -574,7 +588,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
nodeCandFwd2(node, _, unbindBool(stored), unbind(config)) and
|
||||
(
|
||||
exists(Node mid, boolean preservesValue |
|
||||
localFlowStepOrFlowThroughMethod(node, mid, preservesValue, config) and
|
||||
localFlowStepOrFlowThroughCallable(node, mid, preservesValue, config) and
|
||||
nodeCand2(mid, toReturn, stored, config) and
|
||||
(stored = false or preservesValue = true)
|
||||
)
|
||||
@@ -663,10 +677,10 @@ private predicate localFlowEntry(Node node, Configuration config) {
|
||||
config.isSource(node) or
|
||||
jumpStep(_, node, _, config) or
|
||||
node instanceof ParameterNode or
|
||||
node.asExpr() instanceof MethodAccess or
|
||||
node instanceof OutNode or
|
||||
node instanceof PostUpdateNode or
|
||||
read(_, _, node) or
|
||||
node.asExpr() instanceof CastExpr
|
||||
node instanceof CastNode
|
||||
)
|
||||
}
|
||||
|
||||
@@ -679,12 +693,12 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
jumpStep(node, next, _, config) or
|
||||
flowIntoCallable(node, next, config) or
|
||||
flowOutOfCallable(node, next, config) or
|
||||
flowThroughMethod(node, next, _, config) or
|
||||
flowThroughCallable(node, next, _, config) or
|
||||
store(node, _, next) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
node.asExpr() instanceof CastExpr
|
||||
node instanceof CastNode
|
||||
or
|
||||
config.isSink(node)
|
||||
}
|
||||
@@ -706,7 +720,7 @@ private predicate localFlowStepPlus(
|
||||
exists(Node mid, boolean pv1, boolean pv2 |
|
||||
localFlowStepPlus(node1, mid, pv1, config) and
|
||||
localFlowStep(mid, node2, pv2, config) and
|
||||
not mid.asExpr() instanceof CastExpr and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = pv1.booleanAnd(pv2) and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
@@ -725,7 +739,7 @@ private predicate localFlowBigStep(
|
||||
}
|
||||
|
||||
private newtype TAccessPathFront =
|
||||
TFrontNil(Type t) or
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(Content f)
|
||||
|
||||
/**
|
||||
@@ -733,12 +747,12 @@ private newtype TAccessPathFront =
|
||||
*/
|
||||
private class AccessPathFront extends TAccessPathFront {
|
||||
string toString() {
|
||||
exists(Type t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
or
|
||||
exists(Content f | this = TFrontHead(f) | result = f.toString())
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
DataFlowType getType() {
|
||||
this = TFrontNil(result)
|
||||
or
|
||||
exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
|
||||
@@ -755,8 +769,8 @@ private class AccessPathFrontNil extends AccessPathFront, TFrontNil { }
|
||||
private class CastingNode extends Node {
|
||||
CastingNode() {
|
||||
this instanceof ParameterNode or
|
||||
this.asExpr() instanceof CastExpr or
|
||||
this.asExpr() instanceof MethodAccess or
|
||||
this instanceof CastNode or
|
||||
this instanceof OutNode or
|
||||
this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode
|
||||
}
|
||||
}
|
||||
@@ -819,7 +833,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallable(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
or
|
||||
@@ -915,7 +929,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallable(node, mid, preservesValue, config) and
|
||||
flowCand(mid, toReturn, apf0, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
@@ -954,7 +968,7 @@ private predicate consCand(Content f, AccessPathFront apf, Configuration config)
|
||||
}
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(Type t) or
|
||||
TNil(DataFlowType t) or
|
||||
TCons(Content f, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
@@ -975,7 +989,7 @@ private class AccessPath extends TAccessPath {
|
||||
this = TCons(_, result)
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
DataFlowType getType() {
|
||||
this = TNil(result)
|
||||
or
|
||||
exists(Content head | this = TCons(head, _) | result = head.getContainerType())
|
||||
@@ -985,9 +999,11 @@ private class AccessPath extends TAccessPath {
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
override string toString() { exists(Type t | this = TNil(t) | result = ppReprType(t)) }
|
||||
override string toString() { exists(DataFlowType t | this = TNil(t) | result = ppReprType(t)) }
|
||||
|
||||
override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) }
|
||||
override AccessPathFront getFront() {
|
||||
exists(DataFlowType t | this = TNil(t) | result = TFrontNil(t))
|
||||
}
|
||||
}
|
||||
|
||||
private class AccessPathCons extends AccessPath, TCons {
|
||||
@@ -1083,7 +1099,7 @@ private predicate flowFwd0(
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0, AccessPath ap0 |
|
||||
flowFwd(mid, fromArg, apf0, ap0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallable(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0 and apf = apf0
|
||||
or
|
||||
@@ -1196,7 +1212,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPath ap0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallable(node, mid, preservesValue, config) and
|
||||
flow(mid, toReturn, ap0, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0
|
||||
@@ -1255,7 +1271,7 @@ private newtype TPathNode =
|
||||
or
|
||||
// ... or a step from an existing PathNode to another node.
|
||||
exists(PathNodeMid mid |
|
||||
flowStep(mid, node, cc, ap) and
|
||||
pathStep(mid, node, cc, ap) and
|
||||
config = mid.getConfiguration() and
|
||||
flow(node, _, ap, unbind(config))
|
||||
)
|
||||
@@ -1274,8 +1290,14 @@ abstract class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = getNode().toString() + ppAp() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { result = getNode().toString() + ppAp() + ppCtx() }
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() { result = getNode().getLocation() }
|
||||
DataFlowLocation getLocation() { result = getNode().getLocation() }
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
abstract Node getNode();
|
||||
@@ -1284,20 +1306,31 @@ abstract class PathNode extends TPathNode {
|
||||
abstract Configuration getConfiguration();
|
||||
|
||||
/** Gets a successor. */
|
||||
abstract PathNode getSucc();
|
||||
deprecated final PathNode getSucc() { result = this.getASuccessor() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
abstract PathNode getASuccessor();
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
result = " [" + this.(PathNodeMid).getAp().toString() + "]"
|
||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
)
|
||||
}
|
||||
|
||||
private string ppCtx() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getSucc()) }
|
||||
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
|
||||
|
||||
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getSucc() = n2 and reach(n2) }
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -1333,11 +1366,11 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
private PathNodeMid getSuccMid() {
|
||||
flowStep(this, result.getNode(), result.getCallContext(), result.getAp()) and
|
||||
pathStep(this, result.getNode(), result.getCallContext(), result.getAp()) and
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNode getSucc() {
|
||||
override PathNode getASuccessor() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -1391,14 +1424,14 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getSucc() { none() }
|
||||
override PathNode getASuccessor() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
@@ -1421,15 +1454,15 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap))
|
||||
or
|
||||
flowOutOfArgument(mid, node, cc) and ap = mid.getAp()
|
||||
pathOutOfArgument(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
pathIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
or
|
||||
flowOutOfMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
pathOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowThroughMethod(mid, node.asExpr(), cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
pathThroughCallable(mid, node, cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
or
|
||||
valueFlowThroughMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
valuePathThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
@@ -1449,37 +1482,41 @@ private predicate contentStoreStep(
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to an exit of `m` in the context
|
||||
* `innercc`, and the path did not flow through a parameter of `m`.
|
||||
*/
|
||||
private predicate flowOutOfMethod0(PathNodeMid mid, Method m, CallContext innercc) {
|
||||
exists(ReturnNode ret |
|
||||
ret = mid.getNode() and
|
||||
innercc = mid.getCallContext() and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
not innercc instanceof CallContextCall
|
||||
private predicate pathOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
innercc = mid.getCallContext() and
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate pathOutOfCallable1(
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
pathOutOfCallable0(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 `ma`. The last step of this path
|
||||
* is a return from a method and is recorded by `cc`, if needed.
|
||||
* 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 flowOutOfMethod(PathNodeMid mid, MethodAccess ma, CallContext cc) {
|
||||
exists(Method m, CallContext innercc |
|
||||
flowOutOfMethod0(mid, m, innercc) and
|
||||
resolveReturn(innercc, m, ma)
|
||||
|
|
||||
if reducedViableImplInReturn(m, ma) then cc = TReturn(m, ma) else cc = TAnyCallContext()
|
||||
private predicate pathOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnKind kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate flowOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
|
||||
private predicate pathOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
|
||||
exists(
|
||||
PostUpdateNode n, ParameterNode p, Callable callable, CallContext innercc, int i, Call call,
|
||||
ArgumentNode arg
|
||||
PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
|
||||
DataFlowCall call, ArgumentNode arg
|
||||
|
|
||||
mid.getNode() = n and
|
||||
parameterValueFlowsToUpdate(p, n) and
|
||||
@@ -1500,7 +1537,9 @@ private predicate flowOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallCo
|
||||
* Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowIntoArg(PathNodeMid mid, int i, CallContext cc, Call call, boolean emptyAp) {
|
||||
private predicate pathIntoArg(
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, boolean emptyAp
|
||||
) {
|
||||
exists(ArgumentNode arg, AccessPath ap |
|
||||
arg = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -1514,7 +1553,7 @@ private predicate flowIntoArg(PathNodeMid mid, int i, CallContext cc, Call call,
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterCand(Callable callable, int i, Configuration config) {
|
||||
private predicate parameterCand(DataFlowCallable callable, int i, Configuration config) {
|
||||
exists(ParameterNode p |
|
||||
flow(p, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
@@ -1522,10 +1561,11 @@ private predicate parameterCand(Callable callable, int i, Configuration config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallable0(
|
||||
PathNodeMid mid, Callable callable, int i, CallContext outercc, Call call, boolean emptyAp
|
||||
private predicate pathIntoCallable0(
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
boolean emptyAp
|
||||
) {
|
||||
flowIntoArg(mid, i, outercc, call, emptyAp) and
|
||||
pathIntoArg(mid, i, outercc, call, emptyAp) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
parameterCand(callable, any(int j | j <= i and j >= i), mid.getConfiguration())
|
||||
}
|
||||
@@ -1535,11 +1575,11 @@ private predicate flowIntoCallable0(
|
||||
* before and after entering the callable are `outercc` and `innercc`,
|
||||
* respectively.
|
||||
*/
|
||||
private predicate flowIntoCallable(
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, Call call
|
||||
private predicate pathIntoCallable(
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, DataFlowCall call
|
||||
) {
|
||||
exists(int i, Callable callable, boolean emptyAp |
|
||||
flowIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
exists(int i, DataFlowCallable callable, boolean emptyAp |
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
@@ -1548,11 +1588,14 @@ private predicate flowIntoCallable(
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if data may flow from `p` to a return statement in the callable. */
|
||||
/** Holds if data may flow from `p` to a return of kind `kind`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configuration config) {
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
config = mid.getConfiguration() and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
@@ -1565,25 +1608,44 @@ private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configu
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to an argument of `methodcall`,
|
||||
* through a called method `m`, and back out through a return statement in
|
||||
* `m`. The context `cc` is restored to its value prior to entering `m`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) {
|
||||
private predicate pathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, methodcall) and
|
||||
paramFlowsThrough(p, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p) and
|
||||
pathIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, kind, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc) and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) {
|
||||
exists(ParameterNode p |
|
||||
flowIntoCallable(mid, p, cc, _, methodcall) and
|
||||
parameterValueFlowsThrough(p)
|
||||
/**
|
||||
* Holds if data may flow from `mid` through a callable to the node `out`.
|
||||
* The context `cc` is restored to its value prior to entering the callable.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
pathThroughCallable0(call, mid, kind, cc) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate valuePathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
pathIntoCallable(mid, p, cc, innercc, call) and
|
||||
parameterValueFlowsThrough(p, kind, innercc)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valuePathThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
valuePathThroughCallable0(call, mid, kind, cc) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
/**
|
||||
* Provides an implementation of global (interprocedural) data flow. This file
|
||||
* re-exports the local (intraprocedural) data flow analysis from `DataFlowUtil`
|
||||
* and adds a global analysis, mainly exposed through the `Configuration` class.
|
||||
* This file exists in several identical copies, allowing queries to use
|
||||
* multiple `Configuration` classes that depend on each other without
|
||||
* introducing mutual recursion among those configurations.
|
||||
* re-exports the local (intraprocedural) data flow analysis from
|
||||
* `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed
|
||||
* through the `Configuration` class. This file exists in several identical
|
||||
* copies, allowing queries to use multiple `Configuration` classes that depend
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowDispatch
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
@@ -95,7 +94,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(Expr sink) { hasFlowTo(exprNode(sink)) }
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/** DEPRECATED: use `hasFlow` instead. */
|
||||
deprecated predicate hasFlowForward(Node source, Node sink) { hasFlow(source, sink) }
|
||||
@@ -114,7 +113,8 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
|
||||
|
||||
pragma[noinline]
|
||||
private predicate isAdditionalFlowStep(
|
||||
Node node1, Node node2, Callable callable1, Callable callable2, Configuration config
|
||||
Node node1, Node node2, DataFlowCallable callable1, DataFlowCallable callable2,
|
||||
Configuration config
|
||||
) {
|
||||
config.isAdditionalFlowStep(node1, node2) and
|
||||
callable1 = node1.getEnclosingCallable() and
|
||||
@@ -125,7 +125,7 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if the additional step from `node1` to `node2` does not jump between callables.
|
||||
*/
|
||||
private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
|
||||
exists(Callable callable | isAdditionalFlowStep(node1, node2, callable, callable, config))
|
||||
exists(DataFlowCallable callable | isAdditionalFlowStep(node1, node2, callable, callable, config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,16 +148,17 @@ private predicate localFlowStep(Node node1, Node node2, boolean preservesValue,
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private Method returnNodeGetEnclosingCallable(ReturnNode ret) {
|
||||
result = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if field flow should be used for the given configuration.
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
|
||||
viableImpl(call) = result.getCallable() and
|
||||
kind = result.getKind()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is reachable from a source in the given configuration
|
||||
* ignoring call contexts.
|
||||
@@ -198,22 +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(Method m, MethodAccess ma, ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
|
||||
nodeCandFwd1(ret, stored, config) and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
m = viableImpl(ma) and
|
||||
node.asExpr() = ma
|
||||
getReturnPosition(ret) = viableReturnPos(call, kind) and
|
||||
node = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -275,22 +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(Method m, ExprNode ma |
|
||||
nodeCand1(ma, stored, config) and
|
||||
m = returnNodeGetEnclosingCallable(node) and
|
||||
m = viableImpl(ma.getExpr())
|
||||
exists(DataFlowCall call, ReturnKind kind, OutNode out |
|
||||
nodeCand1(out, stored, config) and
|
||||
getReturnPosition(node) = viableReturnPos(call, kind) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -316,11 +316,17 @@ private predicate readCand1(Content f, Configuration config) {
|
||||
* from the configuration.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Configuration config) {
|
||||
private predicate simpleParameterFlow(
|
||||
ParameterNode p, Node node, DataFlowType t, Configuration config
|
||||
) {
|
||||
nodeCand1(node, false, config) and
|
||||
p = node and
|
||||
t = getErasedRepr(node.getType()) and
|
||||
not parameterValueFlowsThrough(p)
|
||||
exists(ReturnNode ret, ReturnKind kind |
|
||||
returnNodeGetEnclosingCallable(ret) = p.getEnclosingCallable() and
|
||||
kind = ret.getKind() and
|
||||
not parameterValueFlowsThrough(p, kind, _)
|
||||
)
|
||||
or
|
||||
nodeCand1(node, false, unbind(config)) and
|
||||
exists(Node mid |
|
||||
@@ -347,7 +353,7 @@ private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Con
|
||||
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
|
||||
@@ -359,45 +365,55 @@ private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Con
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, ExprNode call, RefType t, Configuration config
|
||||
pragma[noinline]
|
||||
private predicate simpleArgumentFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode param, ReturnNode ret |
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
nodeCand1(call, false, unbind(config)) and
|
||||
viableParamArg(param, arg) and
|
||||
simpleParameterFlow(param, ret, t, config) and
|
||||
arg.argumentOf(call.getExpr(), _)
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
exists(ParameterNode p, ReturnNode ret |
|
||||
simpleParameterFlow(p, ret, t, config) and
|
||||
kind = ret.getKind() and
|
||||
viableParamArg(call, p, arg)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a method.
|
||||
* Holds if data can flow from `arg` to `out` through a 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 calls that do not
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate flowThroughMethod(
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, Node out, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
nodeCand1(out, false, unbind(config)) and
|
||||
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a callable.
|
||||
*/
|
||||
private predicate flowThroughCallable(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
) {
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
|
||||
or
|
||||
argumentValueFlowsThrough(node1, node2) and preservesValue = true
|
||||
argumentValueFlowsThrough(node1, node2, _) and preservesValue = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in one local step or a step
|
||||
* through a method.
|
||||
* through a callable.
|
||||
*/
|
||||
private predicate localFlowStepOrFlowThroughMethod(
|
||||
private predicate localFlowStepOrFlowThroughCallable(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
) {
|
||||
localFlowStep(node1, node2, preservesValue, config) or
|
||||
flowThroughMethod(node1, node2, preservesValue, config)
|
||||
flowThroughCallable(node1, node2, preservesValue, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,15 +428,13 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
// 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 method
|
||||
exists(Method m, MethodAccess ma, ReturnNode ret |
|
||||
ret = node1 and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
m = viableImpl(ma) and
|
||||
node2.asExpr() = ma
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
getReturnPosition(node1) = viableReturnPos(call, kind) and
|
||||
node2 = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -430,7 +444,7 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
* path from a source to a sink.
|
||||
*/
|
||||
private predicate flowIntoCallable(Node node1, Node node2, Configuration config) {
|
||||
viableParamArg(node2, node1) and
|
||||
viableParamArg(_, node2, node1) and
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config)
|
||||
}
|
||||
@@ -505,7 +519,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
(
|
||||
exists(Node mid, boolean preservesValue |
|
||||
nodeCandFwd2(mid, fromArg, stored, config) and
|
||||
localFlowStepOrFlowThroughMethod(mid, node, preservesValue, config) and
|
||||
localFlowStepOrFlowThroughCallable(mid, node, preservesValue, config) and
|
||||
(stored = false or preservesValue = true)
|
||||
)
|
||||
or
|
||||
@@ -574,7 +588,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
nodeCandFwd2(node, _, unbindBool(stored), unbind(config)) and
|
||||
(
|
||||
exists(Node mid, boolean preservesValue |
|
||||
localFlowStepOrFlowThroughMethod(node, mid, preservesValue, config) and
|
||||
localFlowStepOrFlowThroughCallable(node, mid, preservesValue, config) and
|
||||
nodeCand2(mid, toReturn, stored, config) and
|
||||
(stored = false or preservesValue = true)
|
||||
)
|
||||
@@ -663,10 +677,10 @@ private predicate localFlowEntry(Node node, Configuration config) {
|
||||
config.isSource(node) or
|
||||
jumpStep(_, node, _, config) or
|
||||
node instanceof ParameterNode or
|
||||
node.asExpr() instanceof MethodAccess or
|
||||
node instanceof OutNode or
|
||||
node instanceof PostUpdateNode or
|
||||
read(_, _, node) or
|
||||
node.asExpr() instanceof CastExpr
|
||||
node instanceof CastNode
|
||||
)
|
||||
}
|
||||
|
||||
@@ -679,12 +693,12 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
jumpStep(node, next, _, config) or
|
||||
flowIntoCallable(node, next, config) or
|
||||
flowOutOfCallable(node, next, config) or
|
||||
flowThroughMethod(node, next, _, config) or
|
||||
flowThroughCallable(node, next, _, config) or
|
||||
store(node, _, next) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
node.asExpr() instanceof CastExpr
|
||||
node instanceof CastNode
|
||||
or
|
||||
config.isSink(node)
|
||||
}
|
||||
@@ -706,7 +720,7 @@ private predicate localFlowStepPlus(
|
||||
exists(Node mid, boolean pv1, boolean pv2 |
|
||||
localFlowStepPlus(node1, mid, pv1, config) and
|
||||
localFlowStep(mid, node2, pv2, config) and
|
||||
not mid.asExpr() instanceof CastExpr and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = pv1.booleanAnd(pv2) and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
@@ -725,7 +739,7 @@ private predicate localFlowBigStep(
|
||||
}
|
||||
|
||||
private newtype TAccessPathFront =
|
||||
TFrontNil(Type t) or
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(Content f)
|
||||
|
||||
/**
|
||||
@@ -733,12 +747,12 @@ private newtype TAccessPathFront =
|
||||
*/
|
||||
private class AccessPathFront extends TAccessPathFront {
|
||||
string toString() {
|
||||
exists(Type t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
or
|
||||
exists(Content f | this = TFrontHead(f) | result = f.toString())
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
DataFlowType getType() {
|
||||
this = TFrontNil(result)
|
||||
or
|
||||
exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
|
||||
@@ -755,8 +769,8 @@ private class AccessPathFrontNil extends AccessPathFront, TFrontNil { }
|
||||
private class CastingNode extends Node {
|
||||
CastingNode() {
|
||||
this instanceof ParameterNode or
|
||||
this.asExpr() instanceof CastExpr or
|
||||
this.asExpr() instanceof MethodAccess or
|
||||
this instanceof CastNode or
|
||||
this instanceof OutNode or
|
||||
this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode
|
||||
}
|
||||
}
|
||||
@@ -819,7 +833,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallable(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
or
|
||||
@@ -915,7 +929,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallable(node, mid, preservesValue, config) and
|
||||
flowCand(mid, toReturn, apf0, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
@@ -954,7 +968,7 @@ private predicate consCand(Content f, AccessPathFront apf, Configuration config)
|
||||
}
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(Type t) or
|
||||
TNil(DataFlowType t) or
|
||||
TCons(Content f, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
@@ -975,7 +989,7 @@ private class AccessPath extends TAccessPath {
|
||||
this = TCons(_, result)
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
DataFlowType getType() {
|
||||
this = TNil(result)
|
||||
or
|
||||
exists(Content head | this = TCons(head, _) | result = head.getContainerType())
|
||||
@@ -985,9 +999,11 @@ private class AccessPath extends TAccessPath {
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
override string toString() { exists(Type t | this = TNil(t) | result = ppReprType(t)) }
|
||||
override string toString() { exists(DataFlowType t | this = TNil(t) | result = ppReprType(t)) }
|
||||
|
||||
override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) }
|
||||
override AccessPathFront getFront() {
|
||||
exists(DataFlowType t | this = TNil(t) | result = TFrontNil(t))
|
||||
}
|
||||
}
|
||||
|
||||
private class AccessPathCons extends AccessPath, TCons {
|
||||
@@ -1083,7 +1099,7 @@ private predicate flowFwd0(
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0, AccessPath ap0 |
|
||||
flowFwd(mid, fromArg, apf0, ap0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallable(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0 and apf = apf0
|
||||
or
|
||||
@@ -1196,7 +1212,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPath ap0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallable(node, mid, preservesValue, config) and
|
||||
flow(mid, toReturn, ap0, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0
|
||||
@@ -1255,7 +1271,7 @@ private newtype TPathNode =
|
||||
or
|
||||
// ... or a step from an existing PathNode to another node.
|
||||
exists(PathNodeMid mid |
|
||||
flowStep(mid, node, cc, ap) and
|
||||
pathStep(mid, node, cc, ap) and
|
||||
config = mid.getConfiguration() and
|
||||
flow(node, _, ap, unbind(config))
|
||||
)
|
||||
@@ -1274,8 +1290,14 @@ abstract class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = getNode().toString() + ppAp() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { result = getNode().toString() + ppAp() + ppCtx() }
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() { result = getNode().getLocation() }
|
||||
DataFlowLocation getLocation() { result = getNode().getLocation() }
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
abstract Node getNode();
|
||||
@@ -1284,20 +1306,31 @@ abstract class PathNode extends TPathNode {
|
||||
abstract Configuration getConfiguration();
|
||||
|
||||
/** Gets a successor. */
|
||||
abstract PathNode getSucc();
|
||||
deprecated final PathNode getSucc() { result = this.getASuccessor() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
abstract PathNode getASuccessor();
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
result = " [" + this.(PathNodeMid).getAp().toString() + "]"
|
||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
)
|
||||
}
|
||||
|
||||
private string ppCtx() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getSucc()) }
|
||||
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
|
||||
|
||||
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getSucc() = n2 and reach(n2) }
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -1333,11 +1366,11 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
private PathNodeMid getSuccMid() {
|
||||
flowStep(this, result.getNode(), result.getCallContext(), result.getAp()) and
|
||||
pathStep(this, result.getNode(), result.getCallContext(), result.getAp()) and
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNode getSucc() {
|
||||
override PathNode getASuccessor() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -1391,14 +1424,14 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getSucc() { none() }
|
||||
override PathNode getASuccessor() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
@@ -1421,15 +1454,15 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap))
|
||||
or
|
||||
flowOutOfArgument(mid, node, cc) and ap = mid.getAp()
|
||||
pathOutOfArgument(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
pathIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
or
|
||||
flowOutOfMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
pathOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowThroughMethod(mid, node.asExpr(), cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
pathThroughCallable(mid, node, cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
or
|
||||
valueFlowThroughMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
valuePathThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
@@ -1449,37 +1482,41 @@ private predicate contentStoreStep(
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to an exit of `m` in the context
|
||||
* `innercc`, and the path did not flow through a parameter of `m`.
|
||||
*/
|
||||
private predicate flowOutOfMethod0(PathNodeMid mid, Method m, CallContext innercc) {
|
||||
exists(ReturnNode ret |
|
||||
ret = mid.getNode() and
|
||||
innercc = mid.getCallContext() and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
not innercc instanceof CallContextCall
|
||||
private predicate pathOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
innercc = mid.getCallContext() and
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate pathOutOfCallable1(
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
pathOutOfCallable0(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 `ma`. The last step of this path
|
||||
* is a return from a method and is recorded by `cc`, if needed.
|
||||
* 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 flowOutOfMethod(PathNodeMid mid, MethodAccess ma, CallContext cc) {
|
||||
exists(Method m, CallContext innercc |
|
||||
flowOutOfMethod0(mid, m, innercc) and
|
||||
resolveReturn(innercc, m, ma)
|
||||
|
|
||||
if reducedViableImplInReturn(m, ma) then cc = TReturn(m, ma) else cc = TAnyCallContext()
|
||||
private predicate pathOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnKind kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate flowOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
|
||||
private predicate pathOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
|
||||
exists(
|
||||
PostUpdateNode n, ParameterNode p, Callable callable, CallContext innercc, int i, Call call,
|
||||
ArgumentNode arg
|
||||
PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
|
||||
DataFlowCall call, ArgumentNode arg
|
||||
|
|
||||
mid.getNode() = n and
|
||||
parameterValueFlowsToUpdate(p, n) and
|
||||
@@ -1500,7 +1537,9 @@ private predicate flowOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallCo
|
||||
* Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowIntoArg(PathNodeMid mid, int i, CallContext cc, Call call, boolean emptyAp) {
|
||||
private predicate pathIntoArg(
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, boolean emptyAp
|
||||
) {
|
||||
exists(ArgumentNode arg, AccessPath ap |
|
||||
arg = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -1514,7 +1553,7 @@ private predicate flowIntoArg(PathNodeMid mid, int i, CallContext cc, Call call,
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterCand(Callable callable, int i, Configuration config) {
|
||||
private predicate parameterCand(DataFlowCallable callable, int i, Configuration config) {
|
||||
exists(ParameterNode p |
|
||||
flow(p, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
@@ -1522,10 +1561,11 @@ private predicate parameterCand(Callable callable, int i, Configuration config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallable0(
|
||||
PathNodeMid mid, Callable callable, int i, CallContext outercc, Call call, boolean emptyAp
|
||||
private predicate pathIntoCallable0(
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
boolean emptyAp
|
||||
) {
|
||||
flowIntoArg(mid, i, outercc, call, emptyAp) and
|
||||
pathIntoArg(mid, i, outercc, call, emptyAp) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
parameterCand(callable, any(int j | j <= i and j >= i), mid.getConfiguration())
|
||||
}
|
||||
@@ -1535,11 +1575,11 @@ private predicate flowIntoCallable0(
|
||||
* before and after entering the callable are `outercc` and `innercc`,
|
||||
* respectively.
|
||||
*/
|
||||
private predicate flowIntoCallable(
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, Call call
|
||||
private predicate pathIntoCallable(
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, DataFlowCall call
|
||||
) {
|
||||
exists(int i, Callable callable, boolean emptyAp |
|
||||
flowIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
exists(int i, DataFlowCallable callable, boolean emptyAp |
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
@@ -1548,11 +1588,14 @@ private predicate flowIntoCallable(
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if data may flow from `p` to a return statement in the callable. */
|
||||
/** Holds if data may flow from `p` to a return of kind `kind`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configuration config) {
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
config = mid.getConfiguration() and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
@@ -1565,25 +1608,44 @@ private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configu
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to an argument of `methodcall`,
|
||||
* through a called method `m`, and back out through a return statement in
|
||||
* `m`. The context `cc` is restored to its value prior to entering `m`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) {
|
||||
private predicate pathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, methodcall) and
|
||||
paramFlowsThrough(p, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p) and
|
||||
pathIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, kind, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc) and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) {
|
||||
exists(ParameterNode p |
|
||||
flowIntoCallable(mid, p, cc, _, methodcall) and
|
||||
parameterValueFlowsThrough(p)
|
||||
/**
|
||||
* Holds if data may flow from `mid` through a callable to the node `out`.
|
||||
* The context `cc` is restored to its value prior to entering the callable.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
pathThroughCallable0(call, mid, kind, cc) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate valuePathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
pathIntoCallable(mid, p, cc, innercc, call) and
|
||||
parameterValueFlowsThrough(p, kind, innercc)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valuePathThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
valuePathThroughCallable0(call, mid, kind, cc) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
/**
|
||||
* Provides an implementation of global (interprocedural) data flow. This file
|
||||
* re-exports the local (intraprocedural) data flow analysis from `DataFlowUtil`
|
||||
* and adds a global analysis, mainly exposed through the `Configuration` class.
|
||||
* This file exists in several identical copies, allowing queries to use
|
||||
* multiple `Configuration` classes that depend on each other without
|
||||
* introducing mutual recursion among those configurations.
|
||||
* re-exports the local (intraprocedural) data flow analysis from
|
||||
* `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed
|
||||
* through the `Configuration` class. This file exists in several identical
|
||||
* copies, allowing queries to use multiple `Configuration` classes that depend
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowDispatch
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
@@ -95,7 +94,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(Expr sink) { hasFlowTo(exprNode(sink)) }
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/** DEPRECATED: use `hasFlow` instead. */
|
||||
deprecated predicate hasFlowForward(Node source, Node sink) { hasFlow(source, sink) }
|
||||
@@ -114,7 +113,8 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
|
||||
|
||||
pragma[noinline]
|
||||
private predicate isAdditionalFlowStep(
|
||||
Node node1, Node node2, Callable callable1, Callable callable2, Configuration config
|
||||
Node node1, Node node2, DataFlowCallable callable1, DataFlowCallable callable2,
|
||||
Configuration config
|
||||
) {
|
||||
config.isAdditionalFlowStep(node1, node2) and
|
||||
callable1 = node1.getEnclosingCallable() and
|
||||
@@ -125,7 +125,7 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if the additional step from `node1` to `node2` does not jump between callables.
|
||||
*/
|
||||
private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
|
||||
exists(Callable callable | isAdditionalFlowStep(node1, node2, callable, callable, config))
|
||||
exists(DataFlowCallable callable | isAdditionalFlowStep(node1, node2, callable, callable, config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,16 +148,17 @@ private predicate localFlowStep(Node node1, Node node2, boolean preservesValue,
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private Method returnNodeGetEnclosingCallable(ReturnNode ret) {
|
||||
result = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if field flow should be used for the given configuration.
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
|
||||
viableImpl(call) = result.getCallable() and
|
||||
kind = result.getKind()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is reachable from a source in the given configuration
|
||||
* ignoring call contexts.
|
||||
@@ -198,22 +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(Method m, MethodAccess ma, ReturnNode ret |
|
||||
exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
|
||||
nodeCandFwd1(ret, stored, config) and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
m = viableImpl(ma) and
|
||||
node.asExpr() = ma
|
||||
getReturnPosition(ret) = viableReturnPos(call, kind) and
|
||||
node = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -275,22 +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(Method m, ExprNode ma |
|
||||
nodeCand1(ma, stored, config) and
|
||||
m = returnNodeGetEnclosingCallable(node) and
|
||||
m = viableImpl(ma.getExpr())
|
||||
exists(DataFlowCall call, ReturnKind kind, OutNode out |
|
||||
nodeCand1(out, stored, config) and
|
||||
getReturnPosition(node) = viableReturnPos(call, kind) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -316,11 +316,17 @@ private predicate readCand1(Content f, Configuration config) {
|
||||
* from the configuration.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Configuration config) {
|
||||
private predicate simpleParameterFlow(
|
||||
ParameterNode p, Node node, DataFlowType t, Configuration config
|
||||
) {
|
||||
nodeCand1(node, false, config) and
|
||||
p = node and
|
||||
t = getErasedRepr(node.getType()) and
|
||||
not parameterValueFlowsThrough(p)
|
||||
exists(ReturnNode ret, ReturnKind kind |
|
||||
returnNodeGetEnclosingCallable(ret) = p.getEnclosingCallable() and
|
||||
kind = ret.getKind() and
|
||||
not parameterValueFlowsThrough(p, kind, _)
|
||||
)
|
||||
or
|
||||
nodeCand1(node, false, unbind(config)) and
|
||||
exists(Node mid |
|
||||
@@ -347,7 +353,7 @@ private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Con
|
||||
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
|
||||
@@ -359,45 +365,55 @@ private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Con
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, ExprNode call, RefType t, Configuration config
|
||||
pragma[noinline]
|
||||
private predicate simpleArgumentFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode param, ReturnNode ret |
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
nodeCand1(call, false, unbind(config)) and
|
||||
viableParamArg(param, arg) and
|
||||
simpleParameterFlow(param, ret, t, config) and
|
||||
arg.argumentOf(call.getExpr(), _)
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
exists(ParameterNode p, ReturnNode ret |
|
||||
simpleParameterFlow(p, ret, t, config) and
|
||||
kind = ret.getKind() and
|
||||
viableParamArg(call, p, arg)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a method.
|
||||
* Holds if data can flow from `arg` to `out` through a 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 calls that do not
|
||||
* necessarily preserve the value of `arg` by making use of at least one
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate flowThroughMethod(
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, Node out, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
nodeCand1(out, false, unbind(config)) and
|
||||
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a callable.
|
||||
*/
|
||||
private predicate flowThroughCallable(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
) {
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
|
||||
or
|
||||
argumentValueFlowsThrough(node1, node2) and preservesValue = true
|
||||
argumentValueFlowsThrough(node1, node2, _) and preservesValue = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in one local step or a step
|
||||
* through a method.
|
||||
* through a callable.
|
||||
*/
|
||||
private predicate localFlowStepOrFlowThroughMethod(
|
||||
private predicate localFlowStepOrFlowThroughCallable(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
) {
|
||||
localFlowStep(node1, node2, preservesValue, config) or
|
||||
flowThroughMethod(node1, node2, preservesValue, config)
|
||||
flowThroughCallable(node1, node2, preservesValue, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,15 +428,13 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
// 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 method
|
||||
exists(Method m, MethodAccess ma, ReturnNode ret |
|
||||
ret = node1 and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
m = viableImpl(ma) and
|
||||
node2.asExpr() = ma
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
getReturnPosition(node1) = viableReturnPos(call, kind) and
|
||||
node2 = getAnOutNode(call, kind)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -430,7 +444,7 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
* path from a source to a sink.
|
||||
*/
|
||||
private predicate flowIntoCallable(Node node1, Node node2, Configuration config) {
|
||||
viableParamArg(node2, node1) and
|
||||
viableParamArg(_, node2, node1) and
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config)
|
||||
}
|
||||
@@ -505,7 +519,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
(
|
||||
exists(Node mid, boolean preservesValue |
|
||||
nodeCandFwd2(mid, fromArg, stored, config) and
|
||||
localFlowStepOrFlowThroughMethod(mid, node, preservesValue, config) and
|
||||
localFlowStepOrFlowThroughCallable(mid, node, preservesValue, config) and
|
||||
(stored = false or preservesValue = true)
|
||||
)
|
||||
or
|
||||
@@ -574,7 +588,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
nodeCandFwd2(node, _, unbindBool(stored), unbind(config)) and
|
||||
(
|
||||
exists(Node mid, boolean preservesValue |
|
||||
localFlowStepOrFlowThroughMethod(node, mid, preservesValue, config) and
|
||||
localFlowStepOrFlowThroughCallable(node, mid, preservesValue, config) and
|
||||
nodeCand2(mid, toReturn, stored, config) and
|
||||
(stored = false or preservesValue = true)
|
||||
)
|
||||
@@ -663,10 +677,10 @@ private predicate localFlowEntry(Node node, Configuration config) {
|
||||
config.isSource(node) or
|
||||
jumpStep(_, node, _, config) or
|
||||
node instanceof ParameterNode or
|
||||
node.asExpr() instanceof MethodAccess or
|
||||
node instanceof OutNode or
|
||||
node instanceof PostUpdateNode or
|
||||
read(_, _, node) or
|
||||
node.asExpr() instanceof CastExpr
|
||||
node instanceof CastNode
|
||||
)
|
||||
}
|
||||
|
||||
@@ -679,12 +693,12 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
jumpStep(node, next, _, config) or
|
||||
flowIntoCallable(node, next, config) or
|
||||
flowOutOfCallable(node, next, config) or
|
||||
flowThroughMethod(node, next, _, config) or
|
||||
flowThroughCallable(node, next, _, config) or
|
||||
store(node, _, next) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
node.asExpr() instanceof CastExpr
|
||||
node instanceof CastNode
|
||||
or
|
||||
config.isSink(node)
|
||||
}
|
||||
@@ -706,7 +720,7 @@ private predicate localFlowStepPlus(
|
||||
exists(Node mid, boolean pv1, boolean pv2 |
|
||||
localFlowStepPlus(node1, mid, pv1, config) and
|
||||
localFlowStep(mid, node2, pv2, config) and
|
||||
not mid.asExpr() instanceof CastExpr and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = pv1.booleanAnd(pv2) and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
@@ -725,7 +739,7 @@ private predicate localFlowBigStep(
|
||||
}
|
||||
|
||||
private newtype TAccessPathFront =
|
||||
TFrontNil(Type t) or
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(Content f)
|
||||
|
||||
/**
|
||||
@@ -733,12 +747,12 @@ private newtype TAccessPathFront =
|
||||
*/
|
||||
private class AccessPathFront extends TAccessPathFront {
|
||||
string toString() {
|
||||
exists(Type t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
or
|
||||
exists(Content f | this = TFrontHead(f) | result = f.toString())
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
DataFlowType getType() {
|
||||
this = TFrontNil(result)
|
||||
or
|
||||
exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
|
||||
@@ -755,8 +769,8 @@ private class AccessPathFrontNil extends AccessPathFront, TFrontNil { }
|
||||
private class CastingNode extends Node {
|
||||
CastingNode() {
|
||||
this instanceof ParameterNode or
|
||||
this.asExpr() instanceof CastExpr or
|
||||
this.asExpr() instanceof MethodAccess or
|
||||
this instanceof CastNode or
|
||||
this instanceof OutNode or
|
||||
this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode
|
||||
}
|
||||
}
|
||||
@@ -819,7 +833,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallable(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
or
|
||||
@@ -915,7 +929,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallable(node, mid, preservesValue, config) and
|
||||
flowCand(mid, toReturn, apf0, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
@@ -954,7 +968,7 @@ private predicate consCand(Content f, AccessPathFront apf, Configuration config)
|
||||
}
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(Type t) or
|
||||
TNil(DataFlowType t) or
|
||||
TCons(Content f, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
@@ -975,7 +989,7 @@ private class AccessPath extends TAccessPath {
|
||||
this = TCons(_, result)
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
DataFlowType getType() {
|
||||
this = TNil(result)
|
||||
or
|
||||
exists(Content head | this = TCons(head, _) | result = head.getContainerType())
|
||||
@@ -985,9 +999,11 @@ private class AccessPath extends TAccessPath {
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
override string toString() { exists(Type t | this = TNil(t) | result = ppReprType(t)) }
|
||||
override string toString() { exists(DataFlowType t | this = TNil(t) | result = ppReprType(t)) }
|
||||
|
||||
override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) }
|
||||
override AccessPathFront getFront() {
|
||||
exists(DataFlowType t | this = TNil(t) | result = TFrontNil(t))
|
||||
}
|
||||
}
|
||||
|
||||
private class AccessPathCons extends AccessPath, TCons {
|
||||
@@ -1083,7 +1099,7 @@ private predicate flowFwd0(
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0, AccessPath ap0 |
|
||||
flowFwd(mid, fromArg, apf0, ap0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallable(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0 and apf = apf0
|
||||
or
|
||||
@@ -1196,7 +1212,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPath ap0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallable(node, mid, preservesValue, config) and
|
||||
flow(mid, toReturn, ap0, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0
|
||||
@@ -1255,7 +1271,7 @@ private newtype TPathNode =
|
||||
or
|
||||
// ... or a step from an existing PathNode to another node.
|
||||
exists(PathNodeMid mid |
|
||||
flowStep(mid, node, cc, ap) and
|
||||
pathStep(mid, node, cc, ap) and
|
||||
config = mid.getConfiguration() and
|
||||
flow(node, _, ap, unbind(config))
|
||||
)
|
||||
@@ -1274,8 +1290,14 @@ abstract class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = getNode().toString() + ppAp() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { result = getNode().toString() + ppAp() + ppCtx() }
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() { result = getNode().getLocation() }
|
||||
DataFlowLocation getLocation() { result = getNode().getLocation() }
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
abstract Node getNode();
|
||||
@@ -1284,20 +1306,31 @@ abstract class PathNode extends TPathNode {
|
||||
abstract Configuration getConfiguration();
|
||||
|
||||
/** Gets a successor. */
|
||||
abstract PathNode getSucc();
|
||||
deprecated final PathNode getSucc() { result = this.getASuccessor() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
abstract PathNode getASuccessor();
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
result = " [" + this.(PathNodeMid).getAp().toString() + "]"
|
||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
)
|
||||
}
|
||||
|
||||
private string ppCtx() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getSucc()) }
|
||||
private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
|
||||
|
||||
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getSucc() = n2 and reach(n2) }
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -1333,11 +1366,11 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
private PathNodeMid getSuccMid() {
|
||||
flowStep(this, result.getNode(), result.getCallContext(), result.getAp()) and
|
||||
pathStep(this, result.getNode(), result.getCallContext(), result.getAp()) and
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNode getSucc() {
|
||||
override PathNode getASuccessor() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -1391,14 +1424,14 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getSucc() { none() }
|
||||
override PathNode getASuccessor() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
@@ -1421,15 +1454,15 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap))
|
||||
or
|
||||
flowOutOfArgument(mid, node, cc) and ap = mid.getAp()
|
||||
pathOutOfArgument(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
pathIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
or
|
||||
flowOutOfMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
pathOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowThroughMethod(mid, node.asExpr(), cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
pathThroughCallable(mid, node, cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
or
|
||||
valueFlowThroughMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
valuePathThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
@@ -1449,37 +1482,41 @@ private predicate contentStoreStep(
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to an exit of `m` in the context
|
||||
* `innercc`, and the path did not flow through a parameter of `m`.
|
||||
*/
|
||||
private predicate flowOutOfMethod0(PathNodeMid mid, Method m, CallContext innercc) {
|
||||
exists(ReturnNode ret |
|
||||
ret = mid.getNode() and
|
||||
innercc = mid.getCallContext() and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
not innercc instanceof CallContextCall
|
||||
private predicate pathOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
innercc = mid.getCallContext() and
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate pathOutOfCallable1(
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
pathOutOfCallable0(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 `ma`. The last step of this path
|
||||
* is a return from a method and is recorded by `cc`, if needed.
|
||||
* 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 flowOutOfMethod(PathNodeMid mid, MethodAccess ma, CallContext cc) {
|
||||
exists(Method m, CallContext innercc |
|
||||
flowOutOfMethod0(mid, m, innercc) and
|
||||
resolveReturn(innercc, m, ma)
|
||||
|
|
||||
if reducedViableImplInReturn(m, ma) then cc = TReturn(m, ma) else cc = TAnyCallContext()
|
||||
private predicate pathOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnKind kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate flowOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
|
||||
private predicate pathOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
|
||||
exists(
|
||||
PostUpdateNode n, ParameterNode p, Callable callable, CallContext innercc, int i, Call call,
|
||||
ArgumentNode arg
|
||||
PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
|
||||
DataFlowCall call, ArgumentNode arg
|
||||
|
|
||||
mid.getNode() = n and
|
||||
parameterValueFlowsToUpdate(p, n) and
|
||||
@@ -1500,7 +1537,9 @@ private predicate flowOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallCo
|
||||
* Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowIntoArg(PathNodeMid mid, int i, CallContext cc, Call call, boolean emptyAp) {
|
||||
private predicate pathIntoArg(
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, boolean emptyAp
|
||||
) {
|
||||
exists(ArgumentNode arg, AccessPath ap |
|
||||
arg = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -1514,7 +1553,7 @@ private predicate flowIntoArg(PathNodeMid mid, int i, CallContext cc, Call call,
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterCand(Callable callable, int i, Configuration config) {
|
||||
private predicate parameterCand(DataFlowCallable callable, int i, Configuration config) {
|
||||
exists(ParameterNode p |
|
||||
flow(p, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
@@ -1522,10 +1561,11 @@ private predicate parameterCand(Callable callable, int i, Configuration config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowIntoCallable0(
|
||||
PathNodeMid mid, Callable callable, int i, CallContext outercc, Call call, boolean emptyAp
|
||||
private predicate pathIntoCallable0(
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
boolean emptyAp
|
||||
) {
|
||||
flowIntoArg(mid, i, outercc, call, emptyAp) and
|
||||
pathIntoArg(mid, i, outercc, call, emptyAp) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
parameterCand(callable, any(int j | j <= i and j >= i), mid.getConfiguration())
|
||||
}
|
||||
@@ -1535,11 +1575,11 @@ private predicate flowIntoCallable0(
|
||||
* before and after entering the callable are `outercc` and `innercc`,
|
||||
* respectively.
|
||||
*/
|
||||
private predicate flowIntoCallable(
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, Call call
|
||||
private predicate pathIntoCallable(
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, DataFlowCall call
|
||||
) {
|
||||
exists(int i, Callable callable, boolean emptyAp |
|
||||
flowIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
exists(int i, DataFlowCallable callable, boolean emptyAp |
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
@@ -1548,11 +1588,14 @@ private predicate flowIntoCallable(
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if data may flow from `p` to a return statement in the callable. */
|
||||
/** Holds if data may flow from `p` to a return of kind `kind`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configuration config) {
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
config = mid.getConfiguration() and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
@@ -1565,25 +1608,44 @@ private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configu
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to an argument of `methodcall`,
|
||||
* through a called method `m`, and back out through a return statement in
|
||||
* `m`. The context `cc` is restored to its value prior to entering `m`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate flowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) {
|
||||
private predicate pathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, methodcall) and
|
||||
paramFlowsThrough(p, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p) and
|
||||
pathIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, kind, innercc, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc) and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) {
|
||||
exists(ParameterNode p |
|
||||
flowIntoCallable(mid, p, cc, _, methodcall) and
|
||||
parameterValueFlowsThrough(p)
|
||||
/**
|
||||
* Holds if data may flow from `mid` through a callable to the node `out`.
|
||||
* The context `cc` is restored to its value prior to entering the callable.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
pathThroughCallable0(call, mid, kind, cc) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate valuePathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
pathIntoCallable(mid, p, cc, innercc, call) and
|
||||
parameterValueFlowsThrough(p, kind, innercc)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valuePathThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
valuePathThroughCallable0(call, mid, kind, cc) and
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowDispatch
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
private ReturnNode getAReturnNodeOfKind(ReturnKind kind) { result.getKind() = kind }
|
||||
|
||||
cached
|
||||
private module ImplCommon {
|
||||
@@ -9,19 +10,17 @@ private module ImplCommon {
|
||||
* The instance parameter is considered to have index `-1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate viableParam(Call call, int i, ParameterNode p) {
|
||||
exists(Callable callable |
|
||||
callable = viableCallable(call) and
|
||||
p.isParameterOf(callable, i)
|
||||
)
|
||||
private predicate viableParam(DataFlowCall call, int i, ParameterNode p) {
|
||||
p.isParameterOf(viableCallable(call), i)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, Call call |
|
||||
predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
|
||||
exists(int i |
|
||||
viableParam(call, i, p) and
|
||||
arg.argumentOf(call, i)
|
||||
)
|
||||
@@ -29,44 +28,167 @@ private module ImplCommon {
|
||||
|
||||
/**
|
||||
* 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 `ReturnNode` 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) {
|
||||
exists(ReturnNode ret | parameterValueFlow(p, ret))
|
||||
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 `call` 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
|
||||
argumentValueFlowsThroughNoCtx(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, _, _) |
|
||||
reducedViableImplInCallContext(_, c, 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)
|
||||
)
|
||||
}
|
||||
|
||||
private CallContextCall getAValidCallContextForParameter(ParameterNode p) {
|
||||
result = TSomeCall(p, _)
|
||||
or
|
||||
exists(DataFlowCall call, int i, DataFlowCallable callable |
|
||||
result = TSpecificCall(call, i, _) and
|
||||
p.isParameterOf(callable, i) and
|
||||
reducedViableImplInCallContext(_, callable, 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())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, ExprNode call) {
|
||||
exists(ParameterNode param |
|
||||
viableParamArg(param, arg) and
|
||||
parameterValueFlowsThrough(param) and
|
||||
arg.argumentOf(call.getExpr(), _) and
|
||||
compatibleTypes(arg.getType(), call.getType())
|
||||
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) and
|
||||
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())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -76,7 +198,7 @@ private module ImplCommon {
|
||||
*/
|
||||
cached
|
||||
predicate parameterValueFlowsToUpdate(ParameterNode p, PostUpdateNode n) {
|
||||
parameterValueFlow(p, n.getPreUpdateNode())
|
||||
parameterValueFlowNoCtx(p, n.getPreUpdateNode())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,7 +207,7 @@ private module ImplCommon {
|
||||
*/
|
||||
private predicate localValueStep(Node node1, Node node2) {
|
||||
localFlowStep(node1, node2) or
|
||||
argumentValueFlowsThrough(node1, node2)
|
||||
argumentValueFlowsThrough(node1, node2, _)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -113,7 +235,7 @@ private module ImplCommon {
|
||||
private predicate storeViaSideEffect(Node node1, Content f, PostUpdateNode node2) {
|
||||
storeStep(node1, f, node2) and readStep(_, f, _)
|
||||
or
|
||||
exists(Call call, int i1, int i2 |
|
||||
exists(DataFlowCall call, int i1, int i2 |
|
||||
setterCall(call, i1, i2, f) and
|
||||
node1.(ArgumentNode).argumentOf(call, i1) and
|
||||
node2.getPreUpdateNode().(ArgumentNode).argumentOf(call, i2) and
|
||||
@@ -125,16 +247,16 @@ 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
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate setterCall(Call call, int i1, int i2, Content f) {
|
||||
exists(Callable callable, ParameterNode p1, ParameterNode p2 |
|
||||
private predicate setterCall(DataFlowCall call, int i1, int i2, Content f) {
|
||||
exists(DataFlowCallable callable, ParameterNode p1, ParameterNode p2 |
|
||||
setterInParam(p1, f, p2) and
|
||||
callable = viableCallable(call) and
|
||||
p1.isParameterOf(callable, i1) and
|
||||
@@ -142,22 +264,36 @@ 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 |
|
||||
arg = node1 and
|
||||
viableParamArg(p, arg) and
|
||||
setterReturn(p, f) and
|
||||
arg.argumentOf(node2.asExpr(), _) and
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
storeReturn0(call, kind, node1, f) and
|
||||
node2 = getAnOutNode(call, kind) and
|
||||
compatibleTypes(node1.getTypeBound(), f.getType()) and
|
||||
compatibleTypes(node2.getTypeBound(), f.getContainerType())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate setterReturn(ParameterNode p, Content f) {
|
||||
exists(Node n1, Node n2, ReturnNode ret |
|
||||
parameterValueFlow(p, n1) and
|
||||
private predicate setterReturn(ParameterNode p, Content f, ReturnKind kind) {
|
||||
exists(Node n1, Node n2 |
|
||||
parameterValueFlowNoCtx(p, n1) and
|
||||
store(n1, f, n2) and
|
||||
localValueStep*(n2, ret)
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -169,21 +305,19 @@ private module ImplCommon {
|
||||
predicate read(Node node1, Content f, Node node2) {
|
||||
readStep(node1, f, node2) and storeStep(_, f, _)
|
||||
or
|
||||
exists(ParameterNode p, ArgumentNode arg |
|
||||
arg = node1 and
|
||||
viableParamArg(p, arg) and
|
||||
getter(p, f) and
|
||||
arg.argumentOf(node2.asExpr(), _) and
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
read0(call, kind, node1, f) and
|
||||
node2 = getAnOutNode(call, kind) and
|
||||
compatibleTypes(node1.getTypeBound(), f.getContainerType()) and
|
||||
compatibleTypes(node2.getTypeBound(), f.getType())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate getter(ParameterNode p, Content f) {
|
||||
exists(Node n1, Node n2, ReturnNode ret |
|
||||
parameterValueFlow(p, n1) and
|
||||
private predicate getter(ParameterNode p, Content f, ReturnKind kind) {
|
||||
exists(Node n1, Node n2 |
|
||||
parameterValueFlowNoCtx(p, n1) and
|
||||
read(n1, f, n2) and
|
||||
localValueStep*(n2, ret)
|
||||
localValueStep*(n2, getAReturnNodeOfKind(kind))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -200,14 +334,14 @@ private module ImplCommon {
|
||||
* Holds if `call` passes an implicit or explicit instance argument, i.e., an
|
||||
* expression that reaches a `this` parameter.
|
||||
*/
|
||||
private predicate callHasInstanceArgument(Call call) {
|
||||
private predicate callHasInstanceArgument(DataFlowCall call) {
|
||||
exists(ArgumentNode arg | arg.argumentOf(call, -1))
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TCallContext =
|
||||
TAnyCallContext() or
|
||||
TSpecificCall(Call call, int i, boolean emptyAp) {
|
||||
TSpecificCall(DataFlowCall call, int i, boolean emptyAp) {
|
||||
reducedViableImplInCallContext(_, _, call) and
|
||||
(emptyAp = true or emptyAp = false) and
|
||||
(
|
||||
@@ -217,24 +351,34 @@ private module ImplCommon {
|
||||
)
|
||||
} or
|
||||
TSomeCall(ParameterNode p, boolean emptyAp) { emptyAp = true or emptyAp = false } or
|
||||
TReturn(Method m, MethodAccess ma) { reducedViableImplInReturn(m, ma) }
|
||||
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.
|
||||
*
|
||||
* There are four cases:
|
||||
* - `TAnyCallContext()` : No restrictions on method flow.
|
||||
* - `TSpecificCall(Call call, int i)` : Flow entered through the `i`th
|
||||
* - `TSpecificCall(DataFlowCall call, int i)` : Flow entered through the `i`th
|
||||
* parameter at the given `call`. This call improves the set of viable
|
||||
* dispatch targets for at least one method call in the current callable.
|
||||
* - `TSomeCall(ParameterNode p)` : Flow entered through parameter `p`. The
|
||||
* originating call does not improve the set of dispatch targets for any
|
||||
* method call in the current callable and was therefore not recorded.
|
||||
* - `TReturn(Method m, MethodAccess ma)` : Flow reached `ma` from `m` and
|
||||
* this dispatch target of `ma` implies a reduced set of dispatch origins
|
||||
* - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and
|
||||
* this dispatch target of `call` implies a reduced set of dispatch origins
|
||||
* to which data may flow if it should reach a `return` statement.
|
||||
*/
|
||||
abstract class CallContext extends TCallContext {
|
||||
@@ -248,7 +392,11 @@ class CallContextAny extends CallContext, TAnyCallContext {
|
||||
abstract class CallContextCall extends CallContext { }
|
||||
|
||||
class CallContextSpecificCall extends CallContextCall, TSpecificCall {
|
||||
override string toString() { result = "CcCall" }
|
||||
override string toString() {
|
||||
exists(DataFlowCall call, int i | this = TSpecificCall(call, i, _) |
|
||||
result = "CcCall(" + call + ", " + i + ")"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class CallContextSomeCall extends CallContextCall, TSomeCall {
|
||||
@@ -256,23 +404,55 @@ class CallContextSomeCall extends CallContextCall, TSomeCall {
|
||||
}
|
||||
|
||||
class CallContextReturn extends CallContext, TReturn {
|
||||
override string toString() { result = "CcReturn" }
|
||||
override string toString() {
|
||||
exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
|
||||
}
|
||||
}
|
||||
|
||||
/** 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, Callable callable, Call call) {
|
||||
predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
|
||||
cc instanceof CallContextAny and callable = viableCallable(call)
|
||||
or
|
||||
exists(Method m0, MethodAccess ma0 |
|
||||
ma0.getEnclosingCallable() = callable and
|
||||
cc = TReturn(m0, ma0) and
|
||||
m0 = prunedViableImplInCallContextReverse(ma0, call)
|
||||
exists(DataFlowCallable c0, DataFlowCall call0 |
|
||||
call0.getEnclosingCallable() = callable and
|
||||
cc = TReturn(c0, call0) and
|
||||
c0 = prunedViableImplInCallContextReverse(call0, call)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[call, cc]
|
||||
Callable resolveCall(Call call, CallContext cc) {
|
||||
exists(Call ctx | cc = TSpecificCall(ctx, _, _) |
|
||||
DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
|
||||
exists(DataFlowCall ctx | cc = TSpecificCall(ctx, _, _) |
|
||||
if reducedViableImplInCallContext(call, _, ctx)
|
||||
then result = prunedViableImplInCallContext(call, ctx)
|
||||
else result = viableCallable(call)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Provides IR-specific definitions for use in the data flow library.
|
||||
*/
|
||||
module Private {
|
||||
import DataFlowPrivate
|
||||
import DataFlowDispatch
|
||||
}
|
||||
|
||||
module Public {
|
||||
import DataFlowUtil
|
||||
}
|
||||
@@ -1,37 +1,61 @@
|
||||
private import cpp
|
||||
private import DataFlowUtil
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import DataFlowDispatch
|
||||
|
||||
/**
|
||||
* A data flow node that occurs as the argument of a call and is passed as-is
|
||||
* to the callable. Instance arguments (`this` pointer) are also included.
|
||||
*/
|
||||
class ArgumentNode extends Node {
|
||||
ArgumentNode() {
|
||||
exists(CallInstruction call |
|
||||
this = call.getAnArgument()
|
||||
)
|
||||
}
|
||||
ArgumentNode() { exists(CallInstruction call | this = call.getAnArgument()) }
|
||||
|
||||
/**
|
||||
* Holds if this argument occurs at the given position in the given call.
|
||||
* The instance argument is considered to have index `-1`.
|
||||
*/
|
||||
predicate argumentOf(Call call, int pos) {
|
||||
exists (CallInstruction callInstr |
|
||||
callInstr.getAST() = call and
|
||||
(
|
||||
this = callInstr.getPositionalArgument(pos) or
|
||||
this = callInstr.getThisArgument() and pos = -1
|
||||
)
|
||||
)
|
||||
predicate argumentOf(DataFlowCall call, int pos) {
|
||||
this = call.getPositionalArgument(pos)
|
||||
or
|
||||
this = call.getThisArgument() and pos = -1
|
||||
}
|
||||
|
||||
/** Gets the call in which this node is an argument. */
|
||||
DataFlowCall getCall() { this.argumentOf(result, _) }
|
||||
}
|
||||
|
||||
private newtype TReturnKind = TNormalReturnKind()
|
||||
|
||||
/**
|
||||
* 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 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() )
|
||||
}
|
||||
ReturnNode() { exists(ReturnValueInstruction ret | this = ret.getReturnValue()) }
|
||||
|
||||
/** Gets the kind of this returned value. */
|
||||
ReturnKind getKind() { result = TNormalReturnKind() }
|
||||
}
|
||||
|
||||
/** 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 from `call` with return kind
|
||||
* `kind`.
|
||||
*/
|
||||
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
|
||||
result = call and
|
||||
kind = TNormalReturnKind()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,9 +63,7 @@ class ReturnNode extends Node {
|
||||
* calling context. For example, this would happen with flow through a
|
||||
* global or static variable.
|
||||
*/
|
||||
predicate jumpStep(Node n1, Node n2) {
|
||||
none()
|
||||
}
|
||||
predicate jumpStep(Node n1, Node n2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `call` passes an implicit or explicit qualifier, i.e., a
|
||||
@@ -53,7 +75,10 @@ predicate callHasQualifier(Call call) {
|
||||
call.getTarget() instanceof Destructor
|
||||
}
|
||||
|
||||
private newtype TContent = TFieldContent(Field f) or TCollectionContent() or TArrayContent()
|
||||
private newtype TContent =
|
||||
TFieldContent(Field f) or
|
||||
TCollectionContent() or
|
||||
TArrayContent()
|
||||
|
||||
/**
|
||||
* A reference contained in an object. Examples include instance fields, the
|
||||
@@ -62,33 +87,49 @@ private newtype TContent = TFieldContent(Field f) or TCollectionContent() or TAr
|
||||
class Content extends TContent {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
|
||||
}
|
||||
|
||||
/** Gets the type of the object containing this content. */
|
||||
abstract RefType getContainerType();
|
||||
abstract Type getContainerType();
|
||||
|
||||
/** Gets the type of this content. */
|
||||
abstract Type getType();
|
||||
}
|
||||
|
||||
private class FieldContent extends Content, TFieldContent {
|
||||
Field f;
|
||||
|
||||
FieldContent() { this = TFieldContent(f) }
|
||||
|
||||
Field getField() { result = f }
|
||||
|
||||
override string toString() { result = f.toString() }
|
||||
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
f.getLocation().hasLocationInfo(path, sl, sc, el, ec)
|
||||
}
|
||||
override RefType getContainerType() { result = f.getDeclaringType() }
|
||||
|
||||
override Type getContainerType() { result = f.getDeclaringType() }
|
||||
|
||||
override Type getType() { result = f.getType() }
|
||||
}
|
||||
|
||||
private class CollectionContent extends Content, TCollectionContent {
|
||||
override string toString() { result = "collection" }
|
||||
override RefType getContainerType() { none() }
|
||||
|
||||
override Type getContainerType() { none() }
|
||||
|
||||
override Type getType() { none() }
|
||||
}
|
||||
|
||||
private class ArrayContent extends Content, TArrayContent {
|
||||
override string toString() { result = "array" }
|
||||
override RefType getContainerType() { none() }
|
||||
|
||||
override Type getContainerType() { none() }
|
||||
|
||||
override Type getType() { none() }
|
||||
}
|
||||
|
||||
@@ -115,15 +156,13 @@ predicate readStep(Node node1, Content f, Node node2) {
|
||||
* possible flow. A single type is used for all numeric types to account for
|
||||
* numeric conversions, and otherwise the erasure is used.
|
||||
*/
|
||||
RefType getErasedRepr(Type t) {
|
||||
Type getErasedRepr(Type t) {
|
||||
suppressUnusedType(t) and
|
||||
result instanceof VoidType // stub implementation
|
||||
}
|
||||
|
||||
/** Gets a string representation of a type returned by `getErasedRepr`. */
|
||||
string ppReprType(Type t) {
|
||||
result = t.toString()
|
||||
}
|
||||
string ppReprType(Type t) { result = t.toString() }
|
||||
|
||||
/**
|
||||
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
|
||||
@@ -139,49 +178,25 @@ private predicate suppressUnusedType(Type t) { any() }
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Java QL library compatibility wrappers
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class RefType extends Type {
|
||||
/** A node that performs a type cast. */
|
||||
class CastNode extends Node {
|
||||
CastNode() { none() } // stub implementation
|
||||
}
|
||||
|
||||
class CastExpr extends Expr {
|
||||
CastExpr() { none() } // stub implementation
|
||||
}
|
||||
class DataFlowCallable = Function;
|
||||
|
||||
/** An argument to a call. */
|
||||
class Argument extends Expr {
|
||||
Call call;
|
||||
int pos;
|
||||
class DataFlowExpr = Expr;
|
||||
|
||||
Argument() {
|
||||
call.getArgument(pos) = this
|
||||
}
|
||||
class DataFlowType = Type;
|
||||
|
||||
/** Gets the call that has this argument. */
|
||||
Call getCall() { result = call }
|
||||
class DataFlowLocation = Location;
|
||||
|
||||
/** Gets the position of this argument. */
|
||||
int getPosition() {
|
||||
result = pos
|
||||
}
|
||||
}
|
||||
|
||||
class Callable extends Function { }
|
||||
|
||||
/**
|
||||
* An alias for `Function` in the C++ library. In the Java library, a `Method`
|
||||
* is any callable except a constructor.
|
||||
*/
|
||||
class Method extends Function { }
|
||||
|
||||
/**
|
||||
* An alias for `FunctionCall` in the C++ library. In the Java library, a
|
||||
* `MethodAccess` is any `Call` that does not call a constructor.
|
||||
*/
|
||||
class MethodAccess extends FunctionCall {
|
||||
/** A function call relevant for data flow. */
|
||||
class DataFlowCall extends CallInstruction, Node {
|
||||
/**
|
||||
* INTERNAL: Do not use. Alternative name for `getEnclosingFunction`.
|
||||
* Gets the nth argument for this call.
|
||||
*
|
||||
* The range of `n` is from `0` to `getNumberOfArguments() - 1`.
|
||||
*/
|
||||
Callable getEnclosingCallable() {
|
||||
result = this.getEnclosingFunction()
|
||||
}
|
||||
Node getArgument(int n) { result = this.getPositionalArgument(n) }
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/**
|
||||
* Provides C++-specific definitions for use in the data flow library.
|
||||
*/
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.IR
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
|
||||
/**
|
||||
* A node in a data flow graph.
|
||||
@@ -10,19 +11,15 @@ import semmle.code.cpp.ir.IR
|
||||
* A node can be either an expression, a parameter, or an uninitialized local
|
||||
* variable. Such nodes are created with `DataFlow::exprNode`,
|
||||
* `DataFlow::parameterNode`, and `DataFlow::uninitializedNode` respectively.
|
||||
*/
|
||||
*/
|
||||
class Node extends Instruction {
|
||||
/**
|
||||
* INTERNAL: Do not use. Alternative name for `getFunction`.
|
||||
*/
|
||||
Function getEnclosingCallable() {
|
||||
result = this.getEnclosingFunction()
|
||||
}
|
||||
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
|
||||
|
||||
/** Gets the type of this node. */
|
||||
Type getType() {
|
||||
result = this.getResultType()
|
||||
}
|
||||
Type getType() { result = this.getResultType() }
|
||||
|
||||
/**
|
||||
* Gets the non-conversion expression corresponding to this node, if any. If
|
||||
@@ -48,9 +45,7 @@ class Node extends Instruction {
|
||||
* Gets the uninitialized local variable corresponding to this node, if
|
||||
* any.
|
||||
*/
|
||||
LocalVariable asUninitialized() {
|
||||
result = this.(UninitializedInstruction).getLocalVariable()
|
||||
}
|
||||
LocalVariable asUninitialized() { result = this.(UninitializedInstruction).getLocalVariable() }
|
||||
|
||||
/**
|
||||
* Gets an upper bound on the type of this node.
|
||||
@@ -88,17 +83,14 @@ class ParameterNode extends Node, InitializeParameterInstruction {
|
||||
* Holds if this node is the parameter of `c` at the specified (zero-based)
|
||||
* position. The implicit `this` parameter is considered to have index `-1`.
|
||||
*/
|
||||
predicate isParameterOf(Function f, int i) {
|
||||
f.getParameter(i) = getParameter()
|
||||
}
|
||||
predicate isParameterOf(Function f, int i) { f.getParameter(i) = getParameter() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of an uninitialized local variable, viewed as a node in a data
|
||||
* flow graph.
|
||||
*/
|
||||
class UninitializedNode extends Node, UninitializedInstruction {
|
||||
}
|
||||
class UninitializedNode extends Node, UninitializedInstruction { }
|
||||
|
||||
/**
|
||||
* A node associated with an object after an operation that might have
|
||||
@@ -143,9 +135,7 @@ ParameterNode parameterNode(Parameter p) { result.getParameter() = p }
|
||||
* Gets the `Node` corresponding to the value of an uninitialized local
|
||||
* variable `v`.
|
||||
*/
|
||||
UninitializedNode uninitializedNode(LocalVariable v) {
|
||||
result.getLocalVariable() = v
|
||||
}
|
||||
UninitializedNode uninitializedNode(LocalVariable v) { result.getLocalVariable() = v }
|
||||
|
||||
/**
|
||||
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
|
||||
@@ -162,6 +152,4 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
* Holds if data flows from `source` to `sink` in zero or more local
|
||||
* (intra-procedural) steps.
|
||||
*/
|
||||
predicate localFlow(Node source, Node sink) {
|
||||
localFlowStep*(source, sink)
|
||||
}
|
||||
predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
|
||||
|
||||
Reference in New Issue
Block a user