Dataflow: Add missing qldoc and sync.

This commit is contained in:
Anders Schack-Mulligen
2020-11-03 09:21:48 +01:00
parent 7eb64aa998
commit 2971784f9c
23 changed files with 748 additions and 70 deletions

View File

@@ -3231,8 +3231,10 @@ private module FlowExploration {
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
}
/** Holds if this is a source in a forward-flow path. */
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
/** Holds if this is a sink in a reverse-flow path. */
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
}

View File

@@ -3231,8 +3231,10 @@ private module FlowExploration {
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
}
/** Holds if this is a source in a forward-flow path. */
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
/** Holds if this is a sink in a reverse-flow path. */
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
}

View File

@@ -112,8 +112,8 @@ abstract class Configuration extends string {
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
/**
* Gets the exploration limit for `hasPartialFlow` measured in approximate
* number of interprocedural steps.
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
* measured in approximate number of interprocedural steps.
*/
int explorationLimit() { none() }
@@ -123,7 +123,7 @@ abstract class Configuration extends string {
* is restricted to be less than or equal to `explorationLimit()`. This
* predicate completely disregards sink definitions.
*
* This predicate is intended for dataflow exploration and debugging and may
* This predicate is intended for data-flow exploration and debugging and may
* perform poorly if the number of sources is too big and/or the exploration
* limit is set too high without using barriers.
*
@@ -136,6 +136,29 @@ abstract class Configuration extends string {
partialFlow(source, node, this) and
dist = node.getSourceDistance()
}
/**
* Holds if there is a partial data flow path from `node` to `sink`. The
* approximate distance between `node` and the closest sink is `dist` and
* is restricted to be less than or equal to `explorationLimit()`. This
* predicate completely disregards source definitions.
*
* This predicate is intended for data-flow exploration and debugging and may
* perform poorly if the number of sinks is too big and/or the exploration
* limit is set too high without using barriers.
*
* This predicate is disabled (has no results) by default. Override
* `explorationLimit()` with a suitable number to enable this predicate.
*
* To use this in a `path-problem` query, import the module `PartialPathGraph`.
*
* Note that reverse flow has slightly lower precision than the corresponding
* forward flow, as reverse flow disregards type pruning among other features.
*/
final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) {
revPartialFlow(node, sink, this) and
dist = node.getSinkDistance()
}
}
/**
@@ -2943,12 +2966,26 @@ private module FlowExploration {
)
}
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
or
exists(DataFlowCallable mid |
interestingCallableSink(mid, config) and callableStep(c, mid, config)
)
}
private newtype TCallableExt =
TCallable(DataFlowCallable c, Configuration config) { interestingCallableSrc(c, config) } or
TCallableSrc()
TCallable(DataFlowCallable c, Configuration config) {
interestingCallableSrc(c, config) or
interestingCallableSink(c, config)
} or
TCallableSrc() or
TCallableSink()
private predicate callableExtSrc(TCallableSrc src) { any() }
private predicate callableExtSink(TCallableSink sink) { any() }
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
callableStep(c1, c2, config) and
@@ -2961,15 +2998,32 @@ private module FlowExploration {
config.isSource(n) and
ce2 = TCallable(n.getEnclosingCallable(), config)
)
or
exists(Node n, Configuration config |
ce2 = TCallableSink() and
config.isSink(n) and
ce1 = TCallable(n.getEnclosingCallable(), config)
)
}
private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) {
callableExtStepFwd(ce2, ce1)
}
private int distSrcExt(TCallableExt c) =
shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result)
private int distSinkExt(TCallableExt c) =
shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result)
private int distSrc(DataFlowCallable c, Configuration config) {
result = distSrcExt(TCallable(c, config)) - 1
}
private int distSink(DataFlowCallable c, Configuration config) {
result = distSinkExt(TCallable(c, config)) - 1
}
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
@@ -2997,18 +3051,12 @@ private module FlowExploration {
or
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
}
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
override string toString() {
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
exists(DataFlowType t | this = TPartialNil(t) | result = TFrontNil(t))
}
}
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
@@ -3019,9 +3067,39 @@ private module FlowExploration {
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
}
override AccessPathFront getFront() {
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
private newtype TRevPartialAccessPath =
TRevPartialNil() or
TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] }
/**
* Conceptually a list of `Content`s, but only the first
* element of the list and its length are tracked.
*/
private class RevPartialAccessPath extends TRevPartialAccessPath {
abstract string toString();
Content getHead() { this = TRevPartialCons(result, _) }
int len() {
this = TRevPartialNil() and result = 0
or
this = TRevPartialCons(_, result)
}
}
private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil {
override string toString() { result = "" }
}
private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons {
override string toString() {
exists(Content c, int len | this = TRevPartialCons(c, len) |
if len = 1
then result = "[" + c.toString() + "]"
else result = "[" + c.toString() + ", ... (" + len.toString() + ")]"
)
}
}
@@ -3033,8 +3111,16 @@ private module FlowExploration {
TSummaryCtx2None() or
TSummaryCtx2Some(PartialAccessPath ap)
private newtype TRevSummaryCtx1 =
TRevSummaryCtx1None() or
TRevSummaryCtx1Some(ReturnPosition pos)
private newtype TRevSummaryCtx2 =
TRevSummaryCtx2None() or
TRevSummaryCtx2Some(RevPartialAccessPath ap)
private newtype TPartialPathNode =
TPartialPathNodeMk(
TPartialPathNodeFwd(
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
@@ -3048,6 +3134,23 @@ private module FlowExploration {
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
Configuration config
) {
config.isSink(node) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil() and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
}
pragma[nomagic]
@@ -3055,7 +3158,7 @@ private module FlowExploration {
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
exists(PartialPathNode mid |
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
if node instanceof CastingNode
@@ -3107,15 +3210,32 @@ private module FlowExploration {
result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
}
/**
* Gets the approximate distance to the nearest sink measured in number
* of interprocedural steps.
*/
int getSinkDistance() {
result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
}
private string ppAp() {
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
exists(string s |
s = this.(PartialPathNodeFwd).getAp().toString() or
s = this.(PartialPathNodeRev).getAp().toString()
|
if s = "" then result = "" else result = " " + s
)
}
private string ppCtx() {
result = " <" + this.(PartialPathNodePriv).getCallContext().toString() + ">"
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
}
/** Holds if this is a source in a forward-flow path. */
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
/** Holds if this is a sink in a reverse-flow path. */
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
}
/**
@@ -3126,7 +3246,7 @@ private module FlowExploration {
query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b }
}
private class PartialPathNodePriv extends PartialPathNode {
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
Node node;
CallContext cc;
TSummaryCtx1 sc1;
@@ -3134,7 +3254,7 @@ private module FlowExploration {
PartialAccessPath ap;
Configuration config;
PartialPathNodePriv() { this = TPartialPathNodeMk(node, cc, sc1, sc2, ap, config) }
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
override Node getNode() { result = node }
@@ -3148,14 +3268,54 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodePriv getASuccessor() {
override PartialPathNodeFwd getASuccessor() {
partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
}
predicate isSource() {
config.isSource(node) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
ap instanceof TPartialNil
}
}
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
Node node;
TRevSummaryCtx1 sc1;
TRevSummaryCtx2 sc2;
RevPartialAccessPath ap;
Configuration config;
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
override Node getNode() { result = node }
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
TRevSummaryCtx2 getSummaryCtx2() { result = sc2 }
RevPartialAccessPath getAp() { result = ap }
override Configuration getConfiguration() { result = config }
override PartialPathNodeRev getASuccessor() {
revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
this.getAp(), this.getConfiguration())
}
predicate isSink() {
config.isSink(node) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil()
}
}
private predicate partialPathStep(
PartialPathNodePriv mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
@@ -3221,8 +3381,7 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
PartialAccessPath ap2
PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
) {
exists(Node midNode, DataFlowType contentType |
midNode = mid.getNode() and
@@ -3238,7 +3397,7 @@ private module FlowExploration {
private predicate apConsFwd(
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
exists(PartialPathNodeFwd mid |
partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
@@ -3246,7 +3405,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathReadStep(
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
exists(Node midNode |
@@ -3260,7 +3419,7 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable0(
PartialPathNodePriv mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
Configuration config
) {
pos = getReturnPosition(mid.getNode()) and
@@ -3272,7 +3431,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathOutOfCallable1(
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
@@ -3286,7 +3445,7 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
@@ -3297,7 +3456,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathIntoArg(
PartialPathNodePriv mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
exists(ArgumentNode arg |
@@ -3311,7 +3470,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathIntoCallable0(
PartialPathNodePriv mid, DataFlowCallable callable, int i, CallContext outercc,
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
DataFlowCall call, PartialAccessPath ap, Configuration config
) {
partialPathIntoArg(mid, i, outercc, call, ap, config) and
@@ -3319,7 +3478,7 @@ private module FlowExploration {
}
private predicate partialPathIntoCallable(
PartialPathNodePriv mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
@@ -3340,7 +3499,7 @@ private module FlowExploration {
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -3353,7 +3512,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathThroughCallable0(
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
@@ -3363,13 +3522,164 @@ private module FlowExploration {
}
private predicate partialPathThroughCallable(
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
out = kind.getAnOutNode(call)
)
}
private predicate revPartialPathStep(
PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
RevPartialAccessPath ap, Configuration config
) {
localFlowStep(node, mid.getNode(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
additionalLocalFlowStep(node, mid.getNode(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
jumpStep(node, mid.getNode(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
additionalJumpStep(node, mid.getNode(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
revPartialPathReadStep(mid, _, _, node, ap) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
exists(RevPartialAccessPath ap0, Content c |
revPartialPathStoreStep(mid, ap0, c, node, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
apConsRev(ap, c, ap0, config)
)
or
exists(ParameterNode p |
mid.getNode() = p and
viableParamArg(_, p, node) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
)
or
exists(ReturnPosition pos |
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
pos = getReturnPosition(node)
)
or
revPartialPathThroughCallable(mid, node, ap, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2()
}
pragma[inline]
private predicate revPartialPathReadStep(
PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
) {
exists(Node midNode |
midNode = mid.getNode() and
ap1 = mid.getAp() and
read(node, c, midNode) and
ap2.getHead() = c and
ap2.len() = unbindInt(ap1.len() + 1)
)
}
pragma[nomagic]
private predicate apConsRev(
RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodeRev mid |
revPartialPathReadStep(mid, ap1, c, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathStoreStep(
PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
) {
exists(Node midNode, TypedContent tc |
midNode = mid.getNode() and
ap = mid.getAp() and
store(node, tc, midNode, _) and
ap.getHead() = c and
config = mid.getConfiguration() and
tc.getContent() = c
)
}
pragma[nomagic]
private predicate revPartialPathIntoReturn(
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
DataFlowCall call, RevPartialAccessPath ap, Configuration config
) {
exists(Node out |
mid.getNode() = out and
viableReturnPosOut(call, pos, out) and
sc1 = TRevSummaryCtx1Some(pos) and
sc2 = TRevSummaryCtx2Some(ap) and
ap = mid.getAp() and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathFlowsThrough(
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
Configuration config
) {
exists(PartialPathNodeRev mid, ParameterNode p |
mid.getNode() = p and
p.isParameterOf(_, pos) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathThroughCallable0(
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
Configuration config
) {
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and
revPartialPathFlowsThrough(pos, sc1, sc2, ap, config)
)
}
pragma[nomagic]
private predicate revPartialPathThroughCallable(
PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, int pos |
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
node.argumentOf(call, pos)
)
}
}
import FlowExploration
@@ -3378,6 +3688,14 @@ private predicate partialFlow(
PartialPathNode source, PartialPathNode node, Configuration configuration
) {
source.getConfiguration() = configuration and
configuration.isSource(source.getNode()) and
source.isFwdSource() and
node = source.getASuccessor+()
}
private predicate revPartialFlow(
PartialPathNode node, PartialPathNode sink, Configuration configuration
) {
sink.getConfiguration() = configuration and
sink.isRevSink() and
node.getASuccessor+() = sink
}

View File

@@ -112,8 +112,8 @@ abstract class Configuration extends string {
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
/**
* Gets the exploration limit for `hasPartialFlow` measured in approximate
* number of interprocedural steps.
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
* measured in approximate number of interprocedural steps.
*/
int explorationLimit() { none() }
@@ -123,7 +123,7 @@ abstract class Configuration extends string {
* is restricted to be less than or equal to `explorationLimit()`. This
* predicate completely disregards sink definitions.
*
* This predicate is intended for dataflow exploration and debugging and may
* This predicate is intended for data-flow exploration and debugging and may
* perform poorly if the number of sources is too big and/or the exploration
* limit is set too high without using barriers.
*
@@ -136,6 +136,29 @@ abstract class Configuration extends string {
partialFlow(source, node, this) and
dist = node.getSourceDistance()
}
/**
* Holds if there is a partial data flow path from `node` to `sink`. The
* approximate distance between `node` and the closest sink is `dist` and
* is restricted to be less than or equal to `explorationLimit()`. This
* predicate completely disregards source definitions.
*
* This predicate is intended for data-flow exploration and debugging and may
* perform poorly if the number of sinks is too big and/or the exploration
* limit is set too high without using barriers.
*
* This predicate is disabled (has no results) by default. Override
* `explorationLimit()` with a suitable number to enable this predicate.
*
* To use this in a `path-problem` query, import the module `PartialPathGraph`.
*
* Note that reverse flow has slightly lower precision than the corresponding
* forward flow, as reverse flow disregards type pruning among other features.
*/
final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) {
revPartialFlow(node, sink, this) and
dist = node.getSinkDistance()
}
}
/**
@@ -2943,12 +2966,26 @@ private module FlowExploration {
)
}
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
or
exists(DataFlowCallable mid |
interestingCallableSink(mid, config) and callableStep(c, mid, config)
)
}
private newtype TCallableExt =
TCallable(DataFlowCallable c, Configuration config) { interestingCallableSrc(c, config) } or
TCallableSrc()
TCallable(DataFlowCallable c, Configuration config) {
interestingCallableSrc(c, config) or
interestingCallableSink(c, config)
} or
TCallableSrc() or
TCallableSink()
private predicate callableExtSrc(TCallableSrc src) { any() }
private predicate callableExtSink(TCallableSink sink) { any() }
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
callableStep(c1, c2, config) and
@@ -2961,15 +2998,32 @@ private module FlowExploration {
config.isSource(n) and
ce2 = TCallable(n.getEnclosingCallable(), config)
)
or
exists(Node n, Configuration config |
ce2 = TCallableSink() and
config.isSink(n) and
ce1 = TCallable(n.getEnclosingCallable(), config)
)
}
private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) {
callableExtStepFwd(ce2, ce1)
}
private int distSrcExt(TCallableExt c) =
shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result)
private int distSinkExt(TCallableExt c) =
shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result)
private int distSrc(DataFlowCallable c, Configuration config) {
result = distSrcExt(TCallable(c, config)) - 1
}
private int distSink(DataFlowCallable c, Configuration config) {
result = distSinkExt(TCallable(c, config)) - 1
}
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
@@ -2997,18 +3051,12 @@ private module FlowExploration {
or
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
}
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
override string toString() {
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
exists(DataFlowType t | this = TPartialNil(t) | result = TFrontNil(t))
}
}
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
@@ -3019,9 +3067,39 @@ private module FlowExploration {
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
}
override AccessPathFront getFront() {
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
private newtype TRevPartialAccessPath =
TRevPartialNil() or
TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] }
/**
* Conceptually a list of `Content`s, but only the first
* element of the list and its length are tracked.
*/
private class RevPartialAccessPath extends TRevPartialAccessPath {
abstract string toString();
Content getHead() { this = TRevPartialCons(result, _) }
int len() {
this = TRevPartialNil() and result = 0
or
this = TRevPartialCons(_, result)
}
}
private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil {
override string toString() { result = "" }
}
private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons {
override string toString() {
exists(Content c, int len | this = TRevPartialCons(c, len) |
if len = 1
then result = "[" + c.toString() + "]"
else result = "[" + c.toString() + ", ... (" + len.toString() + ")]"
)
}
}
@@ -3033,8 +3111,16 @@ private module FlowExploration {
TSummaryCtx2None() or
TSummaryCtx2Some(PartialAccessPath ap)
private newtype TRevSummaryCtx1 =
TRevSummaryCtx1None() or
TRevSummaryCtx1Some(ReturnPosition pos)
private newtype TRevSummaryCtx2 =
TRevSummaryCtx2None() or
TRevSummaryCtx2Some(RevPartialAccessPath ap)
private newtype TPartialPathNode =
TPartialPathNodeMk(
TPartialPathNodeFwd(
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
@@ -3048,6 +3134,23 @@ private module FlowExploration {
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
Configuration config
) {
config.isSink(node) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil() and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
}
pragma[nomagic]
@@ -3055,7 +3158,7 @@ private module FlowExploration {
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
exists(PartialPathNode mid |
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
if node instanceof CastingNode
@@ -3107,15 +3210,32 @@ private module FlowExploration {
result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
}
/**
* Gets the approximate distance to the nearest sink measured in number
* of interprocedural steps.
*/
int getSinkDistance() {
result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
}
private string ppAp() {
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
exists(string s |
s = this.(PartialPathNodeFwd).getAp().toString() or
s = this.(PartialPathNodeRev).getAp().toString()
|
if s = "" then result = "" else result = " " + s
)
}
private string ppCtx() {
result = " <" + this.(PartialPathNodePriv).getCallContext().toString() + ">"
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
}
/** Holds if this is a source in a forward-flow path. */
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
/** Holds if this is a sink in a reverse-flow path. */
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
}
/**
@@ -3126,7 +3246,7 @@ private module FlowExploration {
query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b }
}
private class PartialPathNodePriv extends PartialPathNode {
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
Node node;
CallContext cc;
TSummaryCtx1 sc1;
@@ -3134,7 +3254,7 @@ private module FlowExploration {
PartialAccessPath ap;
Configuration config;
PartialPathNodePriv() { this = TPartialPathNodeMk(node, cc, sc1, sc2, ap, config) }
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
override Node getNode() { result = node }
@@ -3148,14 +3268,54 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodePriv getASuccessor() {
override PartialPathNodeFwd getASuccessor() {
partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
}
predicate isSource() {
config.isSource(node) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
ap instanceof TPartialNil
}
}
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
Node node;
TRevSummaryCtx1 sc1;
TRevSummaryCtx2 sc2;
RevPartialAccessPath ap;
Configuration config;
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
override Node getNode() { result = node }
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
TRevSummaryCtx2 getSummaryCtx2() { result = sc2 }
RevPartialAccessPath getAp() { result = ap }
override Configuration getConfiguration() { result = config }
override PartialPathNodeRev getASuccessor() {
revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
this.getAp(), this.getConfiguration())
}
predicate isSink() {
config.isSink(node) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil()
}
}
private predicate partialPathStep(
PartialPathNodePriv mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
@@ -3221,8 +3381,7 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
PartialAccessPath ap2
PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
) {
exists(Node midNode, DataFlowType contentType |
midNode = mid.getNode() and
@@ -3238,7 +3397,7 @@ private module FlowExploration {
private predicate apConsFwd(
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
exists(PartialPathNodeFwd mid |
partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
@@ -3246,7 +3405,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathReadStep(
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
exists(Node midNode |
@@ -3260,7 +3419,7 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable0(
PartialPathNodePriv mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
Configuration config
) {
pos = getReturnPosition(mid.getNode()) and
@@ -3272,7 +3431,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathOutOfCallable1(
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
@@ -3286,7 +3445,7 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
@@ -3297,7 +3456,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathIntoArg(
PartialPathNodePriv mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
exists(ArgumentNode arg |
@@ -3311,7 +3470,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathIntoCallable0(
PartialPathNodePriv mid, DataFlowCallable callable, int i, CallContext outercc,
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
DataFlowCall call, PartialAccessPath ap, Configuration config
) {
partialPathIntoArg(mid, i, outercc, call, ap, config) and
@@ -3319,7 +3478,7 @@ private module FlowExploration {
}
private predicate partialPathIntoCallable(
PartialPathNodePriv mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
@@ -3340,7 +3499,7 @@ private module FlowExploration {
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -3353,7 +3512,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathThroughCallable0(
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
@@ -3363,13 +3522,164 @@ private module FlowExploration {
}
private predicate partialPathThroughCallable(
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
out = kind.getAnOutNode(call)
)
}
private predicate revPartialPathStep(
PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
RevPartialAccessPath ap, Configuration config
) {
localFlowStep(node, mid.getNode(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
additionalLocalFlowStep(node, mid.getNode(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
jumpStep(node, mid.getNode(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
additionalJumpStep(node, mid.getNode(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
revPartialPathReadStep(mid, _, _, node, ap) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
exists(RevPartialAccessPath ap0, Content c |
revPartialPathStoreStep(mid, ap0, c, node, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
apConsRev(ap, c, ap0, config)
)
or
exists(ParameterNode p |
mid.getNode() = p and
viableParamArg(_, p, node) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
)
or
exists(ReturnPosition pos |
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
pos = getReturnPosition(node)
)
or
revPartialPathThroughCallable(mid, node, ap, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2()
}
pragma[inline]
private predicate revPartialPathReadStep(
PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
) {
exists(Node midNode |
midNode = mid.getNode() and
ap1 = mid.getAp() and
read(node, c, midNode) and
ap2.getHead() = c and
ap2.len() = unbindInt(ap1.len() + 1)
)
}
pragma[nomagic]
private predicate apConsRev(
RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodeRev mid |
revPartialPathReadStep(mid, ap1, c, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathStoreStep(
PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
) {
exists(Node midNode, TypedContent tc |
midNode = mid.getNode() and
ap = mid.getAp() and
store(node, tc, midNode, _) and
ap.getHead() = c and
config = mid.getConfiguration() and
tc.getContent() = c
)
}
pragma[nomagic]
private predicate revPartialPathIntoReturn(
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
DataFlowCall call, RevPartialAccessPath ap, Configuration config
) {
exists(Node out |
mid.getNode() = out and
viableReturnPosOut(call, pos, out) and
sc1 = TRevSummaryCtx1Some(pos) and
sc2 = TRevSummaryCtx2Some(ap) and
ap = mid.getAp() and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathFlowsThrough(
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
Configuration config
) {
exists(PartialPathNodeRev mid, ParameterNode p |
mid.getNode() = p and
p.isParameterOf(_, pos) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathThroughCallable0(
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
Configuration config
) {
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and
revPartialPathFlowsThrough(pos, sc1, sc2, ap, config)
)
}
pragma[nomagic]
private predicate revPartialPathThroughCallable(
PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, int pos |
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
node.argumentOf(call, pos)
)
}
}
import FlowExploration
@@ -3378,6 +3688,14 @@ private predicate partialFlow(
PartialPathNode source, PartialPathNode node, Configuration configuration
) {
source.getConfiguration() = configuration and
configuration.isSource(source.getNode()) and
source.isFwdSource() and
node = source.getASuccessor+()
}
private predicate revPartialFlow(
PartialPathNode node, PartialPathNode sink, Configuration configuration
) {
sink.getConfiguration() = configuration and
sink.isRevSink() and
node.getASuccessor+() = sink
}