Merge pull request #241 from max-schaefer/update-data-flow

Update shared data-flow libraries
This commit is contained in:
Max Schaefer
2020-07-02 14:07:32 +01:00
committed by GitHub
4 changed files with 108 additions and 116 deletions

View File

@@ -97,28 +97,13 @@ DataFlowCallable viableCallable(CallExpr ma) {
}
/**
* Holds if the call context `ctx` reduces the set of viable dispatch
* targets of `ma` in `c`.
* Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context.
*/
predicate reducedViableImplInCallContext(DataFlowCall ma, DataFlowCallable c, DataFlowCall ctx) {
none()
}
predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable f) { none() }
/**
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
* restricted to those `ma`s for which the context makes a difference.
* Gets a viable dispatch target of `call` in the context `ctx`. This is
* restricted to those `call`s for which a context might make a difference.
*/
DataFlowCallable prunedViableImplInCallContext(DataFlowCall ma, DataFlowCall ctx) { none() }
/**
* Holds if flow returning from `m` to `ma` might return further and if
* this path restricts the set of call sites that can be returned to.
*/
predicate reducedViableImplInReturn(DataFlowCallable m, DataFlowCall ma) { none() }
/**
* Gets a viable dispatch target of `ma` in the context `ctx`. This is
* restricted to those `ma`s and results for which the return flow from the
* result to `ma` restricts the possible context `ctx`.
*/
DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall ma, DataFlowCall ctx) { none() }
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { none() }

View File

@@ -1124,11 +1124,11 @@ private module LocalFlowBigStep {
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
t = getErasedNodeTypeBound(node1)
t = getNodeType(node1)
or
additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
t = getErasedNodeTypeBound(node2)
t = getNodeType(node2)
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
@@ -1147,7 +1147,7 @@ private module LocalFlowBigStep {
additionalLocalFlowStepNodeCand2(mid, node2, config) and
not mid instanceof FlowCheckNode and
preservesValue = false and
t = getErasedNodeTypeBound(node2) and
t = getNodeType(node2) and
nodeCand2(node2, unbind(config))
)
)
@@ -1202,9 +1202,7 @@ private predicate flowCandFwd(
) {
flowCandFwd0(node, fromArg, argApf, apf, config) and
not apf.isClearedAt(node) and
if node instanceof CastingNode
then compatibleTypes(getErasedNodeTypeBound(node), apf.getType())
else any()
if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any()
}
pragma[nomagic]
@@ -1216,7 +1214,7 @@ private predicate flowCandFwd0(
config.isSource(node) and
fromArg = false and
argApf = TAccessPathFrontNone() and
apf = TFrontNil(getErasedNodeTypeBound(node))
apf = TFrontNil(getNodeType(node))
or
exists(Node mid |
flowCandFwd(mid, fromArg, argApf, apf, config) and
@@ -1242,7 +1240,7 @@ private predicate flowCandFwd0(
additionalJumpStep(mid, node, config) and
fromArg = false and
argApf = TAccessPathFrontNone() and
apf = TFrontNil(getErasedNodeTypeBound(node))
apf = TFrontNil(getNodeType(node))
)
or
// store
@@ -1672,7 +1670,7 @@ private predicate flowFwd0(
config.isSource(node) and
fromArg = false and
argAp = TAccessPathNone() and
ap = TNil(getErasedNodeTypeBound(node)) and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
or
flowCand(node, _, _, _, unbind(config)) and
@@ -1700,7 +1698,7 @@ private predicate flowFwd0(
additionalJumpStep(mid, node, config) and
fromArg = false and
argAp = TAccessPathNone() and
ap = TNil(getErasedNodeTypeBound(node)) and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
)
)
@@ -2077,7 +2075,7 @@ private newtype TPathNode =
config.isSource(node) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
ap = TNil(getErasedNodeTypeBound(node))
ap = TNil(getNodeType(node))
or
// ... or a step from an existing PathNode to another node.
exists(PathNodeMid mid |
@@ -2304,7 +2302,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
mid.getAp() instanceof AccessPathNil and
ap = TNil(getErasedNodeTypeBound(node))
ap = TNil(getNodeType(node))
or
exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
sc = mid.getSummaryCtx()
@@ -2646,7 +2644,7 @@ private module FlowExploration {
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
ap = TPartialNil(getErasedNodeTypeBound(node)) and
ap = TPartialNil(getNodeType(node)) and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
@@ -2663,7 +2661,7 @@ private module FlowExploration {
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
if node instanceof CastingNode
then compatibleTypes(getErasedNodeTypeBound(node), ap.getType())
then compatibleTypes(getNodeType(node), ap.getType())
else any()
)
}
@@ -2776,7 +2774,7 @@ private module FlowExploration {
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof PartialAccessPathNil and
ap = TPartialNil(getErasedNodeTypeBound(node)) and
ap = TPartialNil(getNodeType(node)) and
config = mid.getConfiguration()
)
or
@@ -2792,7 +2790,7 @@ private module FlowExploration {
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
mid.getAp() instanceof PartialAccessPathNil and
ap = TPartialNil(getErasedNodeTypeBound(node)) and
ap = TPartialNil(getNodeType(node)) and
config = mid.getConfiguration()
or
partialPathStoreStep(mid, _, _, node, ap) and
@@ -2806,7 +2804,7 @@ private module FlowExploration {
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
apConsFwd(ap, tc, ap0, config) and
compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
compatibleTypes(ap.getType(), getNodeType(node))
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)

View File

@@ -22,7 +22,7 @@ private module Cached {
exists(int i |
viableParam(call, i, p) and
arg.argumentOf(call, i) and
compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(p))
compatibleTypes(getNodeType(arg), getNodeType(p))
)
}
@@ -147,54 +147,6 @@ private module Cached {
}
}
private module LocalFlowBigStep {
private predicate localFlowEntry(Node n) {
Cand::cand(_, n) and
(
n instanceof ParameterNode or
n instanceof OutNode or
readStep(_, _, n) or
n instanceof CastNode
)
}
private predicate localFlowExit(Node n) {
Cand::cand(_, n) and
(
n instanceof ArgumentNode
or
n instanceof ReturnNode
or
readStep(n, _, _)
or
n instanceof CastNode
or
n =
any(PostUpdateNode pun | Cand::parameterValueFlowsToPreUpdateCand(_, pun))
.getPreUpdateNode()
)
}
pragma[nomagic]
private predicate localFlowStepPlus(Node node1, Node node2) {
localFlowEntry(node1) and
simpleLocalFlowStep(node1, node2) and
node1 != node2
or
exists(Node mid |
localFlowStepPlus(node1, mid) and
simpleLocalFlowStep(mid, node2) and
not mid instanceof CastNode
)
}
pragma[nomagic]
predicate localFlowBigStep(Node node1, Node node2) {
localFlowStepPlus(node1, node2) and
localFlowExit(node2)
}
}
/**
* The final flow-through calculation:
*
@@ -218,10 +170,10 @@ private module Cached {
then
// normal flow through
read = TReadStepTypesNone() and
compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node))
compatibleTypes(getNodeType(p), getNodeType(node))
or
// getter
compatibleTypes(read.getContentType(), getErasedNodeTypeBound(node))
compatibleTypes(read.getContentType(), getNodeType(node))
else any()
}
@@ -234,7 +186,7 @@ private module Cached {
// local flow
exists(Node mid |
parameterValueFlow(p, mid, read) and
LocalFlowBigStep::localFlowBigStep(mid, node)
simpleLocalFlowStep(mid, node)
)
or
// read
@@ -243,19 +195,26 @@ private module Cached {
readStepWithTypes(mid, read.getContainerType(), read.getContent(), node,
read.getContentType()) and
Cand::parameterValueFlowReturnCand(p, _, true) and
compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType())
compatibleTypes(getNodeType(p), read.getContainerType())
)
or
parameterValueFlow0_0(TReadStepTypesNone(), p, node, read)
}
pragma[nomagic]
private predicate parameterValueFlow0_0(
ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read
) {
// flow through: no prior read
exists(ArgumentNode arg |
parameterValueFlowArg(p, arg, TReadStepTypesNone()) and
parameterValueFlowArg(p, arg, mustBeNone) and
argumentValueFlowsThrough(arg, read, node)
)
or
// flow through: no read inside method
exists(ArgumentNode arg |
parameterValueFlowArg(p, arg, read) and
argumentValueFlowsThrough(arg, TReadStepTypesNone(), node)
argumentValueFlowsThrough(arg, mustBeNone, node)
)
}
@@ -292,11 +251,11 @@ private module Cached {
|
// normal flow through
read = TReadStepTypesNone() and
compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out))
compatibleTypes(getNodeType(arg), getNodeType(out))
or
// getter
compatibleTypes(getErasedNodeTypeBound(arg), read.getContainerType()) and
compatibleTypes(read.getContentType(), getErasedNodeTypeBound(out))
compatibleTypes(getNodeType(arg), read.getContainerType()) and
compatibleTypes(read.getContentType(), getNodeType(out))
)
}
@@ -330,6 +289,67 @@ private module Cached {
import Final
}
import FlowThrough
cached
private module DispatchWithCallContext {
/**
* Holds if the call context `ctx` reduces the set of viable run-time
* dispatch targets of call `call` in `c`.
*/
cached
predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) {
exists(int tgts, int ctxtgts |
mayBenefitFromCallContext(call, c) and
c = viableCallable(ctx) and
ctxtgts = count(viableImplInCallContext(call, ctx)) and
tgts = strictcount(viableCallable(call)) and
ctxtgts < tgts
)
}
/**
* Gets a viable run-time dispatch target for the call `call` in the
* context `ctx`. This is restricted to those calls for which a context
* makes a difference.
*/
cached
DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
result = viableImplInCallContext(call, ctx) and
reducedViableImplInCallContext(call, _, ctx)
}
/**
* Holds if flow returning from callable `c` to call `call` might return
* further and if this path restricts the set of call sites that can be
* returned to.
*/
cached
predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) {
exists(int tgts, int ctxtgts |
mayBenefitFromCallContext(call, _) and
c = viableCallable(call) and
ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and
tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and
ctxtgts < tgts
)
}
/**
* Gets a viable run-time dispatch target for the call `call` in the
* context `ctx`. This is restricted to those calls and results for which
* the return flow from the result to `call` restricts the possible context
* `ctx`.
*/
cached
DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) {
result = viableImplInCallContext(call, ctx) and
reducedViableImplInReturn(result, call)
}
}
import DispatchWithCallContext
/**
* Holds if `p` can flow to the pre-update node associated with post-update
* node `n`, in the same callable, using only value-preserving steps.
@@ -344,8 +364,8 @@ private module Cached {
) {
storeStep(node1, c, node2) and
readStep(_, c, _) and
contentType = getErasedNodeTypeBound(node1) and
containerType = getErasedNodeTypeBound(node2)
contentType = getNodeType(node1) and
containerType = getNodeType(node2)
or
exists(Node n1, Node n2 |
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
@@ -354,8 +374,8 @@ private module Cached {
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1)
or
readStep(n2, c, n1) and
contentType = getErasedNodeTypeBound(n1) and
containerType = getErasedNodeTypeBound(n2)
contentType = getNodeType(n1) and
containerType = getNodeType(n2)
)
}
@@ -371,8 +391,6 @@ private module Cached {
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
}
import FlowThrough
/**
* Holds if the call context `call` either improves virtual dispatch in
* `callable` or if it allows us to prune unreachable nodes in `callable`.
@@ -448,8 +466,8 @@ private predicate readStepWithTypes(
Node n1, DataFlowType container, Content c, Node n2, DataFlowType content
) {
readStep(n1, c, n2) and
container = getErasedNodeTypeBound(n1) and
content = getErasedNodeTypeBound(n2)
container = getNodeType(n1) and
content = getNodeType(n2)
}
private newtype TReadStepTypesOption =
@@ -712,9 +730,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
result = viableCallable(call) and cc instanceof CallContextReturn
}
pragma[noinline]
DataFlowType getErasedNodeTypeBound(Node n) { result = getErasedRepr(n.getTypeBound()) }
predicate read = readStep/3;
/** An optional Boolean value. */

View File

@@ -148,16 +148,10 @@ predicate clearsContent(Node n, Content c) {
none() // stub implementation
}
/**
* Gets a representative (boxed) type for `t` for the purpose of pruning
* possible flow. A single type is used for all numeric types to account for
* numeric conversions, and otherwise the erasure is used.
*/
DataFlowType getErasedRepr(Type t) {
result = t // stub implementation
}
/** Gets the type of `n` used for type pruning. */
DataFlowType getNodeType(Node n) { result = n.getType() }
/** Gets a string representation of a type returned by `getErasedRepr`. */
/** Gets a string representation of a type returned by `getNodeType()`. */
string ppReprType(Type t) { result = t.toString() }
/**