mirror of
https://github.com/github/codeql.git
synced 2026-03-01 13:23:49 +01:00
Java: Generalize data-flow library in preparation for C# adoption
This commit is contained in:
@@ -1,6 +1,21 @@
|
||||
private import java
|
||||
private import DataFlowPrivate
|
||||
import semmle.code.java.dispatch.VirtualDispatch
|
||||
|
||||
/**
|
||||
* A return position. A return position describes how a value can return
|
||||
* from a given callable. For Java, this is simply a method return.
|
||||
*/
|
||||
class ReturnPosition extends Method {
|
||||
ReturnPosition() { exists(ReturnNode ret | ret.getEnclosingCallable() = this) }
|
||||
|
||||
/** Gets the callable that a value can be returned from. */
|
||||
Method getCallable() { result = this }
|
||||
|
||||
/** Gets a return node that can return a value at this position. */
|
||||
ReturnNode getAReturnNode() { result.getEnclosingCallable() = this }
|
||||
}
|
||||
|
||||
cached
|
||||
private module DispatchImpl {
|
||||
/**
|
||||
@@ -186,5 +201,17 @@ private module DispatchImpl {
|
||||
result = viableImplInCallContext(ma, ctx) and
|
||||
reducedViableImplInReturn(result, ma)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that can read the value returned at position `pos` for the
|
||||
* call `call`.
|
||||
*/
|
||||
cached
|
||||
OutNode getAnOutputAtCall(DataFlowCall call, ReturnPosition pos) {
|
||||
exists(Method m | pos.getCallable() = m |
|
||||
m = viableCallable(call) and
|
||||
result = call.getNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
import DispatchImpl
|
||||
|
||||
@@ -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,11 +148,6 @@ 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.
|
||||
*/
|
||||
@@ -209,11 +204,9 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(Method m, MethodAccess ma, ReturnNode ret |
|
||||
exists(ReturnNode ret |
|
||||
nodeCandFwd1(ret, stored, config) and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
m = viableImpl(ma) and
|
||||
node.asExpr() = ma
|
||||
node = getAnOutputAtCall(_, ret.getPosition())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -287,10 +280,9 @@ private predicate nodeCand1(Node node, boolean stored, Configuration 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(Node out |
|
||||
nodeCand1(out, stored, config) and
|
||||
out = getAnOutputAtCall(_, node.(ReturnNode).getPosition())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -316,11 +308,13 @@ 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)
|
||||
not parameterValueFlowsThrough(p, _)
|
||||
or
|
||||
nodeCand1(node, false, unbind(config)) and
|
||||
exists(Node mid |
|
||||
@@ -367,21 +361,21 @@ private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Con
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, ExprNode call, RefType t, Configuration config
|
||||
ArgumentNode arg, Node out, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode param, ReturnNode ret |
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
nodeCand1(call, false, unbind(config)) and
|
||||
nodeCand1(out, false, unbind(config)) and
|
||||
viableParamArg(param, arg) and
|
||||
simpleParameterFlow(param, ret, t, config) and
|
||||
arg.argumentOf(call.getExpr(), _)
|
||||
out = getAnOutputAtCall(arg.getCall(), ret.getPosition())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a method.
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a callable.
|
||||
*/
|
||||
private predicate flowThroughMethod(
|
||||
private predicate flowThroughCallableCand1(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
) {
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
|
||||
@@ -391,13 +385,13 @@ private predicate flowThroughMethod(
|
||||
|
||||
/**
|
||||
* 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)
|
||||
flowThroughCallableCand1(node1, node2, preservesValue, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -405,7 +399,7 @@ private predicate localFlowStepOrFlowThroughMethod(
|
||||
* through a `ReturnNode` or through an argument that has been mutated, and
|
||||
* that this step is part of a path from a source to a sink.
|
||||
*/
|
||||
private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) {
|
||||
private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration config) {
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config) and
|
||||
(
|
||||
@@ -415,13 +409,8 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
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
|
||||
node2 = getAnOutputAtCall(_, node1.(ReturnNode).getPosition())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -429,7 +418,7 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
* Holds if data can flow into a callable and that this step is part of a
|
||||
* path from a source to a sink.
|
||||
*/
|
||||
private predicate flowIntoCallable(Node node1, Node node2, Configuration config) {
|
||||
private predicate flowIntoCallableCand1(Node node1, Node node2, Configuration config) {
|
||||
viableParamArg(node2, node1) and
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config)
|
||||
@@ -441,7 +430,9 @@ private predicate flowIntoCallable(Node node1, Node node2, Configuration config)
|
||||
* contexts.
|
||||
*/
|
||||
private int branch(Node n1, Configuration conf) {
|
||||
result = strictcount(Node n | flowOutOfCallable(n1, n, conf) or flowIntoCallable(n1, n, conf))
|
||||
result = strictcount(Node n |
|
||||
flowOutOfCallableCand1(n1, n, conf) or flowIntoCallableCand1(n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -450,7 +441,9 @@ private int branch(Node n1, Configuration conf) {
|
||||
* contexts.
|
||||
*/
|
||||
private int join(Node n2, Configuration conf) {
|
||||
result = strictcount(Node n | flowOutOfCallable(n, n2, conf) or flowIntoCallable(n, n2, conf))
|
||||
result = strictcount(Node n |
|
||||
flowOutOfCallableCand1(n, n2, conf) or flowIntoCallableCand1(n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -460,10 +453,10 @@ private int join(Node n2, Configuration conf) {
|
||||
* `allowsFieldFlow` flag indicates whether the branching is within the limit
|
||||
* specified by the configuration.
|
||||
*/
|
||||
private predicate flowOutOfCallable(
|
||||
private predicate flowOutOfCallableCand1(
|
||||
Node node1, Node node2, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCallable(node1, node2, config) and
|
||||
flowOutOfCallableCand1(node1, node2, config) and
|
||||
exists(int b, int j |
|
||||
b = branch(node1, config) and
|
||||
j = join(node2, config) and
|
||||
@@ -478,10 +471,10 @@ private predicate flowOutOfCallable(
|
||||
* path from a source to a sink. The `allowsFieldFlow` flag indicates whether
|
||||
* the branching is within the limit specified by the configuration.
|
||||
*/
|
||||
private predicate flowIntoCallable(
|
||||
private predicate flowIntoCallableCand1(
|
||||
Node node1, Node node2, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallable(node1, node2, config) and
|
||||
flowIntoCallableCand1(node1, node2, config) and
|
||||
exists(int b, int j |
|
||||
b = branch(node1, config) and
|
||||
j = join(node2, config) and
|
||||
@@ -505,7 +498,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
|
||||
@@ -534,14 +527,14 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
nodeCandFwd2(mid, _, stored, config) and
|
||||
flowIntoCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = true and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
nodeCandFwd2(mid, false, stored, config) and
|
||||
flowOutOfCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = false and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
)
|
||||
@@ -574,7 +567,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)
|
||||
)
|
||||
@@ -603,14 +596,14 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowIntoCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
nodeCand2(mid, false, stored, config) and
|
||||
toReturn = false and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowOutOfCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
nodeCand2(mid, _, stored, config) and
|
||||
toReturn = true and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
@@ -663,10 +656,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -677,14 +670,14 @@ private predicate localFlowEntry(Node node, Configuration config) {
|
||||
private predicate localFlowExit(Node node, Configuration config) {
|
||||
exists(Node next | nodeCand(next, config) |
|
||||
jumpStep(node, next, _, config) or
|
||||
flowIntoCallable(node, next, config) or
|
||||
flowOutOfCallable(node, next, config) or
|
||||
flowThroughMethod(node, next, _, config) or
|
||||
flowIntoCallableCand1(node, next, config) or
|
||||
flowOutOfCallableCand1(node, next, config) or
|
||||
flowThroughCallableCand1(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 +699,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 +718,7 @@ private predicate localFlowBigStep(
|
||||
}
|
||||
|
||||
private newtype TAccessPathFront =
|
||||
TFrontNil(Type t) or
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(Content f)
|
||||
|
||||
/**
|
||||
@@ -733,12 +726,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 +748,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
|
||||
}
|
||||
}
|
||||
@@ -805,21 +798,21 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowCandFwd(mid, _, apf, config) and
|
||||
flowIntoCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = true and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowCandFwd(mid, false, apf, config) and
|
||||
flowOutOfCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = false and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallableCand1(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
or
|
||||
@@ -901,21 +894,21 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowIntoCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flowCand(mid, false, apf, config) and
|
||||
toReturn = false and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowOutOfCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flowCand(mid, _, apf, config) and
|
||||
toReturn = true and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallableCand1(node, mid, preservesValue, config) and
|
||||
flowCand(mid, toReturn, apf0, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
@@ -954,7 +947,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 +968,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 +978,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 {
|
||||
@@ -1069,21 +1064,21 @@ private predicate flowFwd0(
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowFwd(mid, _, apf, ap, config) and
|
||||
flowIntoCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = true and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowFwd(mid, false, apf, ap, config) and
|
||||
flowOutOfCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = false and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0, AccessPath ap0 |
|
||||
flowFwd(mid, fromArg, apf0, ap0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallableCand1(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0 and apf = apf0
|
||||
or
|
||||
@@ -1182,21 +1177,21 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowIntoCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flow(mid, false, ap, config) and
|
||||
toReturn = false and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowOutOfCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flow(mid, _, ap, config) and
|
||||
toReturn = true and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPath ap0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallableCand1(node, mid, preservesValue, config) and
|
||||
flow(mid, toReturn, ap0, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0
|
||||
@@ -1274,8 +1269,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 +1285,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)
|
||||
|
||||
@@ -1337,7 +1349,7 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNode getSucc() {
|
||||
override PathNode getASuccessor() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -1391,7 +1403,7 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getSucc() { none() }
|
||||
override PathNode getASuccessor() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1425,11 +1437,9 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
flowIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
or
|
||||
flowOutOfMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
flowOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowThroughMethod(mid, node.asExpr(), cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
or
|
||||
valueFlowThroughMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
flowThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
@@ -1450,36 +1460,35 @@ private predicate contentStoreStep(
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
* Holds if data may flow from `mid` to a return at position `pos` in the
|
||||
* context `innercc`, and the path did not flow through a parameter.
|
||||
*/
|
||||
private predicate 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 flowOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
|
||||
pos.getAReturnNode() = mid.getNode() and
|
||||
innercc = mid.getCallContext() and
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
private predicate flowOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, DataFlowCall call, CallContext innercc |
|
||||
flowOutOfCallable0(mid, pos, innercc) and
|
||||
out = getAnOutputAtCall(call, pos) and
|
||||
c = pos.getCallable() and
|
||||
resolveReturn(innercc, c, call)
|
||||
|
|
||||
if reducedViableImplInReturn(m, ma) then cc = TReturn(m, ma) else cc = TAnyCallContext()
|
||||
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate flowOutOfArgument(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 +1509,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 flowIntoArg(
|
||||
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 +1525,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)
|
||||
@@ -1523,7 +1534,8 @@ 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
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
boolean emptyAp
|
||||
) {
|
||||
flowIntoArg(mid, i, outercc, call, emptyAp) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
@@ -1536,9 +1548,9 @@ private predicate flowIntoCallable0(
|
||||
* respectively.
|
||||
*/
|
||||
private predicate flowIntoCallable(
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, Call call
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, DataFlowCall call
|
||||
) {
|
||||
exists(int i, Callable callable, boolean emptyAp |
|
||||
exists(int i, DataFlowCallable callable, boolean emptyAp |
|
||||
flowIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
@@ -1548,42 +1560,32 @@ 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 at position `pos`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configuration config) {
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
mid.getNode() = ret and
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnPosition pos, CallContextCall cc, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid |
|
||||
mid.getNode() = pos.getAReturnNode() and
|
||||
cc = mid.getCallContext() and
|
||||
config = mid.getConfiguration() and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
config = mid.getConfiguration()
|
||||
|
|
||||
cc = TSomeCall(p, true)
|
||||
or
|
||||
exists(int i | cc = TSpecificCall(_, i, true) |
|
||||
p.isParameterOf(returnNodeGetEnclosingCallable(ret), i)
|
||||
)
|
||||
exists(int i | cc = TSpecificCall(_, i, true) | p.isParameterOf(pos.getCallable(), i))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
* 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 flowThroughMethod(PathNodeMid mid, Call methodcall, 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
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) {
|
||||
exists(ParameterNode p |
|
||||
flowIntoCallable(mid, p, cc, _, methodcall) and
|
||||
parameterValueFlowsThrough(p)
|
||||
private predicate flowThroughCallable(PathNodeMid mid, Node out, CallContext cc) {
|
||||
exists(DataFlowCall call, ParameterNode p, ReturnPosition pos, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, pos, innercc, unbind(mid.getConfiguration())) and
|
||||
out = getAnOutputAtCall(call, pos)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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,11 +148,6 @@ 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.
|
||||
*/
|
||||
@@ -209,11 +204,9 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(Method m, MethodAccess ma, ReturnNode ret |
|
||||
exists(ReturnNode ret |
|
||||
nodeCandFwd1(ret, stored, config) and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
m = viableImpl(ma) and
|
||||
node.asExpr() = ma
|
||||
node = getAnOutputAtCall(_, ret.getPosition())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -287,10 +280,9 @@ private predicate nodeCand1(Node node, boolean stored, Configuration 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(Node out |
|
||||
nodeCand1(out, stored, config) and
|
||||
out = getAnOutputAtCall(_, node.(ReturnNode).getPosition())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -316,11 +308,13 @@ 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)
|
||||
not parameterValueFlowsThrough(p, _)
|
||||
or
|
||||
nodeCand1(node, false, unbind(config)) and
|
||||
exists(Node mid |
|
||||
@@ -367,21 +361,21 @@ private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Con
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, ExprNode call, RefType t, Configuration config
|
||||
ArgumentNode arg, Node out, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode param, ReturnNode ret |
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
nodeCand1(call, false, unbind(config)) and
|
||||
nodeCand1(out, false, unbind(config)) and
|
||||
viableParamArg(param, arg) and
|
||||
simpleParameterFlow(param, ret, t, config) and
|
||||
arg.argumentOf(call.getExpr(), _)
|
||||
out = getAnOutputAtCall(arg.getCall(), ret.getPosition())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a method.
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a callable.
|
||||
*/
|
||||
private predicate flowThroughMethod(
|
||||
private predicate flowThroughCallableCand1(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
) {
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
|
||||
@@ -391,13 +385,13 @@ private predicate flowThroughMethod(
|
||||
|
||||
/**
|
||||
* 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)
|
||||
flowThroughCallableCand1(node1, node2, preservesValue, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -405,7 +399,7 @@ private predicate localFlowStepOrFlowThroughMethod(
|
||||
* through a `ReturnNode` or through an argument that has been mutated, and
|
||||
* that this step is part of a path from a source to a sink.
|
||||
*/
|
||||
private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) {
|
||||
private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration config) {
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config) and
|
||||
(
|
||||
@@ -415,13 +409,8 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
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
|
||||
node2 = getAnOutputAtCall(_, node1.(ReturnNode).getPosition())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -429,7 +418,7 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
* Holds if data can flow into a callable and that this step is part of a
|
||||
* path from a source to a sink.
|
||||
*/
|
||||
private predicate flowIntoCallable(Node node1, Node node2, Configuration config) {
|
||||
private predicate flowIntoCallableCand1(Node node1, Node node2, Configuration config) {
|
||||
viableParamArg(node2, node1) and
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config)
|
||||
@@ -441,7 +430,9 @@ private predicate flowIntoCallable(Node node1, Node node2, Configuration config)
|
||||
* contexts.
|
||||
*/
|
||||
private int branch(Node n1, Configuration conf) {
|
||||
result = strictcount(Node n | flowOutOfCallable(n1, n, conf) or flowIntoCallable(n1, n, conf))
|
||||
result = strictcount(Node n |
|
||||
flowOutOfCallableCand1(n1, n, conf) or flowIntoCallableCand1(n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -450,7 +441,9 @@ private int branch(Node n1, Configuration conf) {
|
||||
* contexts.
|
||||
*/
|
||||
private int join(Node n2, Configuration conf) {
|
||||
result = strictcount(Node n | flowOutOfCallable(n, n2, conf) or flowIntoCallable(n, n2, conf))
|
||||
result = strictcount(Node n |
|
||||
flowOutOfCallableCand1(n, n2, conf) or flowIntoCallableCand1(n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -460,10 +453,10 @@ private int join(Node n2, Configuration conf) {
|
||||
* `allowsFieldFlow` flag indicates whether the branching is within the limit
|
||||
* specified by the configuration.
|
||||
*/
|
||||
private predicate flowOutOfCallable(
|
||||
private predicate flowOutOfCallableCand1(
|
||||
Node node1, Node node2, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCallable(node1, node2, config) and
|
||||
flowOutOfCallableCand1(node1, node2, config) and
|
||||
exists(int b, int j |
|
||||
b = branch(node1, config) and
|
||||
j = join(node2, config) and
|
||||
@@ -478,10 +471,10 @@ private predicate flowOutOfCallable(
|
||||
* path from a source to a sink. The `allowsFieldFlow` flag indicates whether
|
||||
* the branching is within the limit specified by the configuration.
|
||||
*/
|
||||
private predicate flowIntoCallable(
|
||||
private predicate flowIntoCallableCand1(
|
||||
Node node1, Node node2, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallable(node1, node2, config) and
|
||||
flowIntoCallableCand1(node1, node2, config) and
|
||||
exists(int b, int j |
|
||||
b = branch(node1, config) and
|
||||
j = join(node2, config) and
|
||||
@@ -505,7 +498,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
|
||||
@@ -534,14 +527,14 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
nodeCandFwd2(mid, _, stored, config) and
|
||||
flowIntoCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = true and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
nodeCandFwd2(mid, false, stored, config) and
|
||||
flowOutOfCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = false and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
)
|
||||
@@ -574,7 +567,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)
|
||||
)
|
||||
@@ -603,14 +596,14 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowIntoCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
nodeCand2(mid, false, stored, config) and
|
||||
toReturn = false and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowOutOfCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
nodeCand2(mid, _, stored, config) and
|
||||
toReturn = true and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
@@ -663,10 +656,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -677,14 +670,14 @@ private predicate localFlowEntry(Node node, Configuration config) {
|
||||
private predicate localFlowExit(Node node, Configuration config) {
|
||||
exists(Node next | nodeCand(next, config) |
|
||||
jumpStep(node, next, _, config) or
|
||||
flowIntoCallable(node, next, config) or
|
||||
flowOutOfCallable(node, next, config) or
|
||||
flowThroughMethod(node, next, _, config) or
|
||||
flowIntoCallableCand1(node, next, config) or
|
||||
flowOutOfCallableCand1(node, next, config) or
|
||||
flowThroughCallableCand1(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 +699,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 +718,7 @@ private predicate localFlowBigStep(
|
||||
}
|
||||
|
||||
private newtype TAccessPathFront =
|
||||
TFrontNil(Type t) or
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(Content f)
|
||||
|
||||
/**
|
||||
@@ -733,12 +726,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 +748,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
|
||||
}
|
||||
}
|
||||
@@ -805,21 +798,21 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowCandFwd(mid, _, apf, config) and
|
||||
flowIntoCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = true and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowCandFwd(mid, false, apf, config) and
|
||||
flowOutOfCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = false and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallableCand1(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
or
|
||||
@@ -901,21 +894,21 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowIntoCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flowCand(mid, false, apf, config) and
|
||||
toReturn = false and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowOutOfCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flowCand(mid, _, apf, config) and
|
||||
toReturn = true and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallableCand1(node, mid, preservesValue, config) and
|
||||
flowCand(mid, toReturn, apf0, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
@@ -954,7 +947,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 +968,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 +978,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 {
|
||||
@@ -1069,21 +1064,21 @@ private predicate flowFwd0(
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowFwd(mid, _, apf, ap, config) and
|
||||
flowIntoCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = true and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowFwd(mid, false, apf, ap, config) and
|
||||
flowOutOfCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = false and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0, AccessPath ap0 |
|
||||
flowFwd(mid, fromArg, apf0, ap0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallableCand1(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0 and apf = apf0
|
||||
or
|
||||
@@ -1182,21 +1177,21 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowIntoCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flow(mid, false, ap, config) and
|
||||
toReturn = false and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowOutOfCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flow(mid, _, ap, config) and
|
||||
toReturn = true and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPath ap0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallableCand1(node, mid, preservesValue, config) and
|
||||
flow(mid, toReturn, ap0, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0
|
||||
@@ -1274,8 +1269,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 +1285,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)
|
||||
|
||||
@@ -1337,7 +1349,7 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNode getSucc() {
|
||||
override PathNode getASuccessor() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -1391,7 +1403,7 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getSucc() { none() }
|
||||
override PathNode getASuccessor() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1425,11 +1437,9 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
flowIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
or
|
||||
flowOutOfMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
flowOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowThroughMethod(mid, node.asExpr(), cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
or
|
||||
valueFlowThroughMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
flowThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
@@ -1450,36 +1460,35 @@ private predicate contentStoreStep(
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
* Holds if data may flow from `mid` to a return at position `pos` in the
|
||||
* context `innercc`, and the path did not flow through a parameter.
|
||||
*/
|
||||
private predicate 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 flowOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
|
||||
pos.getAReturnNode() = mid.getNode() and
|
||||
innercc = mid.getCallContext() and
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
private predicate flowOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, DataFlowCall call, CallContext innercc |
|
||||
flowOutOfCallable0(mid, pos, innercc) and
|
||||
out = getAnOutputAtCall(call, pos) and
|
||||
c = pos.getCallable() and
|
||||
resolveReturn(innercc, c, call)
|
||||
|
|
||||
if reducedViableImplInReturn(m, ma) then cc = TReturn(m, ma) else cc = TAnyCallContext()
|
||||
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate flowOutOfArgument(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 +1509,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 flowIntoArg(
|
||||
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 +1525,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)
|
||||
@@ -1523,7 +1534,8 @@ 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
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
boolean emptyAp
|
||||
) {
|
||||
flowIntoArg(mid, i, outercc, call, emptyAp) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
@@ -1536,9 +1548,9 @@ private predicate flowIntoCallable0(
|
||||
* respectively.
|
||||
*/
|
||||
private predicate flowIntoCallable(
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, Call call
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, DataFlowCall call
|
||||
) {
|
||||
exists(int i, Callable callable, boolean emptyAp |
|
||||
exists(int i, DataFlowCallable callable, boolean emptyAp |
|
||||
flowIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
@@ -1548,42 +1560,32 @@ 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 at position `pos`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configuration config) {
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
mid.getNode() = ret and
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnPosition pos, CallContextCall cc, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid |
|
||||
mid.getNode() = pos.getAReturnNode() and
|
||||
cc = mid.getCallContext() and
|
||||
config = mid.getConfiguration() and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
config = mid.getConfiguration()
|
||||
|
|
||||
cc = TSomeCall(p, true)
|
||||
or
|
||||
exists(int i | cc = TSpecificCall(_, i, true) |
|
||||
p.isParameterOf(returnNodeGetEnclosingCallable(ret), i)
|
||||
)
|
||||
exists(int i | cc = TSpecificCall(_, i, true) | p.isParameterOf(pos.getCallable(), i))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
* 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 flowThroughMethod(PathNodeMid mid, Call methodcall, 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
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) {
|
||||
exists(ParameterNode p |
|
||||
flowIntoCallable(mid, p, cc, _, methodcall) and
|
||||
parameterValueFlowsThrough(p)
|
||||
private predicate flowThroughCallable(PathNodeMid mid, Node out, CallContext cc) {
|
||||
exists(DataFlowCall call, ParameterNode p, ReturnPosition pos, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, pos, innercc, unbind(mid.getConfiguration())) and
|
||||
out = getAnOutputAtCall(call, pos)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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,11 +148,6 @@ 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.
|
||||
*/
|
||||
@@ -209,11 +204,9 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(Method m, MethodAccess ma, ReturnNode ret |
|
||||
exists(ReturnNode ret |
|
||||
nodeCandFwd1(ret, stored, config) and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
m = viableImpl(ma) and
|
||||
node.asExpr() = ma
|
||||
node = getAnOutputAtCall(_, ret.getPosition())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -287,10 +280,9 @@ private predicate nodeCand1(Node node, boolean stored, Configuration 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(Node out |
|
||||
nodeCand1(out, stored, config) and
|
||||
out = getAnOutputAtCall(_, node.(ReturnNode).getPosition())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -316,11 +308,13 @@ 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)
|
||||
not parameterValueFlowsThrough(p, _)
|
||||
or
|
||||
nodeCand1(node, false, unbind(config)) and
|
||||
exists(Node mid |
|
||||
@@ -367,21 +361,21 @@ private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Con
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, ExprNode call, RefType t, Configuration config
|
||||
ArgumentNode arg, Node out, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode param, ReturnNode ret |
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
nodeCand1(call, false, unbind(config)) and
|
||||
nodeCand1(out, false, unbind(config)) and
|
||||
viableParamArg(param, arg) and
|
||||
simpleParameterFlow(param, ret, t, config) and
|
||||
arg.argumentOf(call.getExpr(), _)
|
||||
out = getAnOutputAtCall(arg.getCall(), ret.getPosition())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a method.
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a callable.
|
||||
*/
|
||||
private predicate flowThroughMethod(
|
||||
private predicate flowThroughCallableCand1(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
) {
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
|
||||
@@ -391,13 +385,13 @@ private predicate flowThroughMethod(
|
||||
|
||||
/**
|
||||
* 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)
|
||||
flowThroughCallableCand1(node1, node2, preservesValue, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -405,7 +399,7 @@ private predicate localFlowStepOrFlowThroughMethod(
|
||||
* through a `ReturnNode` or through an argument that has been mutated, and
|
||||
* that this step is part of a path from a source to a sink.
|
||||
*/
|
||||
private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) {
|
||||
private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration config) {
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config) and
|
||||
(
|
||||
@@ -415,13 +409,8 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
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
|
||||
node2 = getAnOutputAtCall(_, node1.(ReturnNode).getPosition())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -429,7 +418,7 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
* Holds if data can flow into a callable and that this step is part of a
|
||||
* path from a source to a sink.
|
||||
*/
|
||||
private predicate flowIntoCallable(Node node1, Node node2, Configuration config) {
|
||||
private predicate flowIntoCallableCand1(Node node1, Node node2, Configuration config) {
|
||||
viableParamArg(node2, node1) and
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config)
|
||||
@@ -441,7 +430,9 @@ private predicate flowIntoCallable(Node node1, Node node2, Configuration config)
|
||||
* contexts.
|
||||
*/
|
||||
private int branch(Node n1, Configuration conf) {
|
||||
result = strictcount(Node n | flowOutOfCallable(n1, n, conf) or flowIntoCallable(n1, n, conf))
|
||||
result = strictcount(Node n |
|
||||
flowOutOfCallableCand1(n1, n, conf) or flowIntoCallableCand1(n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -450,7 +441,9 @@ private int branch(Node n1, Configuration conf) {
|
||||
* contexts.
|
||||
*/
|
||||
private int join(Node n2, Configuration conf) {
|
||||
result = strictcount(Node n | flowOutOfCallable(n, n2, conf) or flowIntoCallable(n, n2, conf))
|
||||
result = strictcount(Node n |
|
||||
flowOutOfCallableCand1(n, n2, conf) or flowIntoCallableCand1(n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -460,10 +453,10 @@ private int join(Node n2, Configuration conf) {
|
||||
* `allowsFieldFlow` flag indicates whether the branching is within the limit
|
||||
* specified by the configuration.
|
||||
*/
|
||||
private predicate flowOutOfCallable(
|
||||
private predicate flowOutOfCallableCand1(
|
||||
Node node1, Node node2, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCallable(node1, node2, config) and
|
||||
flowOutOfCallableCand1(node1, node2, config) and
|
||||
exists(int b, int j |
|
||||
b = branch(node1, config) and
|
||||
j = join(node2, config) and
|
||||
@@ -478,10 +471,10 @@ private predicate flowOutOfCallable(
|
||||
* path from a source to a sink. The `allowsFieldFlow` flag indicates whether
|
||||
* the branching is within the limit specified by the configuration.
|
||||
*/
|
||||
private predicate flowIntoCallable(
|
||||
private predicate flowIntoCallableCand1(
|
||||
Node node1, Node node2, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallable(node1, node2, config) and
|
||||
flowIntoCallableCand1(node1, node2, config) and
|
||||
exists(int b, int j |
|
||||
b = branch(node1, config) and
|
||||
j = join(node2, config) and
|
||||
@@ -505,7 +498,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
|
||||
@@ -534,14 +527,14 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
nodeCandFwd2(mid, _, stored, config) and
|
||||
flowIntoCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = true and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
nodeCandFwd2(mid, false, stored, config) and
|
||||
flowOutOfCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = false and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
)
|
||||
@@ -574,7 +567,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)
|
||||
)
|
||||
@@ -603,14 +596,14 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowIntoCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
nodeCand2(mid, false, stored, config) and
|
||||
toReturn = false and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowOutOfCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
nodeCand2(mid, _, stored, config) and
|
||||
toReturn = true and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
@@ -663,10 +656,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -677,14 +670,14 @@ private predicate localFlowEntry(Node node, Configuration config) {
|
||||
private predicate localFlowExit(Node node, Configuration config) {
|
||||
exists(Node next | nodeCand(next, config) |
|
||||
jumpStep(node, next, _, config) or
|
||||
flowIntoCallable(node, next, config) or
|
||||
flowOutOfCallable(node, next, config) or
|
||||
flowThroughMethod(node, next, _, config) or
|
||||
flowIntoCallableCand1(node, next, config) or
|
||||
flowOutOfCallableCand1(node, next, config) or
|
||||
flowThroughCallableCand1(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 +699,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 +718,7 @@ private predicate localFlowBigStep(
|
||||
}
|
||||
|
||||
private newtype TAccessPathFront =
|
||||
TFrontNil(Type t) or
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(Content f)
|
||||
|
||||
/**
|
||||
@@ -733,12 +726,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 +748,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
|
||||
}
|
||||
}
|
||||
@@ -805,21 +798,21 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowCandFwd(mid, _, apf, config) and
|
||||
flowIntoCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = true and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowCandFwd(mid, false, apf, config) and
|
||||
flowOutOfCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = false and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallableCand1(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
or
|
||||
@@ -901,21 +894,21 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowIntoCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flowCand(mid, false, apf, config) and
|
||||
toReturn = false and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowOutOfCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flowCand(mid, _, apf, config) and
|
||||
toReturn = true and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallableCand1(node, mid, preservesValue, config) and
|
||||
flowCand(mid, toReturn, apf0, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
@@ -954,7 +947,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 +968,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 +978,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 {
|
||||
@@ -1069,21 +1064,21 @@ private predicate flowFwd0(
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowFwd(mid, _, apf, ap, config) and
|
||||
flowIntoCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = true and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowFwd(mid, false, apf, ap, config) and
|
||||
flowOutOfCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = false and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0, AccessPath ap0 |
|
||||
flowFwd(mid, fromArg, apf0, ap0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallableCand1(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0 and apf = apf0
|
||||
or
|
||||
@@ -1182,21 +1177,21 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowIntoCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flow(mid, false, ap, config) and
|
||||
toReturn = false and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowOutOfCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flow(mid, _, ap, config) and
|
||||
toReturn = true and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPath ap0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallableCand1(node, mid, preservesValue, config) and
|
||||
flow(mid, toReturn, ap0, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0
|
||||
@@ -1274,8 +1269,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 +1285,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)
|
||||
|
||||
@@ -1337,7 +1349,7 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNode getSucc() {
|
||||
override PathNode getASuccessor() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -1391,7 +1403,7 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getSucc() { none() }
|
||||
override PathNode getASuccessor() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1425,11 +1437,9 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
flowIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
or
|
||||
flowOutOfMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
flowOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowThroughMethod(mid, node.asExpr(), cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
or
|
||||
valueFlowThroughMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
flowThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
@@ -1450,36 +1460,35 @@ private predicate contentStoreStep(
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
* Holds if data may flow from `mid` to a return at position `pos` in the
|
||||
* context `innercc`, and the path did not flow through a parameter.
|
||||
*/
|
||||
private predicate 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 flowOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
|
||||
pos.getAReturnNode() = mid.getNode() and
|
||||
innercc = mid.getCallContext() and
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
private predicate flowOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, DataFlowCall call, CallContext innercc |
|
||||
flowOutOfCallable0(mid, pos, innercc) and
|
||||
out = getAnOutputAtCall(call, pos) and
|
||||
c = pos.getCallable() and
|
||||
resolveReturn(innercc, c, call)
|
||||
|
|
||||
if reducedViableImplInReturn(m, ma) then cc = TReturn(m, ma) else cc = TAnyCallContext()
|
||||
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate flowOutOfArgument(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 +1509,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 flowIntoArg(
|
||||
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 +1525,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)
|
||||
@@ -1523,7 +1534,8 @@ 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
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
boolean emptyAp
|
||||
) {
|
||||
flowIntoArg(mid, i, outercc, call, emptyAp) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
@@ -1536,9 +1548,9 @@ private predicate flowIntoCallable0(
|
||||
* respectively.
|
||||
*/
|
||||
private predicate flowIntoCallable(
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, Call call
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, DataFlowCall call
|
||||
) {
|
||||
exists(int i, Callable callable, boolean emptyAp |
|
||||
exists(int i, DataFlowCallable callable, boolean emptyAp |
|
||||
flowIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
@@ -1548,42 +1560,32 @@ 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 at position `pos`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configuration config) {
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
mid.getNode() = ret and
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnPosition pos, CallContextCall cc, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid |
|
||||
mid.getNode() = pos.getAReturnNode() and
|
||||
cc = mid.getCallContext() and
|
||||
config = mid.getConfiguration() and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
config = mid.getConfiguration()
|
||||
|
|
||||
cc = TSomeCall(p, true)
|
||||
or
|
||||
exists(int i | cc = TSpecificCall(_, i, true) |
|
||||
p.isParameterOf(returnNodeGetEnclosingCallable(ret), i)
|
||||
)
|
||||
exists(int i | cc = TSpecificCall(_, i, true) | p.isParameterOf(pos.getCallable(), i))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
* 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 flowThroughMethod(PathNodeMid mid, Call methodcall, 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
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) {
|
||||
exists(ParameterNode p |
|
||||
flowIntoCallable(mid, p, cc, _, methodcall) and
|
||||
parameterValueFlowsThrough(p)
|
||||
private predicate flowThroughCallable(PathNodeMid mid, Node out, CallContext cc) {
|
||||
exists(DataFlowCall call, ParameterNode p, ReturnPosition pos, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, pos, innercc, unbind(mid.getConfiguration())) and
|
||||
out = getAnOutputAtCall(call, pos)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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,11 +148,6 @@ 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.
|
||||
*/
|
||||
@@ -209,11 +204,9 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(Method m, MethodAccess ma, ReturnNode ret |
|
||||
exists(ReturnNode ret |
|
||||
nodeCandFwd1(ret, stored, config) and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
m = viableImpl(ma) and
|
||||
node.asExpr() = ma
|
||||
node = getAnOutputAtCall(_, ret.getPosition())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -287,10 +280,9 @@ private predicate nodeCand1(Node node, boolean stored, Configuration 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(Node out |
|
||||
nodeCand1(out, stored, config) and
|
||||
out = getAnOutputAtCall(_, node.(ReturnNode).getPosition())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -316,11 +308,13 @@ 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)
|
||||
not parameterValueFlowsThrough(p, _)
|
||||
or
|
||||
nodeCand1(node, false, unbind(config)) and
|
||||
exists(Node mid |
|
||||
@@ -367,21 +361,21 @@ private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Con
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, ExprNode call, RefType t, Configuration config
|
||||
ArgumentNode arg, Node out, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode param, ReturnNode ret |
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
nodeCand1(call, false, unbind(config)) and
|
||||
nodeCand1(out, false, unbind(config)) and
|
||||
viableParamArg(param, arg) and
|
||||
simpleParameterFlow(param, ret, t, config) and
|
||||
arg.argumentOf(call.getExpr(), _)
|
||||
out = getAnOutputAtCall(arg.getCall(), ret.getPosition())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a method.
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a callable.
|
||||
*/
|
||||
private predicate flowThroughMethod(
|
||||
private predicate flowThroughCallableCand1(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
) {
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
|
||||
@@ -391,13 +385,13 @@ private predicate flowThroughMethod(
|
||||
|
||||
/**
|
||||
* 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)
|
||||
flowThroughCallableCand1(node1, node2, preservesValue, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -405,7 +399,7 @@ private predicate localFlowStepOrFlowThroughMethod(
|
||||
* through a `ReturnNode` or through an argument that has been mutated, and
|
||||
* that this step is part of a path from a source to a sink.
|
||||
*/
|
||||
private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) {
|
||||
private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration config) {
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config) and
|
||||
(
|
||||
@@ -415,13 +409,8 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
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
|
||||
node2 = getAnOutputAtCall(_, node1.(ReturnNode).getPosition())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -429,7 +418,7 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
* Holds if data can flow into a callable and that this step is part of a
|
||||
* path from a source to a sink.
|
||||
*/
|
||||
private predicate flowIntoCallable(Node node1, Node node2, Configuration config) {
|
||||
private predicate flowIntoCallableCand1(Node node1, Node node2, Configuration config) {
|
||||
viableParamArg(node2, node1) and
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config)
|
||||
@@ -441,7 +430,9 @@ private predicate flowIntoCallable(Node node1, Node node2, Configuration config)
|
||||
* contexts.
|
||||
*/
|
||||
private int branch(Node n1, Configuration conf) {
|
||||
result = strictcount(Node n | flowOutOfCallable(n1, n, conf) or flowIntoCallable(n1, n, conf))
|
||||
result = strictcount(Node n |
|
||||
flowOutOfCallableCand1(n1, n, conf) or flowIntoCallableCand1(n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -450,7 +441,9 @@ private int branch(Node n1, Configuration conf) {
|
||||
* contexts.
|
||||
*/
|
||||
private int join(Node n2, Configuration conf) {
|
||||
result = strictcount(Node n | flowOutOfCallable(n, n2, conf) or flowIntoCallable(n, n2, conf))
|
||||
result = strictcount(Node n |
|
||||
flowOutOfCallableCand1(n, n2, conf) or flowIntoCallableCand1(n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -460,10 +453,10 @@ private int join(Node n2, Configuration conf) {
|
||||
* `allowsFieldFlow` flag indicates whether the branching is within the limit
|
||||
* specified by the configuration.
|
||||
*/
|
||||
private predicate flowOutOfCallable(
|
||||
private predicate flowOutOfCallableCand1(
|
||||
Node node1, Node node2, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCallable(node1, node2, config) and
|
||||
flowOutOfCallableCand1(node1, node2, config) and
|
||||
exists(int b, int j |
|
||||
b = branch(node1, config) and
|
||||
j = join(node2, config) and
|
||||
@@ -478,10 +471,10 @@ private predicate flowOutOfCallable(
|
||||
* path from a source to a sink. The `allowsFieldFlow` flag indicates whether
|
||||
* the branching is within the limit specified by the configuration.
|
||||
*/
|
||||
private predicate flowIntoCallable(
|
||||
private predicate flowIntoCallableCand1(
|
||||
Node node1, Node node2, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallable(node1, node2, config) and
|
||||
flowIntoCallableCand1(node1, node2, config) and
|
||||
exists(int b, int j |
|
||||
b = branch(node1, config) and
|
||||
j = join(node2, config) and
|
||||
@@ -505,7 +498,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
|
||||
@@ -534,14 +527,14 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
nodeCandFwd2(mid, _, stored, config) and
|
||||
flowIntoCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = true and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
nodeCandFwd2(mid, false, stored, config) and
|
||||
flowOutOfCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = false and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
)
|
||||
@@ -574,7 +567,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)
|
||||
)
|
||||
@@ -603,14 +596,14 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowIntoCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
nodeCand2(mid, false, stored, config) and
|
||||
toReturn = false and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowOutOfCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
nodeCand2(mid, _, stored, config) and
|
||||
toReturn = true and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
@@ -663,10 +656,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -677,14 +670,14 @@ private predicate localFlowEntry(Node node, Configuration config) {
|
||||
private predicate localFlowExit(Node node, Configuration config) {
|
||||
exists(Node next | nodeCand(next, config) |
|
||||
jumpStep(node, next, _, config) or
|
||||
flowIntoCallable(node, next, config) or
|
||||
flowOutOfCallable(node, next, config) or
|
||||
flowThroughMethod(node, next, _, config) or
|
||||
flowIntoCallableCand1(node, next, config) or
|
||||
flowOutOfCallableCand1(node, next, config) or
|
||||
flowThroughCallableCand1(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 +699,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 +718,7 @@ private predicate localFlowBigStep(
|
||||
}
|
||||
|
||||
private newtype TAccessPathFront =
|
||||
TFrontNil(Type t) or
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(Content f)
|
||||
|
||||
/**
|
||||
@@ -733,12 +726,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 +748,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
|
||||
}
|
||||
}
|
||||
@@ -805,21 +798,21 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowCandFwd(mid, _, apf, config) and
|
||||
flowIntoCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = true and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowCandFwd(mid, false, apf, config) and
|
||||
flowOutOfCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = false and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallableCand1(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
or
|
||||
@@ -901,21 +894,21 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowIntoCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flowCand(mid, false, apf, config) and
|
||||
toReturn = false and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowOutOfCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flowCand(mid, _, apf, config) and
|
||||
toReturn = true and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallableCand1(node, mid, preservesValue, config) and
|
||||
flowCand(mid, toReturn, apf0, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
@@ -954,7 +947,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 +968,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 +978,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 {
|
||||
@@ -1069,21 +1064,21 @@ private predicate flowFwd0(
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowFwd(mid, _, apf, ap, config) and
|
||||
flowIntoCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = true and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowFwd(mid, false, apf, ap, config) and
|
||||
flowOutOfCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = false and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0, AccessPath ap0 |
|
||||
flowFwd(mid, fromArg, apf0, ap0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallableCand1(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0 and apf = apf0
|
||||
or
|
||||
@@ -1182,21 +1177,21 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowIntoCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flow(mid, false, ap, config) and
|
||||
toReturn = false and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowOutOfCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flow(mid, _, ap, config) and
|
||||
toReturn = true and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPath ap0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallableCand1(node, mid, preservesValue, config) and
|
||||
flow(mid, toReturn, ap0, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0
|
||||
@@ -1274,8 +1269,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 +1285,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)
|
||||
|
||||
@@ -1337,7 +1349,7 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNode getSucc() {
|
||||
override PathNode getASuccessor() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -1391,7 +1403,7 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getSucc() { none() }
|
||||
override PathNode getASuccessor() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1425,11 +1437,9 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
flowIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
or
|
||||
flowOutOfMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
flowOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowThroughMethod(mid, node.asExpr(), cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
or
|
||||
valueFlowThroughMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
flowThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
@@ -1450,36 +1460,35 @@ private predicate contentStoreStep(
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
* Holds if data may flow from `mid` to a return at position `pos` in the
|
||||
* context `innercc`, and the path did not flow through a parameter.
|
||||
*/
|
||||
private predicate 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 flowOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
|
||||
pos.getAReturnNode() = mid.getNode() and
|
||||
innercc = mid.getCallContext() and
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
private predicate flowOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, DataFlowCall call, CallContext innercc |
|
||||
flowOutOfCallable0(mid, pos, innercc) and
|
||||
out = getAnOutputAtCall(call, pos) and
|
||||
c = pos.getCallable() and
|
||||
resolveReturn(innercc, c, call)
|
||||
|
|
||||
if reducedViableImplInReturn(m, ma) then cc = TReturn(m, ma) else cc = TAnyCallContext()
|
||||
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate flowOutOfArgument(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 +1509,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 flowIntoArg(
|
||||
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 +1525,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)
|
||||
@@ -1523,7 +1534,8 @@ 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
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
boolean emptyAp
|
||||
) {
|
||||
flowIntoArg(mid, i, outercc, call, emptyAp) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
@@ -1536,9 +1548,9 @@ private predicate flowIntoCallable0(
|
||||
* respectively.
|
||||
*/
|
||||
private predicate flowIntoCallable(
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, Call call
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, DataFlowCall call
|
||||
) {
|
||||
exists(int i, Callable callable, boolean emptyAp |
|
||||
exists(int i, DataFlowCallable callable, boolean emptyAp |
|
||||
flowIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
@@ -1548,42 +1560,32 @@ 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 at position `pos`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configuration config) {
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
mid.getNode() = ret and
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnPosition pos, CallContextCall cc, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid |
|
||||
mid.getNode() = pos.getAReturnNode() and
|
||||
cc = mid.getCallContext() and
|
||||
config = mid.getConfiguration() and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
config = mid.getConfiguration()
|
||||
|
|
||||
cc = TSomeCall(p, true)
|
||||
or
|
||||
exists(int i | cc = TSpecificCall(_, i, true) |
|
||||
p.isParameterOf(returnNodeGetEnclosingCallable(ret), i)
|
||||
)
|
||||
exists(int i | cc = TSpecificCall(_, i, true) | p.isParameterOf(pos.getCallable(), i))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
* 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 flowThroughMethod(PathNodeMid mid, Call methodcall, 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
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) {
|
||||
exists(ParameterNode p |
|
||||
flowIntoCallable(mid, p, cc, _, methodcall) and
|
||||
parameterValueFlowsThrough(p)
|
||||
private predicate flowThroughCallable(PathNodeMid mid, Node out, CallContext cc) {
|
||||
exists(DataFlowCall call, ParameterNode p, ReturnPosition pos, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, pos, innercc, unbind(mid.getConfiguration())) and
|
||||
out = getAnOutputAtCall(call, pos)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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,11 +148,6 @@ 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.
|
||||
*/
|
||||
@@ -209,11 +204,9 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(Method m, MethodAccess ma, ReturnNode ret |
|
||||
exists(ReturnNode ret |
|
||||
nodeCandFwd1(ret, stored, config) and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
m = viableImpl(ma) and
|
||||
node.asExpr() = ma
|
||||
node = getAnOutputAtCall(_, ret.getPosition())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -287,10 +280,9 @@ private predicate nodeCand1(Node node, boolean stored, Configuration 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(Node out |
|
||||
nodeCand1(out, stored, config) and
|
||||
out = getAnOutputAtCall(_, node.(ReturnNode).getPosition())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -316,11 +308,13 @@ 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)
|
||||
not parameterValueFlowsThrough(p, _)
|
||||
or
|
||||
nodeCand1(node, false, unbind(config)) and
|
||||
exists(Node mid |
|
||||
@@ -367,21 +361,21 @@ private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Con
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, ExprNode call, RefType t, Configuration config
|
||||
ArgumentNode arg, Node out, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode param, ReturnNode ret |
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
nodeCand1(call, false, unbind(config)) and
|
||||
nodeCand1(out, false, unbind(config)) and
|
||||
viableParamArg(param, arg) and
|
||||
simpleParameterFlow(param, ret, t, config) and
|
||||
arg.argumentOf(call.getExpr(), _)
|
||||
out = getAnOutputAtCall(arg.getCall(), ret.getPosition())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a method.
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a callable.
|
||||
*/
|
||||
private predicate flowThroughMethod(
|
||||
private predicate flowThroughCallableCand1(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
) {
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
|
||||
@@ -391,13 +385,13 @@ private predicate flowThroughMethod(
|
||||
|
||||
/**
|
||||
* 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)
|
||||
flowThroughCallableCand1(node1, node2, preservesValue, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -405,7 +399,7 @@ private predicate localFlowStepOrFlowThroughMethod(
|
||||
* through a `ReturnNode` or through an argument that has been mutated, and
|
||||
* that this step is part of a path from a source to a sink.
|
||||
*/
|
||||
private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) {
|
||||
private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration config) {
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config) and
|
||||
(
|
||||
@@ -415,13 +409,8 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
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
|
||||
node2 = getAnOutputAtCall(_, node1.(ReturnNode).getPosition())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -429,7 +418,7 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
* Holds if data can flow into a callable and that this step is part of a
|
||||
* path from a source to a sink.
|
||||
*/
|
||||
private predicate flowIntoCallable(Node node1, Node node2, Configuration config) {
|
||||
private predicate flowIntoCallableCand1(Node node1, Node node2, Configuration config) {
|
||||
viableParamArg(node2, node1) and
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config)
|
||||
@@ -441,7 +430,9 @@ private predicate flowIntoCallable(Node node1, Node node2, Configuration config)
|
||||
* contexts.
|
||||
*/
|
||||
private int branch(Node n1, Configuration conf) {
|
||||
result = strictcount(Node n | flowOutOfCallable(n1, n, conf) or flowIntoCallable(n1, n, conf))
|
||||
result = strictcount(Node n |
|
||||
flowOutOfCallableCand1(n1, n, conf) or flowIntoCallableCand1(n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -450,7 +441,9 @@ private int branch(Node n1, Configuration conf) {
|
||||
* contexts.
|
||||
*/
|
||||
private int join(Node n2, Configuration conf) {
|
||||
result = strictcount(Node n | flowOutOfCallable(n, n2, conf) or flowIntoCallable(n, n2, conf))
|
||||
result = strictcount(Node n |
|
||||
flowOutOfCallableCand1(n, n2, conf) or flowIntoCallableCand1(n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -460,10 +453,10 @@ private int join(Node n2, Configuration conf) {
|
||||
* `allowsFieldFlow` flag indicates whether the branching is within the limit
|
||||
* specified by the configuration.
|
||||
*/
|
||||
private predicate flowOutOfCallable(
|
||||
private predicate flowOutOfCallableCand1(
|
||||
Node node1, Node node2, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCallable(node1, node2, config) and
|
||||
flowOutOfCallableCand1(node1, node2, config) and
|
||||
exists(int b, int j |
|
||||
b = branch(node1, config) and
|
||||
j = join(node2, config) and
|
||||
@@ -478,10 +471,10 @@ private predicate flowOutOfCallable(
|
||||
* path from a source to a sink. The `allowsFieldFlow` flag indicates whether
|
||||
* the branching is within the limit specified by the configuration.
|
||||
*/
|
||||
private predicate flowIntoCallable(
|
||||
private predicate flowIntoCallableCand1(
|
||||
Node node1, Node node2, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallable(node1, node2, config) and
|
||||
flowIntoCallableCand1(node1, node2, config) and
|
||||
exists(int b, int j |
|
||||
b = branch(node1, config) and
|
||||
j = join(node2, config) and
|
||||
@@ -505,7 +498,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
|
||||
@@ -534,14 +527,14 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
nodeCandFwd2(mid, _, stored, config) and
|
||||
flowIntoCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = true and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
nodeCandFwd2(mid, false, stored, config) and
|
||||
flowOutOfCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = false and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
)
|
||||
@@ -574,7 +567,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)
|
||||
)
|
||||
@@ -603,14 +596,14 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowIntoCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
nodeCand2(mid, false, stored, config) and
|
||||
toReturn = false and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowOutOfCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
nodeCand2(mid, _, stored, config) and
|
||||
toReturn = true and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
@@ -663,10 +656,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -677,14 +670,14 @@ private predicate localFlowEntry(Node node, Configuration config) {
|
||||
private predicate localFlowExit(Node node, Configuration config) {
|
||||
exists(Node next | nodeCand(next, config) |
|
||||
jumpStep(node, next, _, config) or
|
||||
flowIntoCallable(node, next, config) or
|
||||
flowOutOfCallable(node, next, config) or
|
||||
flowThroughMethod(node, next, _, config) or
|
||||
flowIntoCallableCand1(node, next, config) or
|
||||
flowOutOfCallableCand1(node, next, config) or
|
||||
flowThroughCallableCand1(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 +699,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 +718,7 @@ private predicate localFlowBigStep(
|
||||
}
|
||||
|
||||
private newtype TAccessPathFront =
|
||||
TFrontNil(Type t) or
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(Content f)
|
||||
|
||||
/**
|
||||
@@ -733,12 +726,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 +748,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
|
||||
}
|
||||
}
|
||||
@@ -805,21 +798,21 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowCandFwd(mid, _, apf, config) and
|
||||
flowIntoCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = true and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowCandFwd(mid, false, apf, config) and
|
||||
flowOutOfCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = false and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallableCand1(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
or
|
||||
@@ -901,21 +894,21 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowIntoCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flowCand(mid, false, apf, config) and
|
||||
toReturn = false and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowOutOfCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flowCand(mid, _, apf, config) and
|
||||
toReturn = true and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallableCand1(node, mid, preservesValue, config) and
|
||||
flowCand(mid, toReturn, apf0, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
@@ -954,7 +947,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 +968,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 +978,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 {
|
||||
@@ -1069,21 +1064,21 @@ private predicate flowFwd0(
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowFwd(mid, _, apf, ap, config) and
|
||||
flowIntoCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = true and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowFwd(mid, false, apf, ap, config) and
|
||||
flowOutOfCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = false and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0, AccessPath ap0 |
|
||||
flowFwd(mid, fromArg, apf0, ap0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallableCand1(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0 and apf = apf0
|
||||
or
|
||||
@@ -1182,21 +1177,21 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowIntoCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flow(mid, false, ap, config) and
|
||||
toReturn = false and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowOutOfCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flow(mid, _, ap, config) and
|
||||
toReturn = true and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPath ap0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallableCand1(node, mid, preservesValue, config) and
|
||||
flow(mid, toReturn, ap0, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0
|
||||
@@ -1274,8 +1269,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 +1285,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)
|
||||
|
||||
@@ -1337,7 +1349,7 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNode getSucc() {
|
||||
override PathNode getASuccessor() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -1391,7 +1403,7 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getSucc() { none() }
|
||||
override PathNode getASuccessor() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1425,11 +1437,9 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
flowIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
or
|
||||
flowOutOfMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
flowOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowThroughMethod(mid, node.asExpr(), cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
or
|
||||
valueFlowThroughMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
flowThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
@@ -1450,36 +1460,35 @@ private predicate contentStoreStep(
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
* Holds if data may flow from `mid` to a return at position `pos` in the
|
||||
* context `innercc`, and the path did not flow through a parameter.
|
||||
*/
|
||||
private predicate 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 flowOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
|
||||
pos.getAReturnNode() = mid.getNode() and
|
||||
innercc = mid.getCallContext() and
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
private predicate flowOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, DataFlowCall call, CallContext innercc |
|
||||
flowOutOfCallable0(mid, pos, innercc) and
|
||||
out = getAnOutputAtCall(call, pos) and
|
||||
c = pos.getCallable() and
|
||||
resolveReturn(innercc, c, call)
|
||||
|
|
||||
if reducedViableImplInReturn(m, ma) then cc = TReturn(m, ma) else cc = TAnyCallContext()
|
||||
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate flowOutOfArgument(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 +1509,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 flowIntoArg(
|
||||
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 +1525,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)
|
||||
@@ -1523,7 +1534,8 @@ 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
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
boolean emptyAp
|
||||
) {
|
||||
flowIntoArg(mid, i, outercc, call, emptyAp) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
@@ -1536,9 +1548,9 @@ private predicate flowIntoCallable0(
|
||||
* respectively.
|
||||
*/
|
||||
private predicate flowIntoCallable(
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, Call call
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, DataFlowCall call
|
||||
) {
|
||||
exists(int i, Callable callable, boolean emptyAp |
|
||||
exists(int i, DataFlowCallable callable, boolean emptyAp |
|
||||
flowIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
@@ -1548,42 +1560,32 @@ 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 at position `pos`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configuration config) {
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
mid.getNode() = ret and
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnPosition pos, CallContextCall cc, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid |
|
||||
mid.getNode() = pos.getAReturnNode() and
|
||||
cc = mid.getCallContext() and
|
||||
config = mid.getConfiguration() and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
config = mid.getConfiguration()
|
||||
|
|
||||
cc = TSomeCall(p, true)
|
||||
or
|
||||
exists(int i | cc = TSpecificCall(_, i, true) |
|
||||
p.isParameterOf(returnNodeGetEnclosingCallable(ret), i)
|
||||
)
|
||||
exists(int i | cc = TSpecificCall(_, i, true) | p.isParameterOf(pos.getCallable(), i))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
* 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 flowThroughMethod(PathNodeMid mid, Call methodcall, 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
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) {
|
||||
exists(ParameterNode p |
|
||||
flowIntoCallable(mid, p, cc, _, methodcall) and
|
||||
parameterValueFlowsThrough(p)
|
||||
private predicate flowThroughCallable(PathNodeMid mid, Node out, CallContext cc) {
|
||||
exists(DataFlowCall call, ParameterNode p, ReturnPosition pos, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, pos, innercc, unbind(mid.getConfiguration())) and
|
||||
out = getAnOutputAtCall(call, pos)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowDispatch
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
cached
|
||||
private module ImplCommon {
|
||||
@@ -9,11 +8,8 @@ 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)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -21,7 +17,7 @@ private module ImplCommon {
|
||||
*/
|
||||
cached
|
||||
predicate viableParamArg(ParameterNode p, ArgumentNode arg) {
|
||||
exists(int i, Call call |
|
||||
exists(int i, DataFlowCall call |
|
||||
viableParam(call, i, p) and
|
||||
arg.argumentOf(call, i)
|
||||
)
|
||||
@@ -49,24 +45,24 @@ private module ImplCommon {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 with position `pos` in the same
|
||||
* callable using only value-preserving steps.
|
||||
*/
|
||||
cached
|
||||
predicate parameterValueFlowsThrough(ParameterNode p) {
|
||||
exists(ReturnNode ret | parameterValueFlow(p, ret))
|
||||
predicate parameterValueFlowsThrough(ParameterNode p, ReturnPosition pos) {
|
||||
parameterValueFlow(p, pos.getAReturnNode())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` flows through `call` using only value-preserving steps.
|
||||
*/
|
||||
cached
|
||||
predicate argumentValueFlowsThrough(ArgumentNode arg, ExprNode call) {
|
||||
exists(ParameterNode param |
|
||||
predicate argumentValueFlowsThrough(ArgumentNode arg, Node out) {
|
||||
exists(ParameterNode param, ReturnPosition ret |
|
||||
viableParamArg(param, arg) and
|
||||
parameterValueFlowsThrough(param) and
|
||||
arg.argumentOf(call.getExpr(), _) and
|
||||
compatibleTypes(arg.getType(), call.getType())
|
||||
parameterValueFlowsThrough(param, ret) and
|
||||
out = getAnOutputAtCall(arg.getCall(), ret) and
|
||||
compatibleTypes(arg.getType(), out.getType())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -113,7 +109,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
|
||||
@@ -133,8 +129,8 @@ private module ImplCommon {
|
||||
}
|
||||
|
||||
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
|
||||
@@ -147,7 +143,7 @@ private module ImplCommon {
|
||||
arg = node1 and
|
||||
viableParamArg(p, arg) and
|
||||
setterReturn(p, f) and
|
||||
arg.argumentOf(node2.asExpr(), _) and
|
||||
node2 = arg.getCall().getNode() and
|
||||
compatibleTypes(node1.getTypeBound(), f.getType()) and
|
||||
compatibleTypes(node2.getTypeBound(), f.getContainerType())
|
||||
)
|
||||
@@ -173,7 +169,7 @@ private module ImplCommon {
|
||||
arg = node1 and
|
||||
viableParamArg(p, arg) and
|
||||
getter(p, f) and
|
||||
arg.argumentOf(node2.asExpr(), _) and
|
||||
node2 = arg.getCall().getNode() and
|
||||
compatibleTypes(node1.getTypeBound(), f.getContainerType()) and
|
||||
compatibleTypes(node2.getTypeBound(), f.getType())
|
||||
)
|
||||
@@ -200,14 +196,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,7 +213,7 @@ 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) }
|
||||
}
|
||||
import ImplCommon
|
||||
|
||||
@@ -227,14 +223,14 @@ import ImplCommon
|
||||
*
|
||||
* 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 +244,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 +256,25 @@ 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 + ")")
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
@@ -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,11 +148,6 @@ 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.
|
||||
*/
|
||||
@@ -209,11 +204,9 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(Method m, MethodAccess ma, ReturnNode ret |
|
||||
exists(ReturnNode ret |
|
||||
nodeCandFwd1(ret, stored, config) and
|
||||
m = returnNodeGetEnclosingCallable(ret) and
|
||||
m = viableImpl(ma) and
|
||||
node.asExpr() = ma
|
||||
node = getAnOutputAtCall(_, ret.getPosition())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -287,10 +280,9 @@ private predicate nodeCand1(Node node, boolean stored, Configuration 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(Node out |
|
||||
nodeCand1(out, stored, config) and
|
||||
out = getAnOutputAtCall(_, node.(ReturnNode).getPosition())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -316,11 +308,13 @@ 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)
|
||||
not parameterValueFlowsThrough(p, _)
|
||||
or
|
||||
nodeCand1(node, false, unbind(config)) and
|
||||
exists(Node mid |
|
||||
@@ -367,21 +361,21 @@ private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Con
|
||||
* additional step from the configuration.
|
||||
*/
|
||||
private predicate simpleArgumentFlowsThrough(
|
||||
ArgumentNode arg, ExprNode call, RefType t, Configuration config
|
||||
ArgumentNode arg, Node out, DataFlowType t, Configuration config
|
||||
) {
|
||||
exists(ParameterNode param, ReturnNode ret |
|
||||
nodeCand1(arg, false, unbind(config)) and
|
||||
nodeCand1(call, false, unbind(config)) and
|
||||
nodeCand1(out, false, unbind(config)) and
|
||||
viableParamArg(param, arg) and
|
||||
simpleParameterFlow(param, ret, t, config) and
|
||||
arg.argumentOf(call.getExpr(), _)
|
||||
out = getAnOutputAtCall(arg.getCall(), ret.getPosition())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a method.
|
||||
* Holds if data can flow from `node1` to `node2` by a step through a callable.
|
||||
*/
|
||||
private predicate flowThroughMethod(
|
||||
private predicate flowThroughCallableCand1(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
) {
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
|
||||
@@ -391,13 +385,13 @@ private predicate flowThroughMethod(
|
||||
|
||||
/**
|
||||
* 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)
|
||||
flowThroughCallableCand1(node1, node2, preservesValue, config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -405,7 +399,7 @@ private predicate localFlowStepOrFlowThroughMethod(
|
||||
* through a `ReturnNode` or through an argument that has been mutated, and
|
||||
* that this step is part of a path from a source to a sink.
|
||||
*/
|
||||
private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) {
|
||||
private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration config) {
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config) and
|
||||
(
|
||||
@@ -415,13 +409,8 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
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
|
||||
node2 = getAnOutputAtCall(_, node1.(ReturnNode).getPosition())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -429,7 +418,7 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
|
||||
* Holds if data can flow into a callable and that this step is part of a
|
||||
* path from a source to a sink.
|
||||
*/
|
||||
private predicate flowIntoCallable(Node node1, Node node2, Configuration config) {
|
||||
private predicate flowIntoCallableCand1(Node node1, Node node2, Configuration config) {
|
||||
viableParamArg(node2, node1) and
|
||||
nodeCand1(node1, _, unbind(config)) and
|
||||
nodeCand1(node2, _, config)
|
||||
@@ -441,7 +430,9 @@ private predicate flowIntoCallable(Node node1, Node node2, Configuration config)
|
||||
* contexts.
|
||||
*/
|
||||
private int branch(Node n1, Configuration conf) {
|
||||
result = strictcount(Node n | flowOutOfCallable(n1, n, conf) or flowIntoCallable(n1, n, conf))
|
||||
result = strictcount(Node n |
|
||||
flowOutOfCallableCand1(n1, n, conf) or flowIntoCallableCand1(n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -450,7 +441,9 @@ private int branch(Node n1, Configuration conf) {
|
||||
* contexts.
|
||||
*/
|
||||
private int join(Node n2, Configuration conf) {
|
||||
result = strictcount(Node n | flowOutOfCallable(n, n2, conf) or flowIntoCallable(n, n2, conf))
|
||||
result = strictcount(Node n |
|
||||
flowOutOfCallableCand1(n, n2, conf) or flowIntoCallableCand1(n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -460,10 +453,10 @@ private int join(Node n2, Configuration conf) {
|
||||
* `allowsFieldFlow` flag indicates whether the branching is within the limit
|
||||
* specified by the configuration.
|
||||
*/
|
||||
private predicate flowOutOfCallable(
|
||||
private predicate flowOutOfCallableCand1(
|
||||
Node node1, Node node2, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCallable(node1, node2, config) and
|
||||
flowOutOfCallableCand1(node1, node2, config) and
|
||||
exists(int b, int j |
|
||||
b = branch(node1, config) and
|
||||
j = join(node2, config) and
|
||||
@@ -478,10 +471,10 @@ private predicate flowOutOfCallable(
|
||||
* path from a source to a sink. The `allowsFieldFlow` flag indicates whether
|
||||
* the branching is within the limit specified by the configuration.
|
||||
*/
|
||||
private predicate flowIntoCallable(
|
||||
private predicate flowIntoCallableCand1(
|
||||
Node node1, Node node2, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallable(node1, node2, config) and
|
||||
flowIntoCallableCand1(node1, node2, config) and
|
||||
exists(int b, int j |
|
||||
b = branch(node1, config) and
|
||||
j = join(node2, config) and
|
||||
@@ -505,7 +498,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
|
||||
@@ -534,14 +527,14 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
nodeCandFwd2(mid, _, stored, config) and
|
||||
flowIntoCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = true and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
nodeCandFwd2(mid, false, stored, config) and
|
||||
flowOutOfCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = false and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
)
|
||||
@@ -574,7 +567,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)
|
||||
)
|
||||
@@ -603,14 +596,14 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowIntoCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
nodeCand2(mid, false, stored, config) and
|
||||
toReturn = false and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowOutOfCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
nodeCand2(mid, _, stored, config) and
|
||||
toReturn = true and
|
||||
(stored = false or allowsFieldFlow = true)
|
||||
@@ -663,10 +656,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -677,14 +670,14 @@ private predicate localFlowEntry(Node node, Configuration config) {
|
||||
private predicate localFlowExit(Node node, Configuration config) {
|
||||
exists(Node next | nodeCand(next, config) |
|
||||
jumpStep(node, next, _, config) or
|
||||
flowIntoCallable(node, next, config) or
|
||||
flowOutOfCallable(node, next, config) or
|
||||
flowThroughMethod(node, next, _, config) or
|
||||
flowIntoCallableCand1(node, next, config) or
|
||||
flowOutOfCallableCand1(node, next, config) or
|
||||
flowThroughCallableCand1(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 +699,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 +718,7 @@ private predicate localFlowBigStep(
|
||||
}
|
||||
|
||||
private newtype TAccessPathFront =
|
||||
TFrontNil(Type t) or
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(Content f)
|
||||
|
||||
/**
|
||||
@@ -733,12 +726,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 +748,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
|
||||
}
|
||||
}
|
||||
@@ -805,21 +798,21 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowCandFwd(mid, _, apf, config) and
|
||||
flowIntoCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = true and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowCandFwd(mid, false, apf, config) and
|
||||
flowOutOfCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = false and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowCandFwd(mid, fromArg, apf0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallableCand1(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
or
|
||||
@@ -901,21 +894,21 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowIntoCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flowCand(mid, false, apf, config) and
|
||||
toReturn = false and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowOutOfCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flowCand(mid, _, apf, config) and
|
||||
toReturn = true and
|
||||
(apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallableCand1(node, mid, preservesValue, config) and
|
||||
flowCand(mid, toReturn, apf0, config) and
|
||||
(
|
||||
preservesValue = true and apf = apf0
|
||||
@@ -954,7 +947,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 +968,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 +978,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 {
|
||||
@@ -1069,21 +1064,21 @@ private predicate flowFwd0(
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowFwd(mid, _, apf, ap, config) and
|
||||
flowIntoCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = true and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowFwd(mid, false, apf, ap, config) and
|
||||
flowOutOfCallable(mid, node, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(mid, node, allowsFieldFlow, config) and
|
||||
fromArg = false and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPathFront apf0, AccessPath ap0 |
|
||||
flowFwd(mid, fromArg, apf0, ap0, config) and
|
||||
flowThroughMethod(mid, node, preservesValue, config) and
|
||||
flowThroughCallableCand1(mid, node, preservesValue, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0 and apf = apf0
|
||||
or
|
||||
@@ -1182,21 +1177,21 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowIntoCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowIntoCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flow(mid, false, ap, config) and
|
||||
toReturn = false and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean allowsFieldFlow |
|
||||
flowOutOfCallable(node, mid, allowsFieldFlow, config) and
|
||||
flowOutOfCallableCand1(node, mid, allowsFieldFlow, config) and
|
||||
flow(mid, _, ap, config) and
|
||||
toReturn = true and
|
||||
(ap instanceof AccessPathNil or allowsFieldFlow = true)
|
||||
)
|
||||
or
|
||||
exists(Node mid, boolean preservesValue, AccessPath ap0 |
|
||||
flowThroughMethod(node, mid, preservesValue, config) and
|
||||
flowThroughCallableCand1(node, mid, preservesValue, config) and
|
||||
flow(mid, toReturn, ap0, config) and
|
||||
(
|
||||
preservesValue = true and ap = ap0
|
||||
@@ -1274,8 +1269,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 +1285,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)
|
||||
|
||||
@@ -1337,7 +1349,7 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNode getSucc() {
|
||||
override PathNode getASuccessor() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -1391,7 +1403,7 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getSucc() { none() }
|
||||
override PathNode getASuccessor() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1425,11 +1437,9 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
flowIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
or
|
||||
flowOutOfMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
flowOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
flowThroughMethod(mid, node.asExpr(), cc) and ap = TNil(getErasedRepr(node.getType()))
|
||||
or
|
||||
valueFlowThroughMethod(mid, node.asExpr(), cc) and ap = mid.getAp()
|
||||
flowThroughCallable(mid, node, cc) and ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
|
||||
@@ -1450,36 +1460,35 @@ private predicate contentStoreStep(
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
* Holds if data may flow from `mid` to a return at position `pos` in the
|
||||
* context `innercc`, and the path did not flow through a parameter.
|
||||
*/
|
||||
private predicate 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 flowOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
|
||||
pos.getAReturnNode() = mid.getNode() and
|
||||
innercc = mid.getCallContext() and
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
private predicate flowOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, DataFlowCall call, CallContext innercc |
|
||||
flowOutOfCallable0(mid, pos, innercc) and
|
||||
out = getAnOutputAtCall(call, pos) and
|
||||
c = pos.getCallable() and
|
||||
resolveReturn(innercc, c, call)
|
||||
|
|
||||
if reducedViableImplInReturn(m, ma) then cc = TReturn(m, ma) else cc = TAnyCallContext()
|
||||
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate flowOutOfArgument(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 +1509,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 flowIntoArg(
|
||||
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 +1525,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)
|
||||
@@ -1523,7 +1534,8 @@ 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
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
boolean emptyAp
|
||||
) {
|
||||
flowIntoArg(mid, i, outercc, call, emptyAp) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
@@ -1536,9 +1548,9 @@ private predicate flowIntoCallable0(
|
||||
* respectively.
|
||||
*/
|
||||
private predicate flowIntoCallable(
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, Call call
|
||||
PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, DataFlowCall call
|
||||
) {
|
||||
exists(int i, Callable callable, boolean emptyAp |
|
||||
exists(int i, DataFlowCallable callable, boolean emptyAp |
|
||||
flowIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
@@ -1548,42 +1560,32 @@ 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 at position `pos`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configuration config) {
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
mid.getNode() = ret and
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnPosition pos, CallContextCall cc, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid |
|
||||
mid.getNode() = pos.getAReturnNode() and
|
||||
cc = mid.getCallContext() and
|
||||
config = mid.getConfiguration() and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
config = mid.getConfiguration()
|
||||
|
|
||||
cc = TSomeCall(p, true)
|
||||
or
|
||||
exists(int i | cc = TSpecificCall(_, i, true) |
|
||||
p.isParameterOf(returnNodeGetEnclosingCallable(ret), i)
|
||||
)
|
||||
exists(int i | cc = TSpecificCall(_, i, true) | p.isParameterOf(pos.getCallable(), i))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
* 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 flowThroughMethod(PathNodeMid mid, Call methodcall, 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
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) {
|
||||
exists(ParameterNode p |
|
||||
flowIntoCallable(mid, p, cc, _, methodcall) and
|
||||
parameterValueFlowsThrough(p)
|
||||
private predicate flowThroughCallable(PathNodeMid mid, Node out, CallContext cc) {
|
||||
exists(DataFlowCall call, ParameterNode p, ReturnPosition pos, CallContext innercc |
|
||||
flowIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, pos, innercc, unbind(mid.getConfiguration())) and
|
||||
out = getAnOutputAtCall(call, pos)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Provides Java-specific definitions for use in the data flow library.
|
||||
*/
|
||||
module Private {
|
||||
import DataFlowPrivate
|
||||
import DataFlowDispatch
|
||||
}
|
||||
|
||||
module Public {
|
||||
import DataFlowUtil
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import java
|
||||
private import java
|
||||
private import DataFlowUtil
|
||||
private import DataFlowDispatch
|
||||
private import semmle.code.java.dataflow.SSA
|
||||
private import semmle.code.java.dataflow.TypeFlow
|
||||
|
||||
@@ -22,7 +23,7 @@ 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) {
|
||||
predicate argumentOf(DataFlowCall call, int pos) {
|
||||
exists(Argument arg | this.asExpr() = arg | call = arg.getCall() and pos = arg.getPosition())
|
||||
or
|
||||
call = this.(ImplicitVarargsArray).getCall() and
|
||||
@@ -30,11 +31,22 @@ class ArgumentNode extends Node {
|
||||
or
|
||||
pos = -1 and this = getInstanceArgument(call)
|
||||
}
|
||||
|
||||
/** Gets the call in which this node is an argument. */
|
||||
DataFlowCall getCall() { this.argumentOf(result, _) }
|
||||
}
|
||||
|
||||
/** A data flow node that occurs as the result of a `ReturnStmt`. */
|
||||
class ReturnNode extends ExprNode {
|
||||
ReturnNode() { exists(ReturnStmt ret | this.getExpr() = ret.getResult()) }
|
||||
|
||||
/** Gets the position at which this value is returned. */
|
||||
ReturnPosition getPosition() { this = result.getAReturnNode() }
|
||||
}
|
||||
|
||||
/** A data flow node that represents a call. */
|
||||
class OutNode extends ExprNode {
|
||||
OutNode() { this.getExpr() instanceof Call }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -230,3 +242,21 @@ predicate compatibleTypes(Type t1, Type t2) {
|
||||
canContainBool(e1) and canContainBool(e2)
|
||||
)
|
||||
}
|
||||
|
||||
/** A node that performs a type cast. */
|
||||
class CastNode extends ExprNode {
|
||||
CastNode() { this.getExpr() instanceof CastExpr }
|
||||
}
|
||||
|
||||
class DataFlowCallable = Callable;
|
||||
|
||||
class DataFlowExpr = Expr;
|
||||
|
||||
class DataFlowType = RefType;
|
||||
|
||||
class DataFlowLocation = Location;
|
||||
|
||||
class DataFlowCall extends Call {
|
||||
/** Gets the data flow node corresponding to this call. */
|
||||
ExprNode getNode() { result.getExpr() = this }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user