mirror of
https://github.com/github/codeql.git
synced 2025-12-20 18:56:32 +01:00
Merge branch 'main' into python-add-source-nodes
This commit is contained in:
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* Provides an extension point for for modeling user-controlled data.
|
||||
* Such data is often used as data-flow sources in security queries.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
// Need to import since frameworks can extend `RemoteFlowSource::Range`
|
||||
|
||||
@@ -44,12 +44,24 @@ class StepSummary extends TStepSummary {
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides predicates for updating step summaries (`StepSummary`s). */
|
||||
module StepSummary {
|
||||
/**
|
||||
* Gets the summary that corresponds to having taken a forwards
|
||||
* heap and/or inter-procedural step from `nodeFrom` to `nodeTo`.
|
||||
*/
|
||||
cached
|
||||
predicate step(LocalSourceNode nodeFrom, Node nodeTo, StepSummary summary) {
|
||||
exists(Node mid | typePreservingStep*(nodeFrom, mid) and smallstep(mid, nodeTo, summary))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the summary that corresponds to having taken a forwards
|
||||
* local, heap and/or inter-procedural step from `nodeFrom` to `nodeTo`.
|
||||
*
|
||||
* Unlike `StepSummary::step`, this predicate does not compress
|
||||
* type-preserving steps.
|
||||
*/
|
||||
predicate smallstep(Node nodeFrom, Node nodeTo, StepSummary summary) {
|
||||
typePreservingStep(nodeFrom, nodeTo) and
|
||||
summary = LevelStep()
|
||||
@@ -290,6 +302,7 @@ class TypeTracker extends TTypeTracker {
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides predicates for implementing custom `TypeTracker`s. */
|
||||
module TypeTracker {
|
||||
/**
|
||||
* Gets a valid end point of type tracking.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -118,11 +118,13 @@ class Node extends TNode {
|
||||
Node track(TypeTracker t2, TypeTracker t) { t = t2.step(this, result) }
|
||||
}
|
||||
|
||||
/** A data-flow node corresponding to an SSA variable. */
|
||||
class EssaNode extends Node, TEssaNode {
|
||||
EssaVariable var;
|
||||
|
||||
EssaNode() { this = TEssaNode(var) }
|
||||
|
||||
/** Gets the `EssaVariable` represented by this data-flow node. */
|
||||
EssaVariable getVar() { result = var }
|
||||
|
||||
override EssaVariable asVar() { result = var }
|
||||
@@ -135,11 +137,13 @@ class EssaNode extends Node, TEssaNode {
|
||||
override Location getLocation() { result = var.getDefinition().getLocation() }
|
||||
}
|
||||
|
||||
/** A data-flow node corresponding to a control-flow node. */
|
||||
class CfgNode extends Node, TCfgNode {
|
||||
ControlFlowNode node;
|
||||
|
||||
CfgNode() { this = TCfgNode(node) }
|
||||
|
||||
/** Gets the `ControlFlowNode` represented by this data-flow node. */
|
||||
ControlFlowNode getNode() { result = node }
|
||||
|
||||
override ControlFlowNode asCfgNode() { result = node }
|
||||
@@ -365,14 +369,15 @@ class LocalSourceNode extends Node {
|
||||
}
|
||||
|
||||
/**
|
||||
* A reference contained in an object. This is either a field or a property.
|
||||
* Algebraic datatype for tracking data content associated with values.
|
||||
* Content can be collection elements or object attributes.
|
||||
*/
|
||||
newtype TContent =
|
||||
/** An element of a list. */
|
||||
TListElementContent() or
|
||||
/** An element of a set. */
|
||||
TSetElementContent() or
|
||||
/** An element of a tuple at a specifik index. */
|
||||
/** An element of a tuple at a specific index. */
|
||||
TTupleElementContent(int index) { exists(any(TupleNode tn).getElement(index)) } or
|
||||
/** An element of a dictionary under a specific key. */
|
||||
TDictionaryElementContent(string key) {
|
||||
@@ -380,24 +385,32 @@ newtype TContent =
|
||||
or
|
||||
key = any(Keyword kw).getArg()
|
||||
} or
|
||||
/** An element of a dictionary at any key. */
|
||||
/** An element of a dictionary under any key. */
|
||||
TDictionaryElementAnyContent() or
|
||||
/** An object attribute. */
|
||||
TAttributeContent(string attr) { attr = any(Attribute a).getName() }
|
||||
|
||||
/**
|
||||
* A data-flow value can have associated content.
|
||||
* If the value is a collection, it can have elements,
|
||||
* if it is an object, it can have attribute values.
|
||||
*/
|
||||
class Content extends TContent {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "Content" }
|
||||
}
|
||||
|
||||
/** An element of a list. */
|
||||
class ListElementContent extends TListElementContent, Content {
|
||||
override string toString() { result = "List element" }
|
||||
}
|
||||
|
||||
/** An element of a set. */
|
||||
class SetElementContent extends TSetElementContent, Content {
|
||||
override string toString() { result = "Set element" }
|
||||
}
|
||||
|
||||
/** An element of a tuple at a specific index. */
|
||||
class TupleElementContent extends TTupleElementContent, Content {
|
||||
int index;
|
||||
|
||||
@@ -409,6 +422,7 @@ class TupleElementContent extends TTupleElementContent, Content {
|
||||
override string toString() { result = "Tuple element at index " + index.toString() }
|
||||
}
|
||||
|
||||
/** An element of a dictionary under a specific key. */
|
||||
class DictionaryElementContent extends TDictionaryElementContent, Content {
|
||||
string key;
|
||||
|
||||
@@ -420,10 +434,12 @@ class DictionaryElementContent extends TDictionaryElementContent, Content {
|
||||
override string toString() { result = "Dictionary element at key " + key }
|
||||
}
|
||||
|
||||
/** An element of a dictionary under any key. */
|
||||
class DictionaryElementAnyContent extends TDictionaryElementAnyContent, Content {
|
||||
override string toString() { result = "Any dictionary element" }
|
||||
}
|
||||
|
||||
/** An object attribute. */
|
||||
class AttributeContent extends TAttributeContent, Content {
|
||||
private string attr;
|
||||
|
||||
|
||||
@@ -446,7 +446,6 @@ private module SsaComputeImpl {
|
||||
* ```
|
||||
*/
|
||||
pragma[nomagic]
|
||||
cached
|
||||
private predicate adjacentRefUse(
|
||||
SsaSourceVariable v, BasicBlock b2, int i2, ControlFlowNode use1
|
||||
) {
|
||||
|
||||
@@ -629,8 +629,7 @@ private module Django {
|
||||
t.start() and
|
||||
result = response_attr("HttpResponse")
|
||||
or
|
||||
// TODO: remove/expand this part of the template as needed
|
||||
// Handle `http.HttpResponse` alias
|
||||
// Handle `django.http.HttpResponse` alias
|
||||
t.start() and
|
||||
result = http_attr("HttpResponse")
|
||||
or
|
||||
@@ -670,7 +669,7 @@ private module Django {
|
||||
result.asCfgNode() in [node.getArg(1), node.getArgByName("content_type")]
|
||||
}
|
||||
|
||||
override string getMimetypeDefault() { result = "text/html; charset=utf-8" }
|
||||
override string getMimetypeDefault() { result = "text/html" }
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `django.http.response.HttpResponse`. */
|
||||
@@ -700,8 +699,7 @@ private module Django {
|
||||
t.start() and
|
||||
result = response_attr("HttpResponseRedirect")
|
||||
or
|
||||
// TODO: remove/expand this part of the template as needed
|
||||
// Handle `http.HttpResponseRedirect` alias
|
||||
// Handle `django.http.HttpResponseRedirect` alias
|
||||
t.start() and
|
||||
result = http_attr("HttpResponseRedirect")
|
||||
or
|
||||
@@ -732,13 +730,16 @@ private module Django {
|
||||
ClassInstantiation() { node.getFunction() = classRef().asCfgNode() }
|
||||
|
||||
override DataFlow::Node getBody() {
|
||||
result.asCfgNode() in [node.getArg(0), node.getArgByName("redirect_to")]
|
||||
// note that even though browsers like Chrome usually doesn't fetch the
|
||||
// content of a redirect, it is possible to observe the body (for example,
|
||||
// with cURL).
|
||||
result.asCfgNode() in [node.getArg(1), node.getArgByName("content")]
|
||||
}
|
||||
|
||||
// How to support the `headers` argument here?
|
||||
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
||||
|
||||
override string getMimetypeDefault() { result = "text/html; charset=utf-8" }
|
||||
override string getMimetypeDefault() { result = "text/html" }
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `django.http.response.HttpResponseRedirect`. */
|
||||
@@ -764,8 +765,7 @@ private module Django {
|
||||
t.start() and
|
||||
result = response_attr("HttpResponsePermanentRedirect")
|
||||
or
|
||||
// TODO: remove/expand this part of the template as needed
|
||||
// Handle `http.HttpResponsePermanentRedirect` alias
|
||||
// Handle `django.http.HttpResponsePermanentRedirect` alias
|
||||
t.start() and
|
||||
result = http_attr("HttpResponsePermanentRedirect")
|
||||
or
|
||||
@@ -796,13 +796,16 @@ private module Django {
|
||||
ClassInstantiation() { node.getFunction() = classRef().asCfgNode() }
|
||||
|
||||
override DataFlow::Node getBody() {
|
||||
result.asCfgNode() in [node.getArg(0), node.getArgByName("redirect_to")]
|
||||
// note that even though browsers like Chrome usually doesn't fetch the
|
||||
// content of a redirect, it is possible to observe the body (for example,
|
||||
// with cURL).
|
||||
result.asCfgNode() in [node.getArg(1), node.getArgByName("content")]
|
||||
}
|
||||
|
||||
// How to support the `headers` argument here?
|
||||
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
||||
|
||||
override string getMimetypeDefault() { result = "text/html; charset=utf-8" }
|
||||
override string getMimetypeDefault() { result = "text/html" }
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `django.http.response.HttpResponsePermanentRedirect`. */
|
||||
@@ -829,7 +832,7 @@ private module Django {
|
||||
result = response_attr("HttpResponseNotModified")
|
||||
or
|
||||
// TODO: remove/expand this part of the template as needed
|
||||
// Handle `http.HttpResponseNotModified` alias
|
||||
// Handle `django.http.HttpResponseNotModified` alias
|
||||
t.start() and
|
||||
result = http_attr("HttpResponseNotModified")
|
||||
or
|
||||
@@ -890,8 +893,7 @@ private module Django {
|
||||
t.start() and
|
||||
result = response_attr("HttpResponseBadRequest")
|
||||
or
|
||||
// TODO: remove/expand this part of the template as needed
|
||||
// Handle `http.HttpResponseBadRequest` alias
|
||||
// Handle `django.http.HttpResponseBadRequest` alias
|
||||
t.start() and
|
||||
result = http_attr("HttpResponseBadRequest")
|
||||
or
|
||||
@@ -928,7 +930,7 @@ private module Django {
|
||||
// How to support the `headers` argument here?
|
||||
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
||||
|
||||
override string getMimetypeDefault() { result = "text/html; charset=utf-8" }
|
||||
override string getMimetypeDefault() { result = "text/html" }
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `django.http.response.HttpResponseBadRequest`. */
|
||||
@@ -954,8 +956,7 @@ private module Django {
|
||||
t.start() and
|
||||
result = response_attr("HttpResponseNotFound")
|
||||
or
|
||||
// TODO: remove/expand this part of the template as needed
|
||||
// Handle `http.HttpResponseNotFound` alias
|
||||
// Handle `django.http.HttpResponseNotFound` alias
|
||||
t.start() and
|
||||
result = http_attr("HttpResponseNotFound")
|
||||
or
|
||||
@@ -992,7 +993,7 @@ private module Django {
|
||||
// How to support the `headers` argument here?
|
||||
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
||||
|
||||
override string getMimetypeDefault() { result = "text/html; charset=utf-8" }
|
||||
override string getMimetypeDefault() { result = "text/html" }
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `django.http.response.HttpResponseNotFound`. */
|
||||
@@ -1018,8 +1019,7 @@ private module Django {
|
||||
t.start() and
|
||||
result = response_attr("HttpResponseForbidden")
|
||||
or
|
||||
// TODO: remove/expand this part of the template as needed
|
||||
// Handle `http.HttpResponseForbidden` alias
|
||||
// Handle `django.http.HttpResponseForbidden` alias
|
||||
t.start() and
|
||||
result = http_attr("HttpResponseForbidden")
|
||||
or
|
||||
@@ -1056,7 +1056,7 @@ private module Django {
|
||||
// How to support the `headers` argument here?
|
||||
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
||||
|
||||
override string getMimetypeDefault() { result = "text/html; charset=utf-8" }
|
||||
override string getMimetypeDefault() { result = "text/html" }
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `django.http.response.HttpResponseForbidden`. */
|
||||
@@ -1082,8 +1082,7 @@ private module Django {
|
||||
t.start() and
|
||||
result = response_attr("HttpResponseNotAllowed")
|
||||
or
|
||||
// TODO: remove/expand this part of the template as needed
|
||||
// Handle `http.HttpResponseNotAllowed` alias
|
||||
// Handle `django.http.HttpResponseNotAllowed` alias
|
||||
t.start() and
|
||||
result = http_attr("HttpResponseNotAllowed")
|
||||
or
|
||||
@@ -1121,7 +1120,7 @@ private module Django {
|
||||
// How to support the `headers` argument here?
|
||||
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
||||
|
||||
override string getMimetypeDefault() { result = "text/html; charset=utf-8" }
|
||||
override string getMimetypeDefault() { result = "text/html" }
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `django.http.response.HttpResponseNotAllowed`. */
|
||||
@@ -1147,8 +1146,7 @@ private module Django {
|
||||
t.start() and
|
||||
result = response_attr("HttpResponseGone")
|
||||
or
|
||||
// TODO: remove/expand this part of the template as needed
|
||||
// Handle `http.HttpResponseGone` alias
|
||||
// Handle `django.http.HttpResponseGone` alias
|
||||
t.start() and
|
||||
result = http_attr("HttpResponseGone")
|
||||
or
|
||||
@@ -1185,7 +1183,7 @@ private module Django {
|
||||
// How to support the `headers` argument here?
|
||||
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
||||
|
||||
override string getMimetypeDefault() { result = "text/html; charset=utf-8" }
|
||||
override string getMimetypeDefault() { result = "text/html" }
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `django.http.response.HttpResponseGone`. */
|
||||
@@ -1211,8 +1209,7 @@ private module Django {
|
||||
t.start() and
|
||||
result = response_attr("HttpResponseServerError")
|
||||
or
|
||||
// TODO: remove/expand this part of the template as needed
|
||||
// Handle `http.HttpResponseServerError` alias
|
||||
// Handle `django.http.HttpResponseServerError` alias
|
||||
t.start() and
|
||||
result = http_attr("HttpResponseServerError")
|
||||
or
|
||||
@@ -1249,7 +1246,7 @@ private module Django {
|
||||
// How to support the `headers` argument here?
|
||||
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
||||
|
||||
override string getMimetypeDefault() { result = "text/html; charset=utf-8" }
|
||||
override string getMimetypeDefault() { result = "text/html" }
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `django.http.response.HttpResponseServerError`. */
|
||||
@@ -1275,8 +1272,7 @@ private module Django {
|
||||
t.start() and
|
||||
result = response_attr("JsonResponse")
|
||||
or
|
||||
// TODO: remove/expand this part of the template as needed
|
||||
// Handle `http.JsonResponse` alias
|
||||
// Handle `django.http.JsonResponse` alias
|
||||
t.start() and
|
||||
result = http_attr("JsonResponse")
|
||||
or
|
||||
@@ -1342,8 +1338,7 @@ private module Django {
|
||||
t.start() and
|
||||
result = response_attr("StreamingHttpResponse")
|
||||
or
|
||||
// TODO: remove/expand this part of the template as needed
|
||||
// Handle `http.StreamingHttpResponse` alias
|
||||
// Handle `django.http.StreamingHttpResponse` alias
|
||||
t.start() and
|
||||
result = http_attr("StreamingHttpResponse")
|
||||
or
|
||||
@@ -1380,7 +1375,7 @@ private module Django {
|
||||
// How to support the `headers` argument here?
|
||||
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
||||
|
||||
override string getMimetypeDefault() { result = "text/html; charset=utf-8" }
|
||||
override string getMimetypeDefault() { result = "text/html" }
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `django.http.response.StreamingHttpResponse`. */
|
||||
@@ -1406,8 +1401,7 @@ private module Django {
|
||||
t.start() and
|
||||
result = response_attr("FileResponse")
|
||||
or
|
||||
// TODO: remove/expand this part of the template as needed
|
||||
// Handle `http.FileResponse` alias
|
||||
// Handle `django.http.FileResponse` alias
|
||||
t.start() and
|
||||
result = http_attr("FileResponse")
|
||||
or
|
||||
@@ -1444,7 +1438,10 @@ private module Django {
|
||||
// How to support the `headers` argument here?
|
||||
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
||||
|
||||
override string getMimetypeDefault() { result = "text/html; charset=utf-8" }
|
||||
override string getMimetypeDefault() {
|
||||
// see https://github.com/django/django/blob/ebb08d19424c314c75908bc6048ff57c2f872269/django/http/response.py#L471-L479
|
||||
result = "application/octet-stream"
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `django.http.response.FileResponse`. */
|
||||
|
||||
@@ -32,6 +32,7 @@ module MySQLdb {
|
||||
/** Gets a reference to the `MySQLdb` module. */
|
||||
DataFlow::Node moduleMySQLdb() { result = moduleMySQLdb(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** MySQLdb implements PEP 249, providing ways to execute SQL statements against a database. */
|
||||
class MySQLdb extends PEP249Module {
|
||||
MySQLdb() { this = moduleMySQLdb() }
|
||||
}
|
||||
|
||||
@@ -6,6 +6,12 @@ private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
|
||||
/**
|
||||
* Provides models for the `Werkzeug` PyPI package.
|
||||
* See
|
||||
* - https://pypi.org/project/Werkzeug/
|
||||
* - https://werkzeug.palletsprojects.com/en/1.0.x/#werkzeug
|
||||
*/
|
||||
module Werkzeug {
|
||||
/** Provides models for the `werkzeug` module. */
|
||||
module werkzeug {
|
||||
|
||||
Reference in New Issue
Block a user