Merge branch 'main' into jcogs33/mad-metrics-query

This commit is contained in:
Jami
2022-12-14 15:33:29 -05:00
committed by GitHub
274 changed files with 20617 additions and 7290 deletions

View File

@@ -15,8 +15,18 @@ class Diagnostic extends @diagnostic {
string getGeneratedBy() { diagnostics(this, result, _, _, _, _, _) }
/**
* Gets the severity of the message, on a range from 1 to 5: 1=remark,
* 2=warning, 3=discretionary error, 4=error, 5=catastrophic error.
* Gets the severity of the message.
*
* For Java, this ranges from 1 to 8:
* 1=warning(low)
* 2=warning(normal)
* 3=warning(high)
* 4=error(low) Minor extractor errors, with minimal impact on analysis
* 5=error(normal) Most extractor errors, with local impact on analysis
* 6=error(high) Errors from the frontend
* 7=error(severe) Severe extractor errors affecting a single source file
* 8=error(global) Severe extractor errors likely to affect multiple source files
* For Kotlin, only the "normal" warning and error severities are used.
*/
int getSeverity() { diagnostics(this, _, result, _, _, _, _) }

View File

@@ -628,6 +628,9 @@ class Constructor extends Callable, @constructor {
/** Holds if this is a default constructor, not explicitly declared in source code. */
predicate isDefaultConstructor() { isDefConstr(this) }
/** Holds if this is a constructor without parameters. */
predicate isParameterless() { this.getNumberOfParameters() = 0 }
override Constructor getSourceDeclaration() { constrs(this, _, _, _, _, result) }
override string getSignature() { constrs(this, _, result, _, _, _) }

View File

@@ -621,23 +621,6 @@ private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind)
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -980,6 +963,12 @@ private module Stage1 implements StageSig {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
pragma[nomagic]
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
revFlow(node, config) and
exists(ap)
}
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, _, pragma[only_bind_into](config)) and
@@ -1022,9 +1011,13 @@ private module Stage1 implements StageSig {
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
kind = ret.getKind() and
exists(argAp) and
exists(ap)
}
pragma[nomagic]
@@ -1189,6 +1182,8 @@ private signature module StageSig {
predicate revFlow(NodeEx node, Configuration config);
predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
@@ -1196,7 +1191,9 @@ private signature module StageSig {
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1281,21 +1278,43 @@ private module MkStage<StageSig PrevStage> {
import Param
/* Begin: Stage logic. */
bindingset[result, apa]
private ApApprox unbindApa(ApApprox apa) {
pragma[only_bind_out](apa) = pragma[only_bind_out](result)
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlowAp(node, apa, config)
}
pragma[nomagic]
private predicate flowIntoCallApa(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowOutOfCallApa(
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
ApApprox apa, Configuration config
) {
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
ApApprox argApa, ApApprox apa, Configuration config
) {
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
exists(ReturnKindExt kind |
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call)
)
}
/**
@@ -1307,39 +1326,50 @@ private module MkStage<StageSig PrevStage> {
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, ApApprox apa, Configuration config
) {
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
PrevStage::revFlow(node, state, apa, config) and
filter(node, state, ap, config)
}
pragma[inline]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
Ap ap, ApApprox apa, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
ap = ap0 and
apa = apa0
or
localStep(mid, state0, node, state, false, ap, config, localCc) and
ap0 instanceof ApNil
ap0 instanceof ApNil and
apa = getApprox(ap)
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
@@ -1352,7 +1382,8 @@ private module MkStage<StageSig PrevStage> {
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
@@ -1361,32 +1392,32 @@ private module MkStage<StageSig PrevStage> {
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
ap = apCons(tc, ap0) and
apa = getApprox(ap)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
fwdFlowConsCand(ap0, c, ap, config) and
apa = getApprox(ap)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
or
// flow out of a callable
@@ -1394,8 +1425,8 @@ private module MkStage<StageSig PrevStage> {
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1403,7 +1434,7 @@ private module MkStage<StageSig PrevStage> {
or
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, apa, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
@@ -1413,9 +1444,9 @@ private module MkStage<StageSig PrevStage> {
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
exists(DataFlowType contentType, ApApprox apa1 |
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1433,43 +1464,104 @@ private module MkStage<StageSig PrevStage> {
)
}
private class ApNonNil instanceof Ap {
pragma[nomagic]
ApNonNil() { not this instanceof ApNil }
string toString() { result = "" }
}
pragma[nomagic]
private predicate fwdFlowRead0(
NodeEx node1, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ApNonNil ap, Configuration config
) {
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, _, _, config)
}
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowRetFromArg(
RetNodeEx ret, FlowState state, CcCall ccc, ParameterPosition summaryCtx, ParamNodeEx p,
Ap argAp, ApApprox argApa, Ap ap, ApApprox apa, Configuration config
) {
exists(DataFlowCallable c, ReturnKindExt kind |
fwdFlow(pragma[only_bind_into](ret), state, ccc,
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, apa, config) and
getApprox(argAp) = argApa and
c = ret.getEnclosingCallable() and
kind = ret.getKind() and
p.isParameterOf(c, pragma[only_bind_into](summaryCtx)) and
parameterFlowThroughAllowed(p, kind)
)
}
pragma[inline]
private predicate fwdFlowInMayFlowThrough(
DataFlowCall call, Cc cc, CcCall innerCc, ParameterPositionOption summaryCtx, ApOption argAp,
ParamNodeEx param, Ap ap, ApApprox apa, Configuration config
) {
fwdFlowIn(call, pragma[only_bind_into](param), _, cc, innerCc, summaryCtx, argAp, ap,
pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(param, apa, config)
}
// dedup before joining with `flowThroughOutOfCall`
pragma[nomagic]
private predicate fwdFlowInMayFlowThroughProj(
DataFlowCall call, CcCall innerCc, ApApprox apa, Configuration config
) {
fwdFlowInMayFlowThrough(call, _, innerCc, _, _, _, _, apa, config)
}
/**
* Same as `flowThroughOutOfCall`, but restricted to calls that are reached
* in the flow covered by `fwdFlow`, where data might flow through the target
* callable and back out at `call`.
*/
pragma[nomagic]
private predicate fwdFlowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
ApApprox argApa, ApApprox apa, Configuration config
) {
fwdFlowInMayFlowThroughProj(call, ccc, argApa, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
ApApprox apa, Configuration config
) {
exists(
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc, ApApprox argApa |
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
summaryCtx, _, argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa),
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
fwdFlowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any())
)
}
@@ -1483,8 +1575,7 @@ private module MkStage<StageSig PrevStage> {
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
fwdFlowInMayFlowThrough(call, cc, _, summaryCtx, argAp, param, ap, _, config) and
pos = param.getPosition()
)
}
@@ -1505,41 +1596,55 @@ private module MkStage<StageSig PrevStage> {
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
exists(boolean allowsFieldFlow, ApApprox argApa, ApApprox apa |
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), _, p,
argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa), config) and
kind = ret.getKind() and
fwdFlowThroughOutOfCall(_, ccc, ret, _, allowsFieldFlow, argApa, apa, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any())
)
}
pragma[nomagic]
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
Configuration config
) {
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
exists(ApApprox argApa |
flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
pragma[only_bind_into](config)) and
if allowsFieldFlow = false then argAp instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate flowIntoCallAp(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
Configuration config
) {
exists(ApApprox apa |
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
fwdFlow(arg, _, _, _, _, ap, apa, config)
)
}
pragma[nomagic]
private predicate flowOutOfCallAp(
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Ap ap, Configuration config
) {
exists(ApApprox apa |
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, config) and
fwdFlow(ret, _, _, _, _, ap, apa, config)
)
}
@@ -1628,7 +1733,7 @@ private module MkStage<StageSig PrevStage> {
// flow into a callable
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
@@ -1682,22 +1787,41 @@ private module MkStage<StageSig PrevStage> {
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
flowOutOfCallAp(call, ret, kind, out, allowsFieldFlow, ap, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
/**
* Same as `flowThroughIntoCall`, but restricted to calls that are reached
* in the flow covered by `revFlow`, where data might flow through the target
* callable and back out at `call`.
*/
pragma[nomagic]
private predicate revFlowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
Configuration config
) {
flowThroughIntoCall(call, arg, p, allowsFieldFlow, argAp, config) and
revFlowIsReturned(call, _, _, _, _, config)
}
pragma[nomagic]
private predicate revFlowParamToReturn(
ParamNodeEx p, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, Configuration config
) {
revFlow(p, state, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
revFlowParamToReturn(p, state, kind, returnAp, ap, config) and
revFlowThroughIntoCall(call, arg, p, allowsFieldFlow, ap, config)
)
}
@@ -1750,6 +1874,11 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
pragma[nomagic]
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
revFlow(node, _, _, _, ap, config)
}
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) {
@@ -1786,9 +1915,9 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
ParamNodeEx p, Ap ap, ReturnKindExt kind, Ap returnAp, Configuration config
) {
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
@@ -1796,27 +1925,36 @@ private module MkStage<StageSig PrevStage> {
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
parameterFlowsThroughRev(p, ap, kind, _, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
) {
exists(ParamNodeEx p |
returnFlowsThrough(ret, kind, _, _, p, argAp, ap, config) and
parameterFlowsThroughRev(p, argAp, kind, ap, config)
)
}
pragma[nomagic]
predicate revFlowInToReturnIsReturned(
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
Ap ap, Configuration config
) {
exists(ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
revFlowInToReturnIsReturned(call, arg, state, returnCtx, returnAp, ap, config)
)
}
@@ -2179,16 +2317,16 @@ private module LocalFlowBigStep {
pragma[nomagic]
predicate localFlowBigStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
DataFlowType t, Configuration config, LocalCallContext callContext
) {
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
localFlowExit(node2, state1, config) and
state1 = state2
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
state1 != state2 and
preservesValue = false and
apf = TFrontNil(node2.getDataFlowType()) and
t = node2.getDataFlowType() and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
@@ -2202,11 +2340,87 @@ private import LocalFlowBigStep
private module Stage3Param implements MkStage<Stage2>::StageParam {
private module PrevStage = Stage2;
class Ap = ApproxAccessPathFront;
class ApNil = ApproxAccessPathFrontNil;
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
pragma[noinline]
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
class ApOption = ApproxAccessPathFrontOption;
ApOption apNone() { result = TApproxAccessPathFrontNone() }
ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
import BooleanCallContext
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
expectsContentEx(node, c) and
c = ap.getAHead().getContent()
)
}
pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
(
notExpectsContent(node)
or
expectsContentCand(node, ap, config)
)
}
bindingset[ap, contentType]
predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
}
private module Stage3 implements StageSig {
import MkStage<Stage2>::Stage<Stage3Param>
}
private module Stage4Param implements MkStage<Stage3>::StageParam {
private module PrevStage = Stage3;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
@@ -2226,16 +2440,42 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
import BooleanCallContext
pragma[nomagic]
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2291,8 +2531,8 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
}
}
private module Stage3 implements StageSig {
import MkStage<Stage2>::Stage<Stage3Param>
private module Stage4 implements StageSig {
import MkStage<Stage3>::Stage<Stage4Param>
}
/**
@@ -2303,8 +2543,8 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2315,10 +2555,10 @@ private predicate flowCandSummaryCtx(
*/
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
exists(int tails, int nodes, int apLimit, int tupleLimit |
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2332,11 +2572,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
private newtype TAccessPathApprox =
TNil(DataFlowType t) or
TConsNil(TypedContent tc, DataFlowType t) {
Stage3::consCand(tc, TFrontNil(t), _) and
Stage4::consCand(tc, TFrontNil(t), _) and
not expensiveLen2unfolding(tc, _)
} or
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
Stage3::consCand(tc1, TFrontHead(tc2), _) and
Stage4::consCand(tc1, TFrontHead(tc2), _) and
len in [2 .. accessPathLimit()] and
not expensiveLen2unfolding(tc1, _)
} or
@@ -2466,7 +2706,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
override AccessPathApprox pop(TypedContent head) {
head = tc and
(
exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
result = TConsCons(tc2, _, len - 1)
or
len = 2 and
@@ -2477,7 +2717,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
or
exists(DataFlowType t |
len = 1 and
Stage3::consCand(tc, TFrontNil(t), _) and
Stage4::consCand(tc, TFrontNil(t), _) and
result = TNil(t)
)
)
@@ -2502,13 +2742,14 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
private module Stage4Param implements MkStage<Stage3>::StageParam {
private module PrevStage = Stage3;
private module Stage5Param implements MkStage<Stage4>::StageParam {
private module PrevStage = Stage4;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
pragma[nomagic]
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
ApNil getApNil(NodeEx node) {
@@ -2534,7 +2775,9 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -2571,7 +2814,7 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
private module Stage4 = MkStage<Stage3>::Stage<Stage4Param>;
private module Stage5 = MkStage<Stage4>::Stage<Stage5Param>;
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
@@ -2585,8 +2828,8 @@ private predicate nodeMayUseSummary0(
) {
exists(AccessPathApprox apa0 |
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2596,7 +2839,7 @@ private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
Stage5::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
@@ -2606,8 +2849,8 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage5::revFlow(p, state, _, config)
)
}
@@ -2656,7 +2899,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
len = apa.len() and
result =
strictcount(AccessPathFront apf |
Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
config)
)
)
@@ -2665,7 +2908,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -2686,7 +2929,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
exists(TypedContent head |
apa.pop(head) = result and
Stage4::consCand(head, result, config)
Stage5::consCand(head, result, config)
)
}
@@ -2768,7 +3011,7 @@ private newtype TPathNode =
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) {
// A PathNode is introduced by a source ...
Stage4::revFlow(node, state, config) and
Stage5::revFlow(node, state, config) and
sourceNode(node, state, config) and
(
if hasSourceCallCtx(config)
@@ -2782,7 +3025,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -2924,7 +3167,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
override TypedContent getHead() { result = head1 }
override AccessPath getTail() {
Stage4::consCand(head1, result.getApprox(), _) and
Stage5::consCand(head1, result.getApprox(), _) and
result.getHead() = head2 and
result.length() = len - 1
}
@@ -2955,7 +3198,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
override TypedContent getHead() { result = head }
override AccessPath getTail() {
Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
}
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
@@ -3347,7 +3590,8 @@ private predicate pathStep(
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -3389,7 +3633,7 @@ private predicate pathReadStep(
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3399,7 +3643,7 @@ private predicate pathStoreStep(
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3436,7 +3680,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
Stage4::revFlow(result, _, apa, config)
Stage5::revFlow(result, _, apa, config)
}
/**
@@ -3472,7 +3716,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
Stage4::revFlow(p, _, apa, config) and
Stage5::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
@@ -3751,9 +3995,17 @@ predicate stageStats(
n = 45 and
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
or
stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
stage = "5 Fwd" and
n = 50 and
Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
or
stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
stage = "5 Rev" and
n = 55 and
Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
or
stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
or
stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
}
private module FlowExploration {

View File

@@ -621,23 +621,6 @@ private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind)
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -980,6 +963,12 @@ private module Stage1 implements StageSig {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
pragma[nomagic]
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
revFlow(node, config) and
exists(ap)
}
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, _, pragma[only_bind_into](config)) and
@@ -1022,9 +1011,13 @@ private module Stage1 implements StageSig {
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
kind = ret.getKind() and
exists(argAp) and
exists(ap)
}
pragma[nomagic]
@@ -1189,6 +1182,8 @@ private signature module StageSig {
predicate revFlow(NodeEx node, Configuration config);
predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
@@ -1196,7 +1191,9 @@ private signature module StageSig {
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1281,21 +1278,43 @@ private module MkStage<StageSig PrevStage> {
import Param
/* Begin: Stage logic. */
bindingset[result, apa]
private ApApprox unbindApa(ApApprox apa) {
pragma[only_bind_out](apa) = pragma[only_bind_out](result)
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlowAp(node, apa, config)
}
pragma[nomagic]
private predicate flowIntoCallApa(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowOutOfCallApa(
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
ApApprox apa, Configuration config
) {
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
ApApprox argApa, ApApprox apa, Configuration config
) {
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
exists(ReturnKindExt kind |
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call)
)
}
/**
@@ -1307,39 +1326,50 @@ private module MkStage<StageSig PrevStage> {
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, ApApprox apa, Configuration config
) {
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
PrevStage::revFlow(node, state, apa, config) and
filter(node, state, ap, config)
}
pragma[inline]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
Ap ap, ApApprox apa, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
ap = ap0 and
apa = apa0
or
localStep(mid, state0, node, state, false, ap, config, localCc) and
ap0 instanceof ApNil
ap0 instanceof ApNil and
apa = getApprox(ap)
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
@@ -1352,7 +1382,8 @@ private module MkStage<StageSig PrevStage> {
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
@@ -1361,32 +1392,32 @@ private module MkStage<StageSig PrevStage> {
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
ap = apCons(tc, ap0) and
apa = getApprox(ap)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
fwdFlowConsCand(ap0, c, ap, config) and
apa = getApprox(ap)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
or
// flow out of a callable
@@ -1394,8 +1425,8 @@ private module MkStage<StageSig PrevStage> {
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1403,7 +1434,7 @@ private module MkStage<StageSig PrevStage> {
or
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, apa, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
@@ -1413,9 +1444,9 @@ private module MkStage<StageSig PrevStage> {
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
exists(DataFlowType contentType, ApApprox apa1 |
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1433,43 +1464,104 @@ private module MkStage<StageSig PrevStage> {
)
}
private class ApNonNil instanceof Ap {
pragma[nomagic]
ApNonNil() { not this instanceof ApNil }
string toString() { result = "" }
}
pragma[nomagic]
private predicate fwdFlowRead0(
NodeEx node1, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ApNonNil ap, Configuration config
) {
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, _, _, config)
}
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowRetFromArg(
RetNodeEx ret, FlowState state, CcCall ccc, ParameterPosition summaryCtx, ParamNodeEx p,
Ap argAp, ApApprox argApa, Ap ap, ApApprox apa, Configuration config
) {
exists(DataFlowCallable c, ReturnKindExt kind |
fwdFlow(pragma[only_bind_into](ret), state, ccc,
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, apa, config) and
getApprox(argAp) = argApa and
c = ret.getEnclosingCallable() and
kind = ret.getKind() and
p.isParameterOf(c, pragma[only_bind_into](summaryCtx)) and
parameterFlowThroughAllowed(p, kind)
)
}
pragma[inline]
private predicate fwdFlowInMayFlowThrough(
DataFlowCall call, Cc cc, CcCall innerCc, ParameterPositionOption summaryCtx, ApOption argAp,
ParamNodeEx param, Ap ap, ApApprox apa, Configuration config
) {
fwdFlowIn(call, pragma[only_bind_into](param), _, cc, innerCc, summaryCtx, argAp, ap,
pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(param, apa, config)
}
// dedup before joining with `flowThroughOutOfCall`
pragma[nomagic]
private predicate fwdFlowInMayFlowThroughProj(
DataFlowCall call, CcCall innerCc, ApApprox apa, Configuration config
) {
fwdFlowInMayFlowThrough(call, _, innerCc, _, _, _, _, apa, config)
}
/**
* Same as `flowThroughOutOfCall`, but restricted to calls that are reached
* in the flow covered by `fwdFlow`, where data might flow through the target
* callable and back out at `call`.
*/
pragma[nomagic]
private predicate fwdFlowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
ApApprox argApa, ApApprox apa, Configuration config
) {
fwdFlowInMayFlowThroughProj(call, ccc, argApa, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
ApApprox apa, Configuration config
) {
exists(
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc, ApApprox argApa |
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
summaryCtx, _, argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa),
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
fwdFlowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any())
)
}
@@ -1483,8 +1575,7 @@ private module MkStage<StageSig PrevStage> {
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
fwdFlowInMayFlowThrough(call, cc, _, summaryCtx, argAp, param, ap, _, config) and
pos = param.getPosition()
)
}
@@ -1505,41 +1596,55 @@ private module MkStage<StageSig PrevStage> {
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
exists(boolean allowsFieldFlow, ApApprox argApa, ApApprox apa |
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), _, p,
argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa), config) and
kind = ret.getKind() and
fwdFlowThroughOutOfCall(_, ccc, ret, _, allowsFieldFlow, argApa, apa, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any())
)
}
pragma[nomagic]
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
Configuration config
) {
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
exists(ApApprox argApa |
flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
pragma[only_bind_into](config)) and
if allowsFieldFlow = false then argAp instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate flowIntoCallAp(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
Configuration config
) {
exists(ApApprox apa |
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
fwdFlow(arg, _, _, _, _, ap, apa, config)
)
}
pragma[nomagic]
private predicate flowOutOfCallAp(
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Ap ap, Configuration config
) {
exists(ApApprox apa |
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, config) and
fwdFlow(ret, _, _, _, _, ap, apa, config)
)
}
@@ -1628,7 +1733,7 @@ private module MkStage<StageSig PrevStage> {
// flow into a callable
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
@@ -1682,22 +1787,41 @@ private module MkStage<StageSig PrevStage> {
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
flowOutOfCallAp(call, ret, kind, out, allowsFieldFlow, ap, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
/**
* Same as `flowThroughIntoCall`, but restricted to calls that are reached
* in the flow covered by `revFlow`, where data might flow through the target
* callable and back out at `call`.
*/
pragma[nomagic]
private predicate revFlowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
Configuration config
) {
flowThroughIntoCall(call, arg, p, allowsFieldFlow, argAp, config) and
revFlowIsReturned(call, _, _, _, _, config)
}
pragma[nomagic]
private predicate revFlowParamToReturn(
ParamNodeEx p, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, Configuration config
) {
revFlow(p, state, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
revFlowParamToReturn(p, state, kind, returnAp, ap, config) and
revFlowThroughIntoCall(call, arg, p, allowsFieldFlow, ap, config)
)
}
@@ -1750,6 +1874,11 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
pragma[nomagic]
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
revFlow(node, _, _, _, ap, config)
}
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) {
@@ -1786,9 +1915,9 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
ParamNodeEx p, Ap ap, ReturnKindExt kind, Ap returnAp, Configuration config
) {
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
@@ -1796,27 +1925,36 @@ private module MkStage<StageSig PrevStage> {
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
parameterFlowsThroughRev(p, ap, kind, _, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
) {
exists(ParamNodeEx p |
returnFlowsThrough(ret, kind, _, _, p, argAp, ap, config) and
parameterFlowsThroughRev(p, argAp, kind, ap, config)
)
}
pragma[nomagic]
predicate revFlowInToReturnIsReturned(
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
Ap ap, Configuration config
) {
exists(ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
revFlowInToReturnIsReturned(call, arg, state, returnCtx, returnAp, ap, config)
)
}
@@ -2179,16 +2317,16 @@ private module LocalFlowBigStep {
pragma[nomagic]
predicate localFlowBigStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
DataFlowType t, Configuration config, LocalCallContext callContext
) {
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
localFlowExit(node2, state1, config) and
state1 = state2
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
state1 != state2 and
preservesValue = false and
apf = TFrontNil(node2.getDataFlowType()) and
t = node2.getDataFlowType() and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
@@ -2202,11 +2340,87 @@ private import LocalFlowBigStep
private module Stage3Param implements MkStage<Stage2>::StageParam {
private module PrevStage = Stage2;
class Ap = ApproxAccessPathFront;
class ApNil = ApproxAccessPathFrontNil;
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
pragma[noinline]
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
class ApOption = ApproxAccessPathFrontOption;
ApOption apNone() { result = TApproxAccessPathFrontNone() }
ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
import BooleanCallContext
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
expectsContentEx(node, c) and
c = ap.getAHead().getContent()
)
}
pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
(
notExpectsContent(node)
or
expectsContentCand(node, ap, config)
)
}
bindingset[ap, contentType]
predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
}
private module Stage3 implements StageSig {
import MkStage<Stage2>::Stage<Stage3Param>
}
private module Stage4Param implements MkStage<Stage3>::StageParam {
private module PrevStage = Stage3;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
@@ -2226,16 +2440,42 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
import BooleanCallContext
pragma[nomagic]
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2291,8 +2531,8 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
}
}
private module Stage3 implements StageSig {
import MkStage<Stage2>::Stage<Stage3Param>
private module Stage4 implements StageSig {
import MkStage<Stage3>::Stage<Stage4Param>
}
/**
@@ -2303,8 +2543,8 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2315,10 +2555,10 @@ private predicate flowCandSummaryCtx(
*/
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
exists(int tails, int nodes, int apLimit, int tupleLimit |
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2332,11 +2572,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
private newtype TAccessPathApprox =
TNil(DataFlowType t) or
TConsNil(TypedContent tc, DataFlowType t) {
Stage3::consCand(tc, TFrontNil(t), _) and
Stage4::consCand(tc, TFrontNil(t), _) and
not expensiveLen2unfolding(tc, _)
} or
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
Stage3::consCand(tc1, TFrontHead(tc2), _) and
Stage4::consCand(tc1, TFrontHead(tc2), _) and
len in [2 .. accessPathLimit()] and
not expensiveLen2unfolding(tc1, _)
} or
@@ -2466,7 +2706,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
override AccessPathApprox pop(TypedContent head) {
head = tc and
(
exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
result = TConsCons(tc2, _, len - 1)
or
len = 2 and
@@ -2477,7 +2717,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
or
exists(DataFlowType t |
len = 1 and
Stage3::consCand(tc, TFrontNil(t), _) and
Stage4::consCand(tc, TFrontNil(t), _) and
result = TNil(t)
)
)
@@ -2502,13 +2742,14 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
private module Stage4Param implements MkStage<Stage3>::StageParam {
private module PrevStage = Stage3;
private module Stage5Param implements MkStage<Stage4>::StageParam {
private module PrevStage = Stage4;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
pragma[nomagic]
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
ApNil getApNil(NodeEx node) {
@@ -2534,7 +2775,9 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -2571,7 +2814,7 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
private module Stage4 = MkStage<Stage3>::Stage<Stage4Param>;
private module Stage5 = MkStage<Stage4>::Stage<Stage5Param>;
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
@@ -2585,8 +2828,8 @@ private predicate nodeMayUseSummary0(
) {
exists(AccessPathApprox apa0 |
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2596,7 +2839,7 @@ private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
Stage5::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
@@ -2606,8 +2849,8 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage5::revFlow(p, state, _, config)
)
}
@@ -2656,7 +2899,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
len = apa.len() and
result =
strictcount(AccessPathFront apf |
Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
config)
)
)
@@ -2665,7 +2908,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -2686,7 +2929,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
exists(TypedContent head |
apa.pop(head) = result and
Stage4::consCand(head, result, config)
Stage5::consCand(head, result, config)
)
}
@@ -2768,7 +3011,7 @@ private newtype TPathNode =
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) {
// A PathNode is introduced by a source ...
Stage4::revFlow(node, state, config) and
Stage5::revFlow(node, state, config) and
sourceNode(node, state, config) and
(
if hasSourceCallCtx(config)
@@ -2782,7 +3025,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -2924,7 +3167,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
override TypedContent getHead() { result = head1 }
override AccessPath getTail() {
Stage4::consCand(head1, result.getApprox(), _) and
Stage5::consCand(head1, result.getApprox(), _) and
result.getHead() = head2 and
result.length() = len - 1
}
@@ -2955,7 +3198,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
override TypedContent getHead() { result = head }
override AccessPath getTail() {
Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
}
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
@@ -3347,7 +3590,8 @@ private predicate pathStep(
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -3389,7 +3633,7 @@ private predicate pathReadStep(
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3399,7 +3643,7 @@ private predicate pathStoreStep(
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3436,7 +3680,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
Stage4::revFlow(result, _, apa, config)
Stage5::revFlow(result, _, apa, config)
}
/**
@@ -3472,7 +3716,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
Stage4::revFlow(p, _, apa, config) and
Stage5::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
@@ -3751,9 +3995,17 @@ predicate stageStats(
n = 45 and
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
or
stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
stage = "5 Fwd" and
n = 50 and
Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
or
stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
stage = "5 Rev" and
n = 55 and
Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
or
stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
or
stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
}
private module FlowExploration {

View File

@@ -621,23 +621,6 @@ private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind)
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -980,6 +963,12 @@ private module Stage1 implements StageSig {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
pragma[nomagic]
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
revFlow(node, config) and
exists(ap)
}
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, _, pragma[only_bind_into](config)) and
@@ -1022,9 +1011,13 @@ private module Stage1 implements StageSig {
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
kind = ret.getKind() and
exists(argAp) and
exists(ap)
}
pragma[nomagic]
@@ -1189,6 +1182,8 @@ private signature module StageSig {
predicate revFlow(NodeEx node, Configuration config);
predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
@@ -1196,7 +1191,9 @@ private signature module StageSig {
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1281,21 +1278,43 @@ private module MkStage<StageSig PrevStage> {
import Param
/* Begin: Stage logic. */
bindingset[result, apa]
private ApApprox unbindApa(ApApprox apa) {
pragma[only_bind_out](apa) = pragma[only_bind_out](result)
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlowAp(node, apa, config)
}
pragma[nomagic]
private predicate flowIntoCallApa(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowOutOfCallApa(
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
ApApprox apa, Configuration config
) {
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
ApApprox argApa, ApApprox apa, Configuration config
) {
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
exists(ReturnKindExt kind |
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call)
)
}
/**
@@ -1307,39 +1326,50 @@ private module MkStage<StageSig PrevStage> {
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, ApApprox apa, Configuration config
) {
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
PrevStage::revFlow(node, state, apa, config) and
filter(node, state, ap, config)
}
pragma[inline]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
Ap ap, ApApprox apa, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
ap = ap0 and
apa = apa0
or
localStep(mid, state0, node, state, false, ap, config, localCc) and
ap0 instanceof ApNil
ap0 instanceof ApNil and
apa = getApprox(ap)
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
@@ -1352,7 +1382,8 @@ private module MkStage<StageSig PrevStage> {
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
@@ -1361,32 +1392,32 @@ private module MkStage<StageSig PrevStage> {
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
ap = apCons(tc, ap0) and
apa = getApprox(ap)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
fwdFlowConsCand(ap0, c, ap, config) and
apa = getApprox(ap)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
or
// flow out of a callable
@@ -1394,8 +1425,8 @@ private module MkStage<StageSig PrevStage> {
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1403,7 +1434,7 @@ private module MkStage<StageSig PrevStage> {
or
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, apa, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
@@ -1413,9 +1444,9 @@ private module MkStage<StageSig PrevStage> {
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
exists(DataFlowType contentType, ApApprox apa1 |
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1433,43 +1464,104 @@ private module MkStage<StageSig PrevStage> {
)
}
private class ApNonNil instanceof Ap {
pragma[nomagic]
ApNonNil() { not this instanceof ApNil }
string toString() { result = "" }
}
pragma[nomagic]
private predicate fwdFlowRead0(
NodeEx node1, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ApNonNil ap, Configuration config
) {
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, _, _, config)
}
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowRetFromArg(
RetNodeEx ret, FlowState state, CcCall ccc, ParameterPosition summaryCtx, ParamNodeEx p,
Ap argAp, ApApprox argApa, Ap ap, ApApprox apa, Configuration config
) {
exists(DataFlowCallable c, ReturnKindExt kind |
fwdFlow(pragma[only_bind_into](ret), state, ccc,
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, apa, config) and
getApprox(argAp) = argApa and
c = ret.getEnclosingCallable() and
kind = ret.getKind() and
p.isParameterOf(c, pragma[only_bind_into](summaryCtx)) and
parameterFlowThroughAllowed(p, kind)
)
}
pragma[inline]
private predicate fwdFlowInMayFlowThrough(
DataFlowCall call, Cc cc, CcCall innerCc, ParameterPositionOption summaryCtx, ApOption argAp,
ParamNodeEx param, Ap ap, ApApprox apa, Configuration config
) {
fwdFlowIn(call, pragma[only_bind_into](param), _, cc, innerCc, summaryCtx, argAp, ap,
pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(param, apa, config)
}
// dedup before joining with `flowThroughOutOfCall`
pragma[nomagic]
private predicate fwdFlowInMayFlowThroughProj(
DataFlowCall call, CcCall innerCc, ApApprox apa, Configuration config
) {
fwdFlowInMayFlowThrough(call, _, innerCc, _, _, _, _, apa, config)
}
/**
* Same as `flowThroughOutOfCall`, but restricted to calls that are reached
* in the flow covered by `fwdFlow`, where data might flow through the target
* callable and back out at `call`.
*/
pragma[nomagic]
private predicate fwdFlowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
ApApprox argApa, ApApprox apa, Configuration config
) {
fwdFlowInMayFlowThroughProj(call, ccc, argApa, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
ApApprox apa, Configuration config
) {
exists(
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc, ApApprox argApa |
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
summaryCtx, _, argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa),
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
fwdFlowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any())
)
}
@@ -1483,8 +1575,7 @@ private module MkStage<StageSig PrevStage> {
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
fwdFlowInMayFlowThrough(call, cc, _, summaryCtx, argAp, param, ap, _, config) and
pos = param.getPosition()
)
}
@@ -1505,41 +1596,55 @@ private module MkStage<StageSig PrevStage> {
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
exists(boolean allowsFieldFlow, ApApprox argApa, ApApprox apa |
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), _, p,
argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa), config) and
kind = ret.getKind() and
fwdFlowThroughOutOfCall(_, ccc, ret, _, allowsFieldFlow, argApa, apa, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any())
)
}
pragma[nomagic]
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
Configuration config
) {
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
exists(ApApprox argApa |
flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
pragma[only_bind_into](config)) and
if allowsFieldFlow = false then argAp instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate flowIntoCallAp(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
Configuration config
) {
exists(ApApprox apa |
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
fwdFlow(arg, _, _, _, _, ap, apa, config)
)
}
pragma[nomagic]
private predicate flowOutOfCallAp(
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Ap ap, Configuration config
) {
exists(ApApprox apa |
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, config) and
fwdFlow(ret, _, _, _, _, ap, apa, config)
)
}
@@ -1628,7 +1733,7 @@ private module MkStage<StageSig PrevStage> {
// flow into a callable
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
@@ -1682,22 +1787,41 @@ private module MkStage<StageSig PrevStage> {
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
flowOutOfCallAp(call, ret, kind, out, allowsFieldFlow, ap, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
/**
* Same as `flowThroughIntoCall`, but restricted to calls that are reached
* in the flow covered by `revFlow`, where data might flow through the target
* callable and back out at `call`.
*/
pragma[nomagic]
private predicate revFlowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
Configuration config
) {
flowThroughIntoCall(call, arg, p, allowsFieldFlow, argAp, config) and
revFlowIsReturned(call, _, _, _, _, config)
}
pragma[nomagic]
private predicate revFlowParamToReturn(
ParamNodeEx p, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, Configuration config
) {
revFlow(p, state, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
revFlowParamToReturn(p, state, kind, returnAp, ap, config) and
revFlowThroughIntoCall(call, arg, p, allowsFieldFlow, ap, config)
)
}
@@ -1750,6 +1874,11 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
pragma[nomagic]
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
revFlow(node, _, _, _, ap, config)
}
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) {
@@ -1786,9 +1915,9 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
ParamNodeEx p, Ap ap, ReturnKindExt kind, Ap returnAp, Configuration config
) {
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
@@ -1796,27 +1925,36 @@ private module MkStage<StageSig PrevStage> {
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
parameterFlowsThroughRev(p, ap, kind, _, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
) {
exists(ParamNodeEx p |
returnFlowsThrough(ret, kind, _, _, p, argAp, ap, config) and
parameterFlowsThroughRev(p, argAp, kind, ap, config)
)
}
pragma[nomagic]
predicate revFlowInToReturnIsReturned(
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
Ap ap, Configuration config
) {
exists(ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
revFlowInToReturnIsReturned(call, arg, state, returnCtx, returnAp, ap, config)
)
}
@@ -2179,16 +2317,16 @@ private module LocalFlowBigStep {
pragma[nomagic]
predicate localFlowBigStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
DataFlowType t, Configuration config, LocalCallContext callContext
) {
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
localFlowExit(node2, state1, config) and
state1 = state2
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
state1 != state2 and
preservesValue = false and
apf = TFrontNil(node2.getDataFlowType()) and
t = node2.getDataFlowType() and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
@@ -2202,11 +2340,87 @@ private import LocalFlowBigStep
private module Stage3Param implements MkStage<Stage2>::StageParam {
private module PrevStage = Stage2;
class Ap = ApproxAccessPathFront;
class ApNil = ApproxAccessPathFrontNil;
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
pragma[noinline]
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
class ApOption = ApproxAccessPathFrontOption;
ApOption apNone() { result = TApproxAccessPathFrontNone() }
ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
import BooleanCallContext
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
expectsContentEx(node, c) and
c = ap.getAHead().getContent()
)
}
pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
(
notExpectsContent(node)
or
expectsContentCand(node, ap, config)
)
}
bindingset[ap, contentType]
predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
}
private module Stage3 implements StageSig {
import MkStage<Stage2>::Stage<Stage3Param>
}
private module Stage4Param implements MkStage<Stage3>::StageParam {
private module PrevStage = Stage3;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
@@ -2226,16 +2440,42 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
import BooleanCallContext
pragma[nomagic]
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2291,8 +2531,8 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
}
}
private module Stage3 implements StageSig {
import MkStage<Stage2>::Stage<Stage3Param>
private module Stage4 implements StageSig {
import MkStage<Stage3>::Stage<Stage4Param>
}
/**
@@ -2303,8 +2543,8 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2315,10 +2555,10 @@ private predicate flowCandSummaryCtx(
*/
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
exists(int tails, int nodes, int apLimit, int tupleLimit |
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2332,11 +2572,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
private newtype TAccessPathApprox =
TNil(DataFlowType t) or
TConsNil(TypedContent tc, DataFlowType t) {
Stage3::consCand(tc, TFrontNil(t), _) and
Stage4::consCand(tc, TFrontNil(t), _) and
not expensiveLen2unfolding(tc, _)
} or
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
Stage3::consCand(tc1, TFrontHead(tc2), _) and
Stage4::consCand(tc1, TFrontHead(tc2), _) and
len in [2 .. accessPathLimit()] and
not expensiveLen2unfolding(tc1, _)
} or
@@ -2466,7 +2706,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
override AccessPathApprox pop(TypedContent head) {
head = tc and
(
exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
result = TConsCons(tc2, _, len - 1)
or
len = 2 and
@@ -2477,7 +2717,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
or
exists(DataFlowType t |
len = 1 and
Stage3::consCand(tc, TFrontNil(t), _) and
Stage4::consCand(tc, TFrontNil(t), _) and
result = TNil(t)
)
)
@@ -2502,13 +2742,14 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
private module Stage4Param implements MkStage<Stage3>::StageParam {
private module PrevStage = Stage3;
private module Stage5Param implements MkStage<Stage4>::StageParam {
private module PrevStage = Stage4;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
pragma[nomagic]
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
ApNil getApNil(NodeEx node) {
@@ -2534,7 +2775,9 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -2571,7 +2814,7 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
private module Stage4 = MkStage<Stage3>::Stage<Stage4Param>;
private module Stage5 = MkStage<Stage4>::Stage<Stage5Param>;
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
@@ -2585,8 +2828,8 @@ private predicate nodeMayUseSummary0(
) {
exists(AccessPathApprox apa0 |
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2596,7 +2839,7 @@ private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
Stage5::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
@@ -2606,8 +2849,8 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage5::revFlow(p, state, _, config)
)
}
@@ -2656,7 +2899,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
len = apa.len() and
result =
strictcount(AccessPathFront apf |
Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
config)
)
)
@@ -2665,7 +2908,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -2686,7 +2929,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
exists(TypedContent head |
apa.pop(head) = result and
Stage4::consCand(head, result, config)
Stage5::consCand(head, result, config)
)
}
@@ -2768,7 +3011,7 @@ private newtype TPathNode =
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) {
// A PathNode is introduced by a source ...
Stage4::revFlow(node, state, config) and
Stage5::revFlow(node, state, config) and
sourceNode(node, state, config) and
(
if hasSourceCallCtx(config)
@@ -2782,7 +3025,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -2924,7 +3167,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
override TypedContent getHead() { result = head1 }
override AccessPath getTail() {
Stage4::consCand(head1, result.getApprox(), _) and
Stage5::consCand(head1, result.getApprox(), _) and
result.getHead() = head2 and
result.length() = len - 1
}
@@ -2955,7 +3198,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
override TypedContent getHead() { result = head }
override AccessPath getTail() {
Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
}
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
@@ -3347,7 +3590,8 @@ private predicate pathStep(
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -3389,7 +3633,7 @@ private predicate pathReadStep(
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3399,7 +3643,7 @@ private predicate pathStoreStep(
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3436,7 +3680,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
Stage4::revFlow(result, _, apa, config)
Stage5::revFlow(result, _, apa, config)
}
/**
@@ -3472,7 +3716,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
Stage4::revFlow(p, _, apa, config) and
Stage5::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
@@ -3751,9 +3995,17 @@ predicate stageStats(
n = 45 and
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
or
stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
stage = "5 Fwd" and
n = 50 and
Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
or
stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
stage = "5 Rev" and
n = 55 and
Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
or
stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
or
stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
}
private module FlowExploration {

View File

@@ -621,23 +621,6 @@ private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind)
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -980,6 +963,12 @@ private module Stage1 implements StageSig {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
pragma[nomagic]
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
revFlow(node, config) and
exists(ap)
}
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, _, pragma[only_bind_into](config)) and
@@ -1022,9 +1011,13 @@ private module Stage1 implements StageSig {
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
kind = ret.getKind() and
exists(argAp) and
exists(ap)
}
pragma[nomagic]
@@ -1189,6 +1182,8 @@ private signature module StageSig {
predicate revFlow(NodeEx node, Configuration config);
predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
@@ -1196,7 +1191,9 @@ private signature module StageSig {
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1281,21 +1278,43 @@ private module MkStage<StageSig PrevStage> {
import Param
/* Begin: Stage logic. */
bindingset[result, apa]
private ApApprox unbindApa(ApApprox apa) {
pragma[only_bind_out](apa) = pragma[only_bind_out](result)
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlowAp(node, apa, config)
}
pragma[nomagic]
private predicate flowIntoCallApa(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowOutOfCallApa(
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
ApApprox apa, Configuration config
) {
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
ApApprox argApa, ApApprox apa, Configuration config
) {
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
exists(ReturnKindExt kind |
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call)
)
}
/**
@@ -1307,39 +1326,50 @@ private module MkStage<StageSig PrevStage> {
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, ApApprox apa, Configuration config
) {
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
PrevStage::revFlow(node, state, apa, config) and
filter(node, state, ap, config)
}
pragma[inline]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
Ap ap, ApApprox apa, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
ap = ap0 and
apa = apa0
or
localStep(mid, state0, node, state, false, ap, config, localCc) and
ap0 instanceof ApNil
ap0 instanceof ApNil and
apa = getApprox(ap)
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
@@ -1352,7 +1382,8 @@ private module MkStage<StageSig PrevStage> {
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
@@ -1361,32 +1392,32 @@ private module MkStage<StageSig PrevStage> {
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
ap = apCons(tc, ap0) and
apa = getApprox(ap)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
fwdFlowConsCand(ap0, c, ap, config) and
apa = getApprox(ap)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
or
// flow out of a callable
@@ -1394,8 +1425,8 @@ private module MkStage<StageSig PrevStage> {
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1403,7 +1434,7 @@ private module MkStage<StageSig PrevStage> {
or
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, apa, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
@@ -1413,9 +1444,9 @@ private module MkStage<StageSig PrevStage> {
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
exists(DataFlowType contentType, ApApprox apa1 |
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1433,43 +1464,104 @@ private module MkStage<StageSig PrevStage> {
)
}
private class ApNonNil instanceof Ap {
pragma[nomagic]
ApNonNil() { not this instanceof ApNil }
string toString() { result = "" }
}
pragma[nomagic]
private predicate fwdFlowRead0(
NodeEx node1, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ApNonNil ap, Configuration config
) {
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, _, _, config)
}
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowRetFromArg(
RetNodeEx ret, FlowState state, CcCall ccc, ParameterPosition summaryCtx, ParamNodeEx p,
Ap argAp, ApApprox argApa, Ap ap, ApApprox apa, Configuration config
) {
exists(DataFlowCallable c, ReturnKindExt kind |
fwdFlow(pragma[only_bind_into](ret), state, ccc,
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, apa, config) and
getApprox(argAp) = argApa and
c = ret.getEnclosingCallable() and
kind = ret.getKind() and
p.isParameterOf(c, pragma[only_bind_into](summaryCtx)) and
parameterFlowThroughAllowed(p, kind)
)
}
pragma[inline]
private predicate fwdFlowInMayFlowThrough(
DataFlowCall call, Cc cc, CcCall innerCc, ParameterPositionOption summaryCtx, ApOption argAp,
ParamNodeEx param, Ap ap, ApApprox apa, Configuration config
) {
fwdFlowIn(call, pragma[only_bind_into](param), _, cc, innerCc, summaryCtx, argAp, ap,
pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(param, apa, config)
}
// dedup before joining with `flowThroughOutOfCall`
pragma[nomagic]
private predicate fwdFlowInMayFlowThroughProj(
DataFlowCall call, CcCall innerCc, ApApprox apa, Configuration config
) {
fwdFlowInMayFlowThrough(call, _, innerCc, _, _, _, _, apa, config)
}
/**
* Same as `flowThroughOutOfCall`, but restricted to calls that are reached
* in the flow covered by `fwdFlow`, where data might flow through the target
* callable and back out at `call`.
*/
pragma[nomagic]
private predicate fwdFlowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
ApApprox argApa, ApApprox apa, Configuration config
) {
fwdFlowInMayFlowThroughProj(call, ccc, argApa, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
ApApprox apa, Configuration config
) {
exists(
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc, ApApprox argApa |
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
summaryCtx, _, argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa),
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
fwdFlowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any())
)
}
@@ -1483,8 +1575,7 @@ private module MkStage<StageSig PrevStage> {
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
fwdFlowInMayFlowThrough(call, cc, _, summaryCtx, argAp, param, ap, _, config) and
pos = param.getPosition()
)
}
@@ -1505,41 +1596,55 @@ private module MkStage<StageSig PrevStage> {
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
exists(boolean allowsFieldFlow, ApApprox argApa, ApApprox apa |
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), _, p,
argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa), config) and
kind = ret.getKind() and
fwdFlowThroughOutOfCall(_, ccc, ret, _, allowsFieldFlow, argApa, apa, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any())
)
}
pragma[nomagic]
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
Configuration config
) {
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
exists(ApApprox argApa |
flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
pragma[only_bind_into](config)) and
if allowsFieldFlow = false then argAp instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate flowIntoCallAp(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
Configuration config
) {
exists(ApApprox apa |
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
fwdFlow(arg, _, _, _, _, ap, apa, config)
)
}
pragma[nomagic]
private predicate flowOutOfCallAp(
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Ap ap, Configuration config
) {
exists(ApApprox apa |
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, config) and
fwdFlow(ret, _, _, _, _, ap, apa, config)
)
}
@@ -1628,7 +1733,7 @@ private module MkStage<StageSig PrevStage> {
// flow into a callable
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
@@ -1682,22 +1787,41 @@ private module MkStage<StageSig PrevStage> {
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
flowOutOfCallAp(call, ret, kind, out, allowsFieldFlow, ap, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
/**
* Same as `flowThroughIntoCall`, but restricted to calls that are reached
* in the flow covered by `revFlow`, where data might flow through the target
* callable and back out at `call`.
*/
pragma[nomagic]
private predicate revFlowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
Configuration config
) {
flowThroughIntoCall(call, arg, p, allowsFieldFlow, argAp, config) and
revFlowIsReturned(call, _, _, _, _, config)
}
pragma[nomagic]
private predicate revFlowParamToReturn(
ParamNodeEx p, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, Configuration config
) {
revFlow(p, state, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
revFlowParamToReturn(p, state, kind, returnAp, ap, config) and
revFlowThroughIntoCall(call, arg, p, allowsFieldFlow, ap, config)
)
}
@@ -1750,6 +1874,11 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
pragma[nomagic]
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
revFlow(node, _, _, _, ap, config)
}
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) {
@@ -1786,9 +1915,9 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
ParamNodeEx p, Ap ap, ReturnKindExt kind, Ap returnAp, Configuration config
) {
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
@@ -1796,27 +1925,36 @@ private module MkStage<StageSig PrevStage> {
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
parameterFlowsThroughRev(p, ap, kind, _, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
) {
exists(ParamNodeEx p |
returnFlowsThrough(ret, kind, _, _, p, argAp, ap, config) and
parameterFlowsThroughRev(p, argAp, kind, ap, config)
)
}
pragma[nomagic]
predicate revFlowInToReturnIsReturned(
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
Ap ap, Configuration config
) {
exists(ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
revFlowInToReturnIsReturned(call, arg, state, returnCtx, returnAp, ap, config)
)
}
@@ -2179,16 +2317,16 @@ private module LocalFlowBigStep {
pragma[nomagic]
predicate localFlowBigStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
DataFlowType t, Configuration config, LocalCallContext callContext
) {
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
localFlowExit(node2, state1, config) and
state1 = state2
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
state1 != state2 and
preservesValue = false and
apf = TFrontNil(node2.getDataFlowType()) and
t = node2.getDataFlowType() and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
@@ -2202,11 +2340,87 @@ private import LocalFlowBigStep
private module Stage3Param implements MkStage<Stage2>::StageParam {
private module PrevStage = Stage2;
class Ap = ApproxAccessPathFront;
class ApNil = ApproxAccessPathFrontNil;
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
pragma[noinline]
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
class ApOption = ApproxAccessPathFrontOption;
ApOption apNone() { result = TApproxAccessPathFrontNone() }
ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
import BooleanCallContext
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
expectsContentEx(node, c) and
c = ap.getAHead().getContent()
)
}
pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
(
notExpectsContent(node)
or
expectsContentCand(node, ap, config)
)
}
bindingset[ap, contentType]
predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
}
private module Stage3 implements StageSig {
import MkStage<Stage2>::Stage<Stage3Param>
}
private module Stage4Param implements MkStage<Stage3>::StageParam {
private module PrevStage = Stage3;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
@@ -2226,16 +2440,42 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
import BooleanCallContext
pragma[nomagic]
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2291,8 +2531,8 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
}
}
private module Stage3 implements StageSig {
import MkStage<Stage2>::Stage<Stage3Param>
private module Stage4 implements StageSig {
import MkStage<Stage3>::Stage<Stage4Param>
}
/**
@@ -2303,8 +2543,8 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2315,10 +2555,10 @@ private predicate flowCandSummaryCtx(
*/
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
exists(int tails, int nodes, int apLimit, int tupleLimit |
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2332,11 +2572,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
private newtype TAccessPathApprox =
TNil(DataFlowType t) or
TConsNil(TypedContent tc, DataFlowType t) {
Stage3::consCand(tc, TFrontNil(t), _) and
Stage4::consCand(tc, TFrontNil(t), _) and
not expensiveLen2unfolding(tc, _)
} or
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
Stage3::consCand(tc1, TFrontHead(tc2), _) and
Stage4::consCand(tc1, TFrontHead(tc2), _) and
len in [2 .. accessPathLimit()] and
not expensiveLen2unfolding(tc1, _)
} or
@@ -2466,7 +2706,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
override AccessPathApprox pop(TypedContent head) {
head = tc and
(
exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
result = TConsCons(tc2, _, len - 1)
or
len = 2 and
@@ -2477,7 +2717,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
or
exists(DataFlowType t |
len = 1 and
Stage3::consCand(tc, TFrontNil(t), _) and
Stage4::consCand(tc, TFrontNil(t), _) and
result = TNil(t)
)
)
@@ -2502,13 +2742,14 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
private module Stage4Param implements MkStage<Stage3>::StageParam {
private module PrevStage = Stage3;
private module Stage5Param implements MkStage<Stage4>::StageParam {
private module PrevStage = Stage4;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
pragma[nomagic]
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
ApNil getApNil(NodeEx node) {
@@ -2534,7 +2775,9 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -2571,7 +2814,7 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
private module Stage4 = MkStage<Stage3>::Stage<Stage4Param>;
private module Stage5 = MkStage<Stage4>::Stage<Stage5Param>;
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
@@ -2585,8 +2828,8 @@ private predicate nodeMayUseSummary0(
) {
exists(AccessPathApprox apa0 |
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2596,7 +2839,7 @@ private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
Stage5::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
@@ -2606,8 +2849,8 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage5::revFlow(p, state, _, config)
)
}
@@ -2656,7 +2899,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
len = apa.len() and
result =
strictcount(AccessPathFront apf |
Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
config)
)
)
@@ -2665,7 +2908,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -2686,7 +2929,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
exists(TypedContent head |
apa.pop(head) = result and
Stage4::consCand(head, result, config)
Stage5::consCand(head, result, config)
)
}
@@ -2768,7 +3011,7 @@ private newtype TPathNode =
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) {
// A PathNode is introduced by a source ...
Stage4::revFlow(node, state, config) and
Stage5::revFlow(node, state, config) and
sourceNode(node, state, config) and
(
if hasSourceCallCtx(config)
@@ -2782,7 +3025,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -2924,7 +3167,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
override TypedContent getHead() { result = head1 }
override AccessPath getTail() {
Stage4::consCand(head1, result.getApprox(), _) and
Stage5::consCand(head1, result.getApprox(), _) and
result.getHead() = head2 and
result.length() = len - 1
}
@@ -2955,7 +3198,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
override TypedContent getHead() { result = head }
override AccessPath getTail() {
Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
}
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
@@ -3347,7 +3590,8 @@ private predicate pathStep(
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -3389,7 +3633,7 @@ private predicate pathReadStep(
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3399,7 +3643,7 @@ private predicate pathStoreStep(
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3436,7 +3680,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
Stage4::revFlow(result, _, apa, config)
Stage5::revFlow(result, _, apa, config)
}
/**
@@ -3472,7 +3716,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
Stage4::revFlow(p, _, apa, config) and
Stage5::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
@@ -3751,9 +3995,17 @@ predicate stageStats(
n = 45 and
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
or
stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
stage = "5 Fwd" and
n = 50 and
Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
or
stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
stage = "5 Rev" and
n = 55 and
Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
or
stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
or
stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
}
private module FlowExploration {

View File

@@ -621,23 +621,6 @@ private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind)
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -980,6 +963,12 @@ private module Stage1 implements StageSig {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
pragma[nomagic]
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
revFlow(node, config) and
exists(ap)
}
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, _, pragma[only_bind_into](config)) and
@@ -1022,9 +1011,13 @@ private module Stage1 implements StageSig {
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
kind = ret.getKind() and
exists(argAp) and
exists(ap)
}
pragma[nomagic]
@@ -1189,6 +1182,8 @@ private signature module StageSig {
predicate revFlow(NodeEx node, Configuration config);
predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
@@ -1196,7 +1191,9 @@ private signature module StageSig {
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1281,21 +1278,43 @@ private module MkStage<StageSig PrevStage> {
import Param
/* Begin: Stage logic. */
bindingset[result, apa]
private ApApprox unbindApa(ApApprox apa) {
pragma[only_bind_out](apa) = pragma[only_bind_out](result)
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlowAp(node, apa, config)
}
pragma[nomagic]
private predicate flowIntoCallApa(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowOutOfCallApa(
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
ApApprox apa, Configuration config
) {
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
ApApprox argApa, ApApprox apa, Configuration config
) {
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
exists(ReturnKindExt kind |
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call)
)
}
/**
@@ -1307,39 +1326,50 @@ private module MkStage<StageSig PrevStage> {
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, ApApprox apa, Configuration config
) {
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
PrevStage::revFlow(node, state, apa, config) and
filter(node, state, ap, config)
}
pragma[inline]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
Ap ap, ApApprox apa, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
ap = ap0 and
apa = apa0
or
localStep(mid, state0, node, state, false, ap, config, localCc) and
ap0 instanceof ApNil
ap0 instanceof ApNil and
apa = getApprox(ap)
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
@@ -1352,7 +1382,8 @@ private module MkStage<StageSig PrevStage> {
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
@@ -1361,32 +1392,32 @@ private module MkStage<StageSig PrevStage> {
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
ap = apCons(tc, ap0) and
apa = getApprox(ap)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
fwdFlowConsCand(ap0, c, ap, config) and
apa = getApprox(ap)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
or
// flow out of a callable
@@ -1394,8 +1425,8 @@ private module MkStage<StageSig PrevStage> {
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1403,7 +1434,7 @@ private module MkStage<StageSig PrevStage> {
or
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, apa, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
@@ -1413,9 +1444,9 @@ private module MkStage<StageSig PrevStage> {
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
exists(DataFlowType contentType, ApApprox apa1 |
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1433,43 +1464,104 @@ private module MkStage<StageSig PrevStage> {
)
}
private class ApNonNil instanceof Ap {
pragma[nomagic]
ApNonNil() { not this instanceof ApNil }
string toString() { result = "" }
}
pragma[nomagic]
private predicate fwdFlowRead0(
NodeEx node1, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ApNonNil ap, Configuration config
) {
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, _, _, config)
}
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowRetFromArg(
RetNodeEx ret, FlowState state, CcCall ccc, ParameterPosition summaryCtx, ParamNodeEx p,
Ap argAp, ApApprox argApa, Ap ap, ApApprox apa, Configuration config
) {
exists(DataFlowCallable c, ReturnKindExt kind |
fwdFlow(pragma[only_bind_into](ret), state, ccc,
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, apa, config) and
getApprox(argAp) = argApa and
c = ret.getEnclosingCallable() and
kind = ret.getKind() and
p.isParameterOf(c, pragma[only_bind_into](summaryCtx)) and
parameterFlowThroughAllowed(p, kind)
)
}
pragma[inline]
private predicate fwdFlowInMayFlowThrough(
DataFlowCall call, Cc cc, CcCall innerCc, ParameterPositionOption summaryCtx, ApOption argAp,
ParamNodeEx param, Ap ap, ApApprox apa, Configuration config
) {
fwdFlowIn(call, pragma[only_bind_into](param), _, cc, innerCc, summaryCtx, argAp, ap,
pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(param, apa, config)
}
// dedup before joining with `flowThroughOutOfCall`
pragma[nomagic]
private predicate fwdFlowInMayFlowThroughProj(
DataFlowCall call, CcCall innerCc, ApApprox apa, Configuration config
) {
fwdFlowInMayFlowThrough(call, _, innerCc, _, _, _, _, apa, config)
}
/**
* Same as `flowThroughOutOfCall`, but restricted to calls that are reached
* in the flow covered by `fwdFlow`, where data might flow through the target
* callable and back out at `call`.
*/
pragma[nomagic]
private predicate fwdFlowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
ApApprox argApa, ApApprox apa, Configuration config
) {
fwdFlowInMayFlowThroughProj(call, ccc, argApa, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
ApApprox apa, Configuration config
) {
exists(
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc, ApApprox argApa |
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
summaryCtx, _, argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa),
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
fwdFlowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any())
)
}
@@ -1483,8 +1575,7 @@ private module MkStage<StageSig PrevStage> {
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
fwdFlowInMayFlowThrough(call, cc, _, summaryCtx, argAp, param, ap, _, config) and
pos = param.getPosition()
)
}
@@ -1505,41 +1596,55 @@ private module MkStage<StageSig PrevStage> {
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
exists(boolean allowsFieldFlow, ApApprox argApa, ApApprox apa |
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), _, p,
argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa), config) and
kind = ret.getKind() and
fwdFlowThroughOutOfCall(_, ccc, ret, _, allowsFieldFlow, argApa, apa, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any())
)
}
pragma[nomagic]
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
Configuration config
) {
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
exists(ApApprox argApa |
flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
pragma[only_bind_into](config)) and
if allowsFieldFlow = false then argAp instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate flowIntoCallAp(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
Configuration config
) {
exists(ApApprox apa |
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
fwdFlow(arg, _, _, _, _, ap, apa, config)
)
}
pragma[nomagic]
private predicate flowOutOfCallAp(
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Ap ap, Configuration config
) {
exists(ApApprox apa |
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, config) and
fwdFlow(ret, _, _, _, _, ap, apa, config)
)
}
@@ -1628,7 +1733,7 @@ private module MkStage<StageSig PrevStage> {
// flow into a callable
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
@@ -1682,22 +1787,41 @@ private module MkStage<StageSig PrevStage> {
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
flowOutOfCallAp(call, ret, kind, out, allowsFieldFlow, ap, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
/**
* Same as `flowThroughIntoCall`, but restricted to calls that are reached
* in the flow covered by `revFlow`, where data might flow through the target
* callable and back out at `call`.
*/
pragma[nomagic]
private predicate revFlowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
Configuration config
) {
flowThroughIntoCall(call, arg, p, allowsFieldFlow, argAp, config) and
revFlowIsReturned(call, _, _, _, _, config)
}
pragma[nomagic]
private predicate revFlowParamToReturn(
ParamNodeEx p, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, Configuration config
) {
revFlow(p, state, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
revFlowParamToReturn(p, state, kind, returnAp, ap, config) and
revFlowThroughIntoCall(call, arg, p, allowsFieldFlow, ap, config)
)
}
@@ -1750,6 +1874,11 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
pragma[nomagic]
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
revFlow(node, _, _, _, ap, config)
}
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) {
@@ -1786,9 +1915,9 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
ParamNodeEx p, Ap ap, ReturnKindExt kind, Ap returnAp, Configuration config
) {
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
@@ -1796,27 +1925,36 @@ private module MkStage<StageSig PrevStage> {
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
parameterFlowsThroughRev(p, ap, kind, _, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
) {
exists(ParamNodeEx p |
returnFlowsThrough(ret, kind, _, _, p, argAp, ap, config) and
parameterFlowsThroughRev(p, argAp, kind, ap, config)
)
}
pragma[nomagic]
predicate revFlowInToReturnIsReturned(
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
Ap ap, Configuration config
) {
exists(ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
revFlowInToReturnIsReturned(call, arg, state, returnCtx, returnAp, ap, config)
)
}
@@ -2179,16 +2317,16 @@ private module LocalFlowBigStep {
pragma[nomagic]
predicate localFlowBigStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
DataFlowType t, Configuration config, LocalCallContext callContext
) {
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
localFlowExit(node2, state1, config) and
state1 = state2
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
state1 != state2 and
preservesValue = false and
apf = TFrontNil(node2.getDataFlowType()) and
t = node2.getDataFlowType() and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
@@ -2202,11 +2340,87 @@ private import LocalFlowBigStep
private module Stage3Param implements MkStage<Stage2>::StageParam {
private module PrevStage = Stage2;
class Ap = ApproxAccessPathFront;
class ApNil = ApproxAccessPathFrontNil;
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
pragma[noinline]
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
class ApOption = ApproxAccessPathFrontOption;
ApOption apNone() { result = TApproxAccessPathFrontNone() }
ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
import BooleanCallContext
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
expectsContentEx(node, c) and
c = ap.getAHead().getContent()
)
}
pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
(
notExpectsContent(node)
or
expectsContentCand(node, ap, config)
)
}
bindingset[ap, contentType]
predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
}
private module Stage3 implements StageSig {
import MkStage<Stage2>::Stage<Stage3Param>
}
private module Stage4Param implements MkStage<Stage3>::StageParam {
private module PrevStage = Stage3;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
@@ -2226,16 +2440,42 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
import BooleanCallContext
pragma[nomagic]
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2291,8 +2531,8 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
}
}
private module Stage3 implements StageSig {
import MkStage<Stage2>::Stage<Stage3Param>
private module Stage4 implements StageSig {
import MkStage<Stage3>::Stage<Stage4Param>
}
/**
@@ -2303,8 +2543,8 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2315,10 +2555,10 @@ private predicate flowCandSummaryCtx(
*/
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
exists(int tails, int nodes, int apLimit, int tupleLimit |
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2332,11 +2572,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
private newtype TAccessPathApprox =
TNil(DataFlowType t) or
TConsNil(TypedContent tc, DataFlowType t) {
Stage3::consCand(tc, TFrontNil(t), _) and
Stage4::consCand(tc, TFrontNil(t), _) and
not expensiveLen2unfolding(tc, _)
} or
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
Stage3::consCand(tc1, TFrontHead(tc2), _) and
Stage4::consCand(tc1, TFrontHead(tc2), _) and
len in [2 .. accessPathLimit()] and
not expensiveLen2unfolding(tc1, _)
} or
@@ -2466,7 +2706,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
override AccessPathApprox pop(TypedContent head) {
head = tc and
(
exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
result = TConsCons(tc2, _, len - 1)
or
len = 2 and
@@ -2477,7 +2717,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
or
exists(DataFlowType t |
len = 1 and
Stage3::consCand(tc, TFrontNil(t), _) and
Stage4::consCand(tc, TFrontNil(t), _) and
result = TNil(t)
)
)
@@ -2502,13 +2742,14 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
private module Stage4Param implements MkStage<Stage3>::StageParam {
private module PrevStage = Stage3;
private module Stage5Param implements MkStage<Stage4>::StageParam {
private module PrevStage = Stage4;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
pragma[nomagic]
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
ApNil getApNil(NodeEx node) {
@@ -2534,7 +2775,9 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -2571,7 +2814,7 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
private module Stage4 = MkStage<Stage3>::Stage<Stage4Param>;
private module Stage5 = MkStage<Stage4>::Stage<Stage5Param>;
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
@@ -2585,8 +2828,8 @@ private predicate nodeMayUseSummary0(
) {
exists(AccessPathApprox apa0 |
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2596,7 +2839,7 @@ private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
Stage5::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
@@ -2606,8 +2849,8 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage5::revFlow(p, state, _, config)
)
}
@@ -2656,7 +2899,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
len = apa.len() and
result =
strictcount(AccessPathFront apf |
Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
config)
)
)
@@ -2665,7 +2908,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -2686,7 +2929,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
exists(TypedContent head |
apa.pop(head) = result and
Stage4::consCand(head, result, config)
Stage5::consCand(head, result, config)
)
}
@@ -2768,7 +3011,7 @@ private newtype TPathNode =
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) {
// A PathNode is introduced by a source ...
Stage4::revFlow(node, state, config) and
Stage5::revFlow(node, state, config) and
sourceNode(node, state, config) and
(
if hasSourceCallCtx(config)
@@ -2782,7 +3025,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -2924,7 +3167,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
override TypedContent getHead() { result = head1 }
override AccessPath getTail() {
Stage4::consCand(head1, result.getApprox(), _) and
Stage5::consCand(head1, result.getApprox(), _) and
result.getHead() = head2 and
result.length() = len - 1
}
@@ -2955,7 +3198,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
override TypedContent getHead() { result = head }
override AccessPath getTail() {
Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
}
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
@@ -3347,7 +3590,8 @@ private predicate pathStep(
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -3389,7 +3633,7 @@ private predicate pathReadStep(
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3399,7 +3643,7 @@ private predicate pathStoreStep(
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3436,7 +3680,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
Stage4::revFlow(result, _, apa, config)
Stage5::revFlow(result, _, apa, config)
}
/**
@@ -3472,7 +3716,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
Stage4::revFlow(p, _, apa, config) and
Stage5::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
@@ -3751,9 +3995,17 @@ predicate stageStats(
n = 45 and
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
or
stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
stage = "5 Fwd" and
n = 50 and
Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
or
stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
stage = "5 Rev" and
n = 55 and
Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
or
stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
or
stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
}
private module FlowExploration {

View File

@@ -621,23 +621,6 @@ private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind)
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -980,6 +963,12 @@ private module Stage1 implements StageSig {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
pragma[nomagic]
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
revFlow(node, config) and
exists(ap)
}
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, _, pragma[only_bind_into](config)) and
@@ -1022,9 +1011,13 @@ private module Stage1 implements StageSig {
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
kind = ret.getKind() and
exists(argAp) and
exists(ap)
}
pragma[nomagic]
@@ -1189,6 +1182,8 @@ private signature module StageSig {
predicate revFlow(NodeEx node, Configuration config);
predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
@@ -1196,7 +1191,9 @@ private signature module StageSig {
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1281,21 +1278,43 @@ private module MkStage<StageSig PrevStage> {
import Param
/* Begin: Stage logic. */
bindingset[result, apa]
private ApApprox unbindApa(ApApprox apa) {
pragma[only_bind_out](apa) = pragma[only_bind_out](result)
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlowAp(node, apa, config)
}
pragma[nomagic]
private predicate flowIntoCallApa(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowOutOfCallApa(
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
ApApprox apa, Configuration config
) {
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
ApApprox argApa, ApApprox apa, Configuration config
) {
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
exists(ReturnKindExt kind |
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call)
)
}
/**
@@ -1307,39 +1326,50 @@ private module MkStage<StageSig PrevStage> {
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, ApApprox apa, Configuration config
) {
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
PrevStage::revFlow(node, state, apa, config) and
filter(node, state, ap, config)
}
pragma[inline]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
Ap ap, ApApprox apa, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
ap = ap0 and
apa = apa0
or
localStep(mid, state0, node, state, false, ap, config, localCc) and
ap0 instanceof ApNil
ap0 instanceof ApNil and
apa = getApprox(ap)
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
@@ -1352,7 +1382,8 @@ private module MkStage<StageSig PrevStage> {
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
@@ -1361,32 +1392,32 @@ private module MkStage<StageSig PrevStage> {
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
ap = apCons(tc, ap0) and
apa = getApprox(ap)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
fwdFlowConsCand(ap0, c, ap, config) and
apa = getApprox(ap)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
or
// flow out of a callable
@@ -1394,8 +1425,8 @@ private module MkStage<StageSig PrevStage> {
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1403,7 +1434,7 @@ private module MkStage<StageSig PrevStage> {
or
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, apa, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
@@ -1413,9 +1444,9 @@ private module MkStage<StageSig PrevStage> {
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
exists(DataFlowType contentType, ApApprox apa1 |
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1433,43 +1464,104 @@ private module MkStage<StageSig PrevStage> {
)
}
private class ApNonNil instanceof Ap {
pragma[nomagic]
ApNonNil() { not this instanceof ApNil }
string toString() { result = "" }
}
pragma[nomagic]
private predicate fwdFlowRead0(
NodeEx node1, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ApNonNil ap, Configuration config
) {
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, _, _, config)
}
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowRetFromArg(
RetNodeEx ret, FlowState state, CcCall ccc, ParameterPosition summaryCtx, ParamNodeEx p,
Ap argAp, ApApprox argApa, Ap ap, ApApprox apa, Configuration config
) {
exists(DataFlowCallable c, ReturnKindExt kind |
fwdFlow(pragma[only_bind_into](ret), state, ccc,
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, apa, config) and
getApprox(argAp) = argApa and
c = ret.getEnclosingCallable() and
kind = ret.getKind() and
p.isParameterOf(c, pragma[only_bind_into](summaryCtx)) and
parameterFlowThroughAllowed(p, kind)
)
}
pragma[inline]
private predicate fwdFlowInMayFlowThrough(
DataFlowCall call, Cc cc, CcCall innerCc, ParameterPositionOption summaryCtx, ApOption argAp,
ParamNodeEx param, Ap ap, ApApprox apa, Configuration config
) {
fwdFlowIn(call, pragma[only_bind_into](param), _, cc, innerCc, summaryCtx, argAp, ap,
pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(param, apa, config)
}
// dedup before joining with `flowThroughOutOfCall`
pragma[nomagic]
private predicate fwdFlowInMayFlowThroughProj(
DataFlowCall call, CcCall innerCc, ApApprox apa, Configuration config
) {
fwdFlowInMayFlowThrough(call, _, innerCc, _, _, _, _, apa, config)
}
/**
* Same as `flowThroughOutOfCall`, but restricted to calls that are reached
* in the flow covered by `fwdFlow`, where data might flow through the target
* callable and back out at `call`.
*/
pragma[nomagic]
private predicate fwdFlowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
ApApprox argApa, ApApprox apa, Configuration config
) {
fwdFlowInMayFlowThroughProj(call, ccc, argApa, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
ApApprox apa, Configuration config
) {
exists(
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc, ApApprox argApa |
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
summaryCtx, _, argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa),
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
fwdFlowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any())
)
}
@@ -1483,8 +1575,7 @@ private module MkStage<StageSig PrevStage> {
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
fwdFlowInMayFlowThrough(call, cc, _, summaryCtx, argAp, param, ap, _, config) and
pos = param.getPosition()
)
}
@@ -1505,41 +1596,55 @@ private module MkStage<StageSig PrevStage> {
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
exists(boolean allowsFieldFlow, ApApprox argApa, ApApprox apa |
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), _, p,
argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa), config) and
kind = ret.getKind() and
fwdFlowThroughOutOfCall(_, ccc, ret, _, allowsFieldFlow, argApa, apa, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any())
)
}
pragma[nomagic]
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
Configuration config
) {
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
exists(ApApprox argApa |
flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
pragma[only_bind_into](config)) and
if allowsFieldFlow = false then argAp instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate flowIntoCallAp(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
Configuration config
) {
exists(ApApprox apa |
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
fwdFlow(arg, _, _, _, _, ap, apa, config)
)
}
pragma[nomagic]
private predicate flowOutOfCallAp(
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Ap ap, Configuration config
) {
exists(ApApprox apa |
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, config) and
fwdFlow(ret, _, _, _, _, ap, apa, config)
)
}
@@ -1628,7 +1733,7 @@ private module MkStage<StageSig PrevStage> {
// flow into a callable
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
@@ -1682,22 +1787,41 @@ private module MkStage<StageSig PrevStage> {
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
flowOutOfCallAp(call, ret, kind, out, allowsFieldFlow, ap, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
/**
* Same as `flowThroughIntoCall`, but restricted to calls that are reached
* in the flow covered by `revFlow`, where data might flow through the target
* callable and back out at `call`.
*/
pragma[nomagic]
private predicate revFlowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
Configuration config
) {
flowThroughIntoCall(call, arg, p, allowsFieldFlow, argAp, config) and
revFlowIsReturned(call, _, _, _, _, config)
}
pragma[nomagic]
private predicate revFlowParamToReturn(
ParamNodeEx p, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, Configuration config
) {
revFlow(p, state, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
revFlowParamToReturn(p, state, kind, returnAp, ap, config) and
revFlowThroughIntoCall(call, arg, p, allowsFieldFlow, ap, config)
)
}
@@ -1750,6 +1874,11 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
pragma[nomagic]
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
revFlow(node, _, _, _, ap, config)
}
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) {
@@ -1786,9 +1915,9 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
ParamNodeEx p, Ap ap, ReturnKindExt kind, Ap returnAp, Configuration config
) {
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
@@ -1796,27 +1925,36 @@ private module MkStage<StageSig PrevStage> {
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
parameterFlowsThroughRev(p, ap, kind, _, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
) {
exists(ParamNodeEx p |
returnFlowsThrough(ret, kind, _, _, p, argAp, ap, config) and
parameterFlowsThroughRev(p, argAp, kind, ap, config)
)
}
pragma[nomagic]
predicate revFlowInToReturnIsReturned(
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
Ap ap, Configuration config
) {
exists(ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
revFlowInToReturnIsReturned(call, arg, state, returnCtx, returnAp, ap, config)
)
}
@@ -2179,16 +2317,16 @@ private module LocalFlowBigStep {
pragma[nomagic]
predicate localFlowBigStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
DataFlowType t, Configuration config, LocalCallContext callContext
) {
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
localFlowExit(node2, state1, config) and
state1 = state2
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
state1 != state2 and
preservesValue = false and
apf = TFrontNil(node2.getDataFlowType()) and
t = node2.getDataFlowType() and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
@@ -2202,11 +2340,87 @@ private import LocalFlowBigStep
private module Stage3Param implements MkStage<Stage2>::StageParam {
private module PrevStage = Stage2;
class Ap = ApproxAccessPathFront;
class ApNil = ApproxAccessPathFrontNil;
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
pragma[noinline]
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
class ApOption = ApproxAccessPathFrontOption;
ApOption apNone() { result = TApproxAccessPathFrontNone() }
ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
import BooleanCallContext
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
expectsContentEx(node, c) and
c = ap.getAHead().getContent()
)
}
pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
(
notExpectsContent(node)
or
expectsContentCand(node, ap, config)
)
}
bindingset[ap, contentType]
predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
}
private module Stage3 implements StageSig {
import MkStage<Stage2>::Stage<Stage3Param>
}
private module Stage4Param implements MkStage<Stage3>::StageParam {
private module PrevStage = Stage3;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
@@ -2226,16 +2440,42 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
import BooleanCallContext
pragma[nomagic]
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2291,8 +2531,8 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
}
}
private module Stage3 implements StageSig {
import MkStage<Stage2>::Stage<Stage3Param>
private module Stage4 implements StageSig {
import MkStage<Stage3>::Stage<Stage4Param>
}
/**
@@ -2303,8 +2543,8 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2315,10 +2555,10 @@ private predicate flowCandSummaryCtx(
*/
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
exists(int tails, int nodes, int apLimit, int tupleLimit |
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2332,11 +2572,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
private newtype TAccessPathApprox =
TNil(DataFlowType t) or
TConsNil(TypedContent tc, DataFlowType t) {
Stage3::consCand(tc, TFrontNil(t), _) and
Stage4::consCand(tc, TFrontNil(t), _) and
not expensiveLen2unfolding(tc, _)
} or
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
Stage3::consCand(tc1, TFrontHead(tc2), _) and
Stage4::consCand(tc1, TFrontHead(tc2), _) and
len in [2 .. accessPathLimit()] and
not expensiveLen2unfolding(tc1, _)
} or
@@ -2466,7 +2706,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
override AccessPathApprox pop(TypedContent head) {
head = tc and
(
exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
result = TConsCons(tc2, _, len - 1)
or
len = 2 and
@@ -2477,7 +2717,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
or
exists(DataFlowType t |
len = 1 and
Stage3::consCand(tc, TFrontNil(t), _) and
Stage4::consCand(tc, TFrontNil(t), _) and
result = TNil(t)
)
)
@@ -2502,13 +2742,14 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
private module Stage4Param implements MkStage<Stage3>::StageParam {
private module PrevStage = Stage3;
private module Stage5Param implements MkStage<Stage4>::StageParam {
private module PrevStage = Stage4;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
pragma[nomagic]
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
ApNil getApNil(NodeEx node) {
@@ -2534,7 +2775,9 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -2571,7 +2814,7 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
private module Stage4 = MkStage<Stage3>::Stage<Stage4Param>;
private module Stage5 = MkStage<Stage4>::Stage<Stage5Param>;
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
@@ -2585,8 +2828,8 @@ private predicate nodeMayUseSummary0(
) {
exists(AccessPathApprox apa0 |
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2596,7 +2839,7 @@ private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
Stage5::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
@@ -2606,8 +2849,8 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage5::revFlow(p, state, _, config)
)
}
@@ -2656,7 +2899,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
len = apa.len() and
result =
strictcount(AccessPathFront apf |
Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
config)
)
)
@@ -2665,7 +2908,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -2686,7 +2929,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
exists(TypedContent head |
apa.pop(head) = result and
Stage4::consCand(head, result, config)
Stage5::consCand(head, result, config)
)
}
@@ -2768,7 +3011,7 @@ private newtype TPathNode =
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) {
// A PathNode is introduced by a source ...
Stage4::revFlow(node, state, config) and
Stage5::revFlow(node, state, config) and
sourceNode(node, state, config) and
(
if hasSourceCallCtx(config)
@@ -2782,7 +3025,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -2924,7 +3167,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
override TypedContent getHead() { result = head1 }
override AccessPath getTail() {
Stage4::consCand(head1, result.getApprox(), _) and
Stage5::consCand(head1, result.getApprox(), _) and
result.getHead() = head2 and
result.length() = len - 1
}
@@ -2955,7 +3198,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
override TypedContent getHead() { result = head }
override AccessPath getTail() {
Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
}
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
@@ -3347,7 +3590,8 @@ private predicate pathStep(
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -3389,7 +3633,7 @@ private predicate pathReadStep(
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3399,7 +3643,7 @@ private predicate pathStoreStep(
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3436,7 +3680,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
Stage4::revFlow(result, _, apa, config)
Stage5::revFlow(result, _, apa, config)
}
/**
@@ -3472,7 +3716,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
Stage4::revFlow(p, _, apa, config) and
Stage5::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
@@ -3751,9 +3995,17 @@ predicate stageStats(
n = 45 and
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
or
stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
stage = "5 Fwd" and
n = 50 and
Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
or
stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
stage = "5 Rev" and
n = 55 and
Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
or
stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
or
stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
}
private module FlowExploration {

View File

@@ -926,18 +926,46 @@ private module Cached {
TReturnCtxNoFlowThrough() or
TReturnCtxMaybeFlowThrough(ReturnKindExt kind)
cached
newtype TTypedContentApprox =
MkTypedContentApprox(ContentApprox c, DataFlowType t) {
exists(Content cont |
c = getContentApprox(cont) and
store(_, cont, _, _, t)
)
}
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
cached
TypedContent getATypedContent(TypedContentApprox c) {
exists(ContentApprox cls, DataFlowType t, Content cont |
c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
result = MkTypedContent(cont, pragma[only_bind_into](t)) and
cls = getContentApprox(cont)
)
}
cached
newtype TAccessPathFront =
TFrontNil(DataFlowType t) or
TFrontHead(TypedContent tc)
cached
newtype TApproxAccessPathFront =
TApproxFrontNil(DataFlowType t) or
TApproxFrontHead(TypedContentApprox tc)
cached
newtype TAccessPathFrontOption =
TAccessPathFrontNone() or
TAccessPathFrontSome(AccessPathFront apf)
cached
newtype TApproxAccessPathFrontOption =
TApproxAccessPathFrontNone() or
TApproxAccessPathFrontSome(ApproxAccessPathFront apf)
}
/**
@@ -1353,6 +1381,75 @@ class ReturnCtx extends TReturnCtx {
}
}
/** An approximated `Content` tagged with the type of a containing object. */
class TypedContentApprox extends MkTypedContentApprox {
private ContentApprox c;
private DataFlowType t;
TypedContentApprox() { this = MkTypedContentApprox(c, t) }
/** Gets a typed content approximated by this value. */
TypedContent getATypedContent() { result = getATypedContent(this) }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this approximated content. */
string toString() { result = c.toString() }
}
/**
* The front of an approximated access path. This is either a head or a nil.
*/
abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract boolean toBoolNonEmpty();
pragma[nomagic]
TypedContent getAHead() {
exists(TypedContentApprox cont |
this = TApproxFrontHead(cont) and
result = cont.getATypedContent()
)
}
}
class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
private DataFlowType t;
ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override boolean toBoolNonEmpty() { result = false }
}
class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
private TypedContentApprox tc;
ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
override string toString() { result = tc.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override boolean toBoolNonEmpty() { result = true }
}
/** An optional approximated access path front. */
class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
string toString() {
this = TApproxAccessPathFrontNone() and result = "<none>"
or
this = TApproxAccessPathFrontSome(any(ApproxAccessPathFront apf | result = apf.toString()))
}
}
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
@@ -1385,7 +1482,7 @@ abstract class AccessPathFront extends TAccessPathFront {
abstract DataFlowType getType();
abstract boolean toBoolNonEmpty();
abstract ApproxAccessPathFront toApprox();
TypedContent getHead() { this = TFrontHead(result) }
}
@@ -1399,7 +1496,7 @@ class AccessPathFrontNil extends AccessPathFront, TFrontNil {
override DataFlowType getType() { result = t }
override boolean toBoolNonEmpty() { result = false }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
}
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
@@ -1411,7 +1508,7 @@ class AccessPathFrontHead extends AccessPathFront, TFrontHead {
override DataFlowType getType() { result = tc.getContainerType() }
override boolean toBoolNonEmpty() { result = true }
override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
}
/** An optional access path front. */

View File

@@ -260,4 +260,9 @@ module Consistency {
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
msg = "Parameter node with multiple positions."
}
query predicate uniqueContentApprox(Content c, string msg) {
not exists(unique(ContentApprox approx | approx = getContentApprox(c))) and
msg = "Non-unique content approximation."
}
}

View File

@@ -621,23 +621,6 @@ private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind)
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -980,6 +963,12 @@ private module Stage1 implements StageSig {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
pragma[nomagic]
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
revFlow(node, config) and
exists(ap)
}
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, _, pragma[only_bind_into](config)) and
@@ -1022,9 +1011,13 @@ private module Stage1 implements StageSig {
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
kind = ret.getKind() and
exists(argAp) and
exists(ap)
}
pragma[nomagic]
@@ -1189,6 +1182,8 @@ private signature module StageSig {
predicate revFlow(NodeEx node, Configuration config);
predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
@@ -1196,7 +1191,9 @@ private signature module StageSig {
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1281,21 +1278,43 @@ private module MkStage<StageSig PrevStage> {
import Param
/* Begin: Stage logic. */
bindingset[result, apa]
private ApApprox unbindApa(ApApprox apa) {
pragma[only_bind_out](apa) = pragma[only_bind_out](result)
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlowAp(node, apa, config)
}
pragma[nomagic]
private predicate flowIntoCallApa(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowOutOfCallApa(
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
ApApprox apa, Configuration config
) {
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
ApApprox argApa, ApApprox apa, Configuration config
) {
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
exists(ReturnKindExt kind |
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call)
)
}
/**
@@ -1307,39 +1326,50 @@ private module MkStage<StageSig PrevStage> {
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, ApApprox apa, Configuration config
) {
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
PrevStage::revFlow(node, state, apa, config) and
filter(node, state, ap, config)
}
pragma[inline]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
Ap ap, ApApprox apa, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
ap = ap0 and
apa = apa0
or
localStep(mid, state0, node, state, false, ap, config, localCc) and
ap0 instanceof ApNil
ap0 instanceof ApNil and
apa = getApprox(ap)
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
@@ -1352,7 +1382,8 @@ private module MkStage<StageSig PrevStage> {
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
@@ -1361,32 +1392,32 @@ private module MkStage<StageSig PrevStage> {
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
ap = apCons(tc, ap0) and
apa = getApprox(ap)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
fwdFlowConsCand(ap0, c, ap, config) and
apa = getApprox(ap)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
or
// flow out of a callable
@@ -1394,8 +1425,8 @@ private module MkStage<StageSig PrevStage> {
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1403,7 +1434,7 @@ private module MkStage<StageSig PrevStage> {
or
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, apa, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
@@ -1413,9 +1444,9 @@ private module MkStage<StageSig PrevStage> {
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
exists(DataFlowType contentType, ApApprox apa1 |
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1433,43 +1464,104 @@ private module MkStage<StageSig PrevStage> {
)
}
private class ApNonNil instanceof Ap {
pragma[nomagic]
ApNonNil() { not this instanceof ApNil }
string toString() { result = "" }
}
pragma[nomagic]
private predicate fwdFlowRead0(
NodeEx node1, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ApNonNil ap, Configuration config
) {
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, _, _, config)
}
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowRetFromArg(
RetNodeEx ret, FlowState state, CcCall ccc, ParameterPosition summaryCtx, ParamNodeEx p,
Ap argAp, ApApprox argApa, Ap ap, ApApprox apa, Configuration config
) {
exists(DataFlowCallable c, ReturnKindExt kind |
fwdFlow(pragma[only_bind_into](ret), state, ccc,
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, apa, config) and
getApprox(argAp) = argApa and
c = ret.getEnclosingCallable() and
kind = ret.getKind() and
p.isParameterOf(c, pragma[only_bind_into](summaryCtx)) and
parameterFlowThroughAllowed(p, kind)
)
}
pragma[inline]
private predicate fwdFlowInMayFlowThrough(
DataFlowCall call, Cc cc, CcCall innerCc, ParameterPositionOption summaryCtx, ApOption argAp,
ParamNodeEx param, Ap ap, ApApprox apa, Configuration config
) {
fwdFlowIn(call, pragma[only_bind_into](param), _, cc, innerCc, summaryCtx, argAp, ap,
pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(param, apa, config)
}
// dedup before joining with `flowThroughOutOfCall`
pragma[nomagic]
private predicate fwdFlowInMayFlowThroughProj(
DataFlowCall call, CcCall innerCc, ApApprox apa, Configuration config
) {
fwdFlowInMayFlowThrough(call, _, innerCc, _, _, _, _, apa, config)
}
/**
* Same as `flowThroughOutOfCall`, but restricted to calls that are reached
* in the flow covered by `fwdFlow`, where data might flow through the target
* callable and back out at `call`.
*/
pragma[nomagic]
private predicate fwdFlowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
ApApprox argApa, ApApprox apa, Configuration config
) {
fwdFlowInMayFlowThroughProj(call, ccc, argApa, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
ApApprox apa, Configuration config
) {
exists(
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc, ApApprox argApa |
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
summaryCtx, _, argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa),
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
fwdFlowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any())
)
}
@@ -1483,8 +1575,7 @@ private module MkStage<StageSig PrevStage> {
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
fwdFlowInMayFlowThrough(call, cc, _, summaryCtx, argAp, param, ap, _, config) and
pos = param.getPosition()
)
}
@@ -1505,41 +1596,55 @@ private module MkStage<StageSig PrevStage> {
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
exists(boolean allowsFieldFlow, ApApprox argApa, ApApprox apa |
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), _, p,
argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa), config) and
kind = ret.getKind() and
fwdFlowThroughOutOfCall(_, ccc, ret, _, allowsFieldFlow, argApa, apa, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any())
)
}
pragma[nomagic]
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
Configuration config
) {
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
exists(ApApprox argApa |
flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
pragma[only_bind_into](config)) and
if allowsFieldFlow = false then argAp instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate flowIntoCallAp(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
Configuration config
) {
exists(ApApprox apa |
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
fwdFlow(arg, _, _, _, _, ap, apa, config)
)
}
pragma[nomagic]
private predicate flowOutOfCallAp(
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Ap ap, Configuration config
) {
exists(ApApprox apa |
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, config) and
fwdFlow(ret, _, _, _, _, ap, apa, config)
)
}
@@ -1628,7 +1733,7 @@ private module MkStage<StageSig PrevStage> {
// flow into a callable
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
@@ -1682,22 +1787,41 @@ private module MkStage<StageSig PrevStage> {
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
flowOutOfCallAp(call, ret, kind, out, allowsFieldFlow, ap, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
/**
* Same as `flowThroughIntoCall`, but restricted to calls that are reached
* in the flow covered by `revFlow`, where data might flow through the target
* callable and back out at `call`.
*/
pragma[nomagic]
private predicate revFlowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
Configuration config
) {
flowThroughIntoCall(call, arg, p, allowsFieldFlow, argAp, config) and
revFlowIsReturned(call, _, _, _, _, config)
}
pragma[nomagic]
private predicate revFlowParamToReturn(
ParamNodeEx p, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, Configuration config
) {
revFlow(p, state, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
revFlowParamToReturn(p, state, kind, returnAp, ap, config) and
revFlowThroughIntoCall(call, arg, p, allowsFieldFlow, ap, config)
)
}
@@ -1750,6 +1874,11 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
pragma[nomagic]
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
revFlow(node, _, _, _, ap, config)
}
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) {
@@ -1786,9 +1915,9 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
ParamNodeEx p, Ap ap, ReturnKindExt kind, Ap returnAp, Configuration config
) {
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
@@ -1796,27 +1925,36 @@ private module MkStage<StageSig PrevStage> {
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
parameterFlowsThroughRev(p, ap, kind, _, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
) {
exists(ParamNodeEx p |
returnFlowsThrough(ret, kind, _, _, p, argAp, ap, config) and
parameterFlowsThroughRev(p, argAp, kind, ap, config)
)
}
pragma[nomagic]
predicate revFlowInToReturnIsReturned(
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
Ap ap, Configuration config
) {
exists(ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
revFlowInToReturnIsReturned(call, arg, state, returnCtx, returnAp, ap, config)
)
}
@@ -2179,16 +2317,16 @@ private module LocalFlowBigStep {
pragma[nomagic]
predicate localFlowBigStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
DataFlowType t, Configuration config, LocalCallContext callContext
) {
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
localFlowExit(node2, state1, config) and
state1 = state2
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
state1 != state2 and
preservesValue = false and
apf = TFrontNil(node2.getDataFlowType()) and
t = node2.getDataFlowType() and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
@@ -2202,11 +2340,87 @@ private import LocalFlowBigStep
private module Stage3Param implements MkStage<Stage2>::StageParam {
private module PrevStage = Stage2;
class Ap = ApproxAccessPathFront;
class ApNil = ApproxAccessPathFrontNil;
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
pragma[noinline]
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
class ApOption = ApproxAccessPathFrontOption;
ApOption apNone() { result = TApproxAccessPathFrontNone() }
ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
import BooleanCallContext
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
expectsContentEx(node, c) and
c = ap.getAHead().getContent()
)
}
pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
(
notExpectsContent(node)
or
expectsContentCand(node, ap, config)
)
}
bindingset[ap, contentType]
predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
}
private module Stage3 implements StageSig {
import MkStage<Stage2>::Stage<Stage3Param>
}
private module Stage4Param implements MkStage<Stage3>::StageParam {
private module PrevStage = Stage3;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
@@ -2226,16 +2440,42 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
import BooleanCallContext
pragma[nomagic]
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2291,8 +2531,8 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
}
}
private module Stage3 implements StageSig {
import MkStage<Stage2>::Stage<Stage3Param>
private module Stage4 implements StageSig {
import MkStage<Stage3>::Stage<Stage4Param>
}
/**
@@ -2303,8 +2543,8 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2315,10 +2555,10 @@ private predicate flowCandSummaryCtx(
*/
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
exists(int tails, int nodes, int apLimit, int tupleLimit |
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2332,11 +2572,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
private newtype TAccessPathApprox =
TNil(DataFlowType t) or
TConsNil(TypedContent tc, DataFlowType t) {
Stage3::consCand(tc, TFrontNil(t), _) and
Stage4::consCand(tc, TFrontNil(t), _) and
not expensiveLen2unfolding(tc, _)
} or
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
Stage3::consCand(tc1, TFrontHead(tc2), _) and
Stage4::consCand(tc1, TFrontHead(tc2), _) and
len in [2 .. accessPathLimit()] and
not expensiveLen2unfolding(tc1, _)
} or
@@ -2466,7 +2706,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
override AccessPathApprox pop(TypedContent head) {
head = tc and
(
exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
result = TConsCons(tc2, _, len - 1)
or
len = 2 and
@@ -2477,7 +2717,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
or
exists(DataFlowType t |
len = 1 and
Stage3::consCand(tc, TFrontNil(t), _) and
Stage4::consCand(tc, TFrontNil(t), _) and
result = TNil(t)
)
)
@@ -2502,13 +2742,14 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
private module Stage4Param implements MkStage<Stage3>::StageParam {
private module PrevStage = Stage3;
private module Stage5Param implements MkStage<Stage4>::StageParam {
private module PrevStage = Stage4;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
pragma[nomagic]
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
ApNil getApNil(NodeEx node) {
@@ -2534,7 +2775,9 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -2571,7 +2814,7 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
private module Stage4 = MkStage<Stage3>::Stage<Stage4Param>;
private module Stage5 = MkStage<Stage4>::Stage<Stage5Param>;
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
@@ -2585,8 +2828,8 @@ private predicate nodeMayUseSummary0(
) {
exists(AccessPathApprox apa0 |
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2596,7 +2839,7 @@ private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
Stage5::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
@@ -2606,8 +2849,8 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage5::revFlow(p, state, _, config)
)
}
@@ -2656,7 +2899,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
len = apa.len() and
result =
strictcount(AccessPathFront apf |
Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
config)
)
)
@@ -2665,7 +2908,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -2686,7 +2929,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
exists(TypedContent head |
apa.pop(head) = result and
Stage4::consCand(head, result, config)
Stage5::consCand(head, result, config)
)
}
@@ -2768,7 +3011,7 @@ private newtype TPathNode =
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) {
// A PathNode is introduced by a source ...
Stage4::revFlow(node, state, config) and
Stage5::revFlow(node, state, config) and
sourceNode(node, state, config) and
(
if hasSourceCallCtx(config)
@@ -2782,7 +3025,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -2924,7 +3167,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
override TypedContent getHead() { result = head1 }
override AccessPath getTail() {
Stage4::consCand(head1, result.getApprox(), _) and
Stage5::consCand(head1, result.getApprox(), _) and
result.getHead() = head2 and
result.length() = len - 1
}
@@ -2955,7 +3198,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
override TypedContent getHead() { result = head }
override AccessPath getTail() {
Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
}
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
@@ -3347,7 +3590,8 @@ private predicate pathStep(
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -3389,7 +3633,7 @@ private predicate pathReadStep(
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3399,7 +3643,7 @@ private predicate pathStoreStep(
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3436,7 +3680,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
Stage4::revFlow(result, _, apa, config)
Stage5::revFlow(result, _, apa, config)
}
/**
@@ -3472,7 +3716,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
Stage4::revFlow(p, _, apa, config) and
Stage5::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
@@ -3751,9 +3995,17 @@ predicate stageStats(
n = 45 and
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
or
stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
stage = "5 Fwd" and
n = 50 and
Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
or
stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
stage = "5 Rev" and
n = 55 and
Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
or
stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
or
stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
}
private module FlowExploration {

View File

@@ -621,23 +621,6 @@ private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind)
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -980,6 +963,12 @@ private module Stage1 implements StageSig {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
pragma[nomagic]
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
revFlow(node, config) and
exists(ap)
}
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, _, pragma[only_bind_into](config)) and
@@ -1022,9 +1011,13 @@ private module Stage1 implements StageSig {
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
kind = ret.getKind() and
exists(argAp) and
exists(ap)
}
pragma[nomagic]
@@ -1189,6 +1182,8 @@ private signature module StageSig {
predicate revFlow(NodeEx node, Configuration config);
predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
@@ -1196,7 +1191,9 @@ private signature module StageSig {
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1281,21 +1278,43 @@ private module MkStage<StageSig PrevStage> {
import Param
/* Begin: Stage logic. */
bindingset[result, apa]
private ApApprox unbindApa(ApApprox apa) {
pragma[only_bind_out](apa) = pragma[only_bind_out](result)
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlowAp(node, apa, config)
}
pragma[nomagic]
private predicate flowIntoCallApa(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowOutOfCallApa(
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
ApApprox apa, Configuration config
) {
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
ApApprox argApa, ApApprox apa, Configuration config
) {
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
exists(ReturnKindExt kind |
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call)
)
}
/**
@@ -1307,39 +1326,50 @@ private module MkStage<StageSig PrevStage> {
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, ApApprox apa, Configuration config
) {
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
PrevStage::revFlow(node, state, apa, config) and
filter(node, state, ap, config)
}
pragma[inline]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
Ap ap, ApApprox apa, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
ap = ap0 and
apa = apa0
or
localStep(mid, state0, node, state, false, ap, config, localCc) and
ap0 instanceof ApNil
ap0 instanceof ApNil and
apa = getApprox(ap)
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
@@ -1352,7 +1382,8 @@ private module MkStage<StageSig PrevStage> {
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
@@ -1361,32 +1392,32 @@ private module MkStage<StageSig PrevStage> {
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
ap = getApNil(node) and
apa = getApprox(ap)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
ap = apCons(tc, ap0) and
apa = getApprox(ap)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
fwdFlowConsCand(ap0, c, ap, config) and
apa = getApprox(ap)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
or
// flow out of a callable
@@ -1394,8 +1425,8 @@ private module MkStage<StageSig PrevStage> {
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1403,7 +1434,7 @@ private module MkStage<StageSig PrevStage> {
or
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, apa, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
@@ -1413,9 +1444,9 @@ private module MkStage<StageSig PrevStage> {
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
exists(DataFlowType contentType, ApApprox apa1 |
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1433,43 +1464,104 @@ private module MkStage<StageSig PrevStage> {
)
}
private class ApNonNil instanceof Ap {
pragma[nomagic]
ApNonNil() { not this instanceof ApNil }
string toString() { result = "" }
}
pragma[nomagic]
private predicate fwdFlowRead0(
NodeEx node1, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ApNonNil ap, Configuration config
) {
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, _, _, config)
}
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowRetFromArg(
RetNodeEx ret, FlowState state, CcCall ccc, ParameterPosition summaryCtx, ParamNodeEx p,
Ap argAp, ApApprox argApa, Ap ap, ApApprox apa, Configuration config
) {
exists(DataFlowCallable c, ReturnKindExt kind |
fwdFlow(pragma[only_bind_into](ret), state, ccc,
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, apa, config) and
getApprox(argAp) = argApa and
c = ret.getEnclosingCallable() and
kind = ret.getKind() and
p.isParameterOf(c, pragma[only_bind_into](summaryCtx)) and
parameterFlowThroughAllowed(p, kind)
)
}
pragma[inline]
private predicate fwdFlowInMayFlowThrough(
DataFlowCall call, Cc cc, CcCall innerCc, ParameterPositionOption summaryCtx, ApOption argAp,
ParamNodeEx param, Ap ap, ApApprox apa, Configuration config
) {
fwdFlowIn(call, pragma[only_bind_into](param), _, cc, innerCc, summaryCtx, argAp, ap,
pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(param, apa, config)
}
// dedup before joining with `flowThroughOutOfCall`
pragma[nomagic]
private predicate fwdFlowInMayFlowThroughProj(
DataFlowCall call, CcCall innerCc, ApApprox apa, Configuration config
) {
fwdFlowInMayFlowThrough(call, _, innerCc, _, _, _, _, apa, config)
}
/**
* Same as `flowThroughOutOfCall`, but restricted to calls that are reached
* in the flow covered by `fwdFlow`, where data might flow through the target
* callable and back out at `call`.
*/
pragma[nomagic]
private predicate fwdFlowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
ApApprox argApa, ApApprox apa, Configuration config
) {
fwdFlowInMayFlowThroughProj(call, ccc, argApa, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
ApApprox apa, Configuration config
) {
exists(
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc, ApApprox argApa |
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
summaryCtx, _, argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa),
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
fwdFlowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, argApa, apa, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any())
)
}
@@ -1483,8 +1575,7 @@ private module MkStage<StageSig PrevStage> {
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
fwdFlowInMayFlowThrough(call, cc, _, summaryCtx, argAp, param, ap, _, config) and
pos = param.getPosition()
)
}
@@ -1505,41 +1596,55 @@ private module MkStage<StageSig PrevStage> {
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
exists(boolean allowsFieldFlow, ApApprox argApa, ApApprox apa |
fwdFlowRetFromArg(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc), _, p,
argAp, pragma[only_bind_into](argApa), ap, pragma[only_bind_into](apa), config) and
kind = ret.getKind() and
fwdFlowThroughOutOfCall(_, ccc, ret, _, allowsFieldFlow, argApa, apa, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any())
)
}
pragma[nomagic]
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
Configuration config
) {
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
exists(ApApprox argApa |
flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
pragma[only_bind_into](config)) and
if allowsFieldFlow = false then argAp instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate flowIntoCallAp(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
Configuration config
) {
exists(ApApprox apa |
flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
fwdFlow(arg, _, _, _, _, ap, apa, config)
)
}
pragma[nomagic]
private predicate flowOutOfCallAp(
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Ap ap, Configuration config
) {
exists(ApApprox apa |
flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, config) and
fwdFlow(ret, _, _, _, _, ap, apa, config)
)
}
@@ -1628,7 +1733,7 @@ private module MkStage<StageSig PrevStage> {
// flow into a callable
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
@@ -1682,22 +1787,41 @@ private module MkStage<StageSig PrevStage> {
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
flowOutOfCallAp(call, ret, kind, out, allowsFieldFlow, ap, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
/**
* Same as `flowThroughIntoCall`, but restricted to calls that are reached
* in the flow covered by `revFlow`, where data might flow through the target
* callable and back out at `call`.
*/
pragma[nomagic]
private predicate revFlowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp,
Configuration config
) {
flowThroughIntoCall(call, arg, p, allowsFieldFlow, argAp, config) and
revFlowIsReturned(call, _, _, _, _, config)
}
pragma[nomagic]
private predicate revFlowParamToReturn(
ParamNodeEx p, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap, Configuration config
) {
revFlow(p, state, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
revFlowParamToReturn(p, state, kind, returnAp, ap, config) and
revFlowThroughIntoCall(call, arg, p, allowsFieldFlow, ap, config)
)
}
@@ -1750,6 +1874,11 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
pragma[nomagic]
predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
revFlow(node, _, _, _, ap, config)
}
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) {
@@ -1786,9 +1915,9 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
ParamNodeEx p, Ap ap, ReturnKindExt kind, Ap returnAp, Configuration config
) {
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(returnAp), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
@@ -1796,27 +1925,36 @@ private module MkStage<StageSig PrevStage> {
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
parameterFlowsThroughRev(p, ap, kind, _, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
predicate returnMayFlowThrough(
RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
) {
exists(ParamNodeEx p |
returnFlowsThrough(ret, kind, _, _, p, argAp, ap, config) and
parameterFlowsThroughRev(p, argAp, kind, ap, config)
)
}
pragma[nomagic]
predicate revFlowInToReturnIsReturned(
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
Ap ap, Configuration config
) {
exists(ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
revFlowInToReturnIsReturned(call, arg, state, returnCtx, returnAp, ap, config)
)
}
@@ -2179,16 +2317,16 @@ private module LocalFlowBigStep {
pragma[nomagic]
predicate localFlowBigStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
DataFlowType t, Configuration config, LocalCallContext callContext
) {
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
localFlowExit(node2, state1, config) and
state1 = state2
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
state1 != state2 and
preservesValue = false and
apf = TFrontNil(node2.getDataFlowType()) and
t = node2.getDataFlowType() and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
@@ -2202,11 +2340,87 @@ private import LocalFlowBigStep
private module Stage3Param implements MkStage<Stage2>::StageParam {
private module PrevStage = Stage2;
class Ap = ApproxAccessPathFront;
class ApNil = ApproxAccessPathFrontNil;
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
pragma[noinline]
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
class ApOption = ApproxAccessPathFrontOption;
ApOption apNone() { result = TApproxAccessPathFrontNone() }
ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
import BooleanCallContext
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
expectsContentEx(node, c) and
c = ap.getAHead().getContent()
)
}
pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
(
notExpectsContent(node)
or
expectsContentCand(node, ap, config)
)
}
bindingset[ap, contentType]
predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
}
private module Stage3 implements StageSig {
import MkStage<Stage2>::Stage<Stage3Param>
}
private module Stage4Param implements MkStage<Stage3>::StageParam {
private module PrevStage = Stage3;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
@@ -2226,16 +2440,42 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
import BooleanCallContext
pragma[nomagic]
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2291,8 +2531,8 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
}
}
private module Stage3 implements StageSig {
import MkStage<Stage2>::Stage<Stage3Param>
private module Stage4 implements StageSig {
import MkStage<Stage3>::Stage<Stage4Param>
}
/**
@@ -2303,8 +2543,8 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2315,10 +2555,10 @@ private predicate flowCandSummaryCtx(
*/
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
exists(int tails, int nodes, int apLimit, int tupleLimit |
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2332,11 +2572,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
private newtype TAccessPathApprox =
TNil(DataFlowType t) or
TConsNil(TypedContent tc, DataFlowType t) {
Stage3::consCand(tc, TFrontNil(t), _) and
Stage4::consCand(tc, TFrontNil(t), _) and
not expensiveLen2unfolding(tc, _)
} or
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
Stage3::consCand(tc1, TFrontHead(tc2), _) and
Stage4::consCand(tc1, TFrontHead(tc2), _) and
len in [2 .. accessPathLimit()] and
not expensiveLen2unfolding(tc1, _)
} or
@@ -2466,7 +2706,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
override AccessPathApprox pop(TypedContent head) {
head = tc and
(
exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
result = TConsCons(tc2, _, len - 1)
or
len = 2 and
@@ -2477,7 +2717,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
or
exists(DataFlowType t |
len = 1 and
Stage3::consCand(tc, TFrontNil(t), _) and
Stage4::consCand(tc, TFrontNil(t), _) and
result = TNil(t)
)
)
@@ -2502,13 +2742,14 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
private module Stage4Param implements MkStage<Stage3>::StageParam {
private module PrevStage = Stage3;
private module Stage5Param implements MkStage<Stage4>::StageParam {
private module PrevStage = Stage4;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
pragma[nomagic]
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
ApNil getApNil(NodeEx node) {
@@ -2534,7 +2775,9 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -2571,7 +2814,7 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
private module Stage4 = MkStage<Stage3>::Stage<Stage4Param>;
private module Stage5 = MkStage<Stage4>::Stage<Stage5Param>;
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
@@ -2585,8 +2828,8 @@ private predicate nodeMayUseSummary0(
) {
exists(AccessPathApprox apa0 |
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2596,7 +2839,7 @@ private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
Stage5::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
@@ -2606,8 +2849,8 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage5::revFlow(p, state, _, config)
)
}
@@ -2656,7 +2899,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
len = apa.len() and
result =
strictcount(AccessPathFront apf |
Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
config)
)
)
@@ -2665,7 +2908,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -2686,7 +2929,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
exists(TypedContent head |
apa.pop(head) = result and
Stage4::consCand(head, result, config)
Stage5::consCand(head, result, config)
)
}
@@ -2768,7 +3011,7 @@ private newtype TPathNode =
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) {
// A PathNode is introduced by a source ...
Stage4::revFlow(node, state, config) and
Stage5::revFlow(node, state, config) and
sourceNode(node, state, config) and
(
if hasSourceCallCtx(config)
@@ -2782,7 +3025,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -2924,7 +3167,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
override TypedContent getHead() { result = head1 }
override AccessPath getTail() {
Stage4::consCand(head1, result.getApprox(), _) and
Stage5::consCand(head1, result.getApprox(), _) and
result.getHead() = head2 and
result.length() = len - 1
}
@@ -2955,7 +3198,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
override TypedContent getHead() { result = head }
override AccessPath getTail() {
Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
}
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
@@ -3347,7 +3590,8 @@ private predicate pathStep(
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -3389,7 +3633,7 @@ private predicate pathReadStep(
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3399,7 +3643,7 @@ private predicate pathStoreStep(
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3436,7 +3680,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
Stage4::revFlow(result, _, apa, config)
Stage5::revFlow(result, _, apa, config)
}
/**
@@ -3472,7 +3716,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
Stage4::revFlow(p, _, apa, config) and
Stage5::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
@@ -3751,9 +3995,17 @@ predicate stageStats(
n = 45 and
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
or
stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
stage = "5 Fwd" and
n = 50 and
Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
or
stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
stage = "5 Rev" and
n = 55 and
Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
or
stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
or
stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
}
private module FlowExploration {

View File

@@ -1,49 +1,77 @@
private import java
private import semmle.code.java.dataflow.InstanceAccess
private import semmle.code.java.dataflow.ExternalFlow
private import semmle.code.java.dataflow.FlowSummary
private import semmle.code.java.dataflow.TypeFlow
private import DataFlowPrivate
private import DataFlowUtil
private import FlowSummaryImpl as FlowSummaryImpl
private import DataFlowImplCommon as DataFlowImplCommon
/** Gets a string for approximating the name of a field. */
string approximateFieldContent(FieldContent fc) { result = fc.getField().getName().prefix(1) }
cached
newtype TNode =
TExprNode(Expr e) {
DataFlowImplCommon::forceCachingInSameStage() and
not e.getType() instanceof VoidType and
not e.getParent*() instanceof Annotation
} or
TExplicitParameterNode(Parameter p) { exists(p.getCallable().getBody()) } or
TImplicitVarargsArray(Call c) {
c.getCallee().isVarargs() and
not exists(Argument arg | arg.getCall() = c and arg.isExplicitVarargsArray())
} or
TInstanceParameterNode(Callable c) { exists(c.getBody()) and not c.isStatic() } or
TImplicitInstanceAccess(InstanceAccessExt ia) { not ia.isExplicit(_) } or
TMallocNode(ClassInstanceExpr cie) or
TExplicitExprPostUpdate(Expr e) {
explicitInstanceArgument(_, e)
or
e instanceof Argument and not e.getType() instanceof ImmutableType
or
exists(FieldAccess fa | fa.getField() instanceof InstanceField and e = fa.getQualifier())
or
exists(ArrayAccess aa | e = aa.getArray())
} or
TImplicitExprPostUpdate(InstanceAccessExt ia) {
implicitInstanceArgument(_, ia)
or
exists(FieldAccess fa |
fa.getField() instanceof InstanceField and ia.isImplicitFieldQualifier(fa)
)
} or
TSummaryInternalNode(SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) {
FlowSummaryImpl::Private::summaryNodeRange(c, state)
} or
TSummaryParameterNode(SummarizedCallable c, int pos) {
FlowSummaryImpl::Private::summaryParameterNodeRange(c, pos)
} or
TFieldValueNode(Field f)
private module Cached {
cached
newtype TNode =
TExprNode(Expr e) {
DataFlowImplCommon::forceCachingInSameStage() and
not e.getType() instanceof VoidType and
not e.getParent*() instanceof Annotation
} or
TExplicitParameterNode(Parameter p) { exists(p.getCallable().getBody()) } or
TImplicitVarargsArray(Call c) {
c.getCallee().isVarargs() and
not exists(Argument arg | arg.getCall() = c and arg.isExplicitVarargsArray())
} or
TInstanceParameterNode(Callable c) { exists(c.getBody()) and not c.isStatic() } or
TImplicitInstanceAccess(InstanceAccessExt ia) { not ia.isExplicit(_) } or
TMallocNode(ClassInstanceExpr cie) or
TExplicitExprPostUpdate(Expr e) {
explicitInstanceArgument(_, e)
or
e instanceof Argument and not e.getType() instanceof ImmutableType
or
exists(FieldAccess fa | fa.getField() instanceof InstanceField and e = fa.getQualifier())
or
exists(ArrayAccess aa | e = aa.getArray())
} or
TImplicitExprPostUpdate(InstanceAccessExt ia) {
implicitInstanceArgument(_, ia)
or
exists(FieldAccess fa |
fa.getField() instanceof InstanceField and ia.isImplicitFieldQualifier(fa)
)
} or
TSummaryInternalNode(SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) {
FlowSummaryImpl::Private::summaryNodeRange(c, state)
} or
TSummaryParameterNode(SummarizedCallable c, int pos) {
FlowSummaryImpl::Private::summaryParameterNodeRange(c, pos)
} or
TFieldValueNode(Field f)
cached
newtype TContent =
TFieldContent(InstanceField f) or
TArrayContent() or
TCollectionContent() or
TMapKeyContent() or
TMapValueContent() or
TSyntheticFieldContent(SyntheticField s)
cached
newtype TContentApprox =
TFieldContentApprox(string firstChar) { firstChar = approximateFieldContent(_) } or
TArrayContentApprox() or
TCollectionContentApprox() or
TMapKeyContentApprox() or
TMapValueContentApprox() or
TSyntheticFieldApproxContent()
}
import Cached
private predicate explicitInstanceArgument(Call call, Expr instarg) {
call instanceof MethodAccess and

View File

@@ -9,6 +9,7 @@ private import semmle.code.java.dataflow.FlowSteps
private import semmle.code.java.dataflow.FlowSummary
private import FlowSummaryImpl as FlowSummaryImpl
private import DataFlowImplConsistency
private import DataFlowNodes
import DataFlowNodes::Private
private newtype TReturnKind = TNormalReturnKind()
@@ -420,6 +421,48 @@ predicate allowParameterReturnInSelf(ParameterNode p) {
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(p)
}
/** An approximated `Content`. */
class ContentApprox extends TContentApprox {
/** Gets a textual representation of this approximated `Content`. */
string toString() {
exists(string firstChar |
this = TFieldContentApprox(firstChar) and
result = "approximated field " + firstChar
)
or
this = TArrayContentApprox() and
result = "[]"
or
this = TCollectionContentApprox() and
result = "<element>"
or
this = TMapKeyContentApprox() and
result = "<map.key>"
or
this = TMapValueContentApprox() and
result = "<map.value>"
or
this = TSyntheticFieldApproxContent() and
result = "approximated synthetic field"
}
}
/** Gets an approximated value for content `c`. */
pragma[nomagic]
ContentApprox getContentApprox(Content c) {
result = TFieldContentApprox(approximateFieldContent(c))
or
c instanceof ArrayContent and result = TArrayContentApprox()
or
c instanceof CollectionContent and result = TCollectionContentApprox()
or
c instanceof MapKeyContent and result = TMapKeyContentApprox()
or
c instanceof MapValueContent and result = TMapValueContentApprox()
or
c instanceof SyntheticFieldContent and result = TSyntheticFieldApproxContent()
}
private class MyConsistencyConfiguration extends Consistency::ConsistencyConfiguration {
override predicate argHasPostUpdateExclude(ArgumentNode n) {
n.getType() instanceof ImmutableType or n instanceof ImplicitVarargsArray

View File

@@ -12,6 +12,7 @@ private import semmle.code.java.dataflow.FlowSummary
private import semmle.code.java.dataflow.InstanceAccess
private import FlowSummaryImpl as FlowSummaryImpl
private import TaintTrackingUtil as TaintTrackingUtil
private import DataFlowNodes
import DataFlowNodes::Public
import semmle.code.Unit
@@ -186,14 +187,6 @@ private predicate simpleLocalFlowStep0(Node node1, Node node2) {
FlowSummaryImpl::Private::Steps::summaryLocalStep(node1, node2, true)
}
private newtype TContent =
TFieldContent(InstanceField f) or
TArrayContent() or
TCollectionContent() or
TMapKeyContent() or
TMapValueContent() or
TSyntheticFieldContent(SyntheticField s)
/**
* A description of the way data may be stored inside an object. Examples
* include instance fields, the contents of a collection object, or the contents

View File

@@ -9,11 +9,12 @@
import java
private import VirtualDispatch
private import semmle.code.java.dataflow.internal.BaseSSA
private import semmle.code.java.dataflow.internal.DataFlowUtil
private import semmle.code.java.dataflow.internal.DataFlowPrivate
private import semmle.code.java.dataflow.internal.DataFlowUtil as DataFlow
private import semmle.code.java.dataflow.internal.DataFlowPrivate as DataFlowPrivate
private import semmle.code.java.dataflow.InstanceAccess
private import semmle.code.java.Collections
private import semmle.code.java.Maps
private import codeql.typetracking.TypeTracking
/**
* Gets a viable dispatch target for `ma`. This is the input dispatch relation.
@@ -23,7 +24,8 @@ private Method viableImpl_inp(MethodAccess ma) { result = viableImpl_v2(ma) }
private Callable dispatchCand(Call c) {
c instanceof ConstructorCall and result = c.getCallee().getSourceDeclaration()
or
result = viableImpl_inp(c)
result = viableImpl_inp(c) and
not dispatchOrigin(_, c, result)
}
/**
@@ -56,7 +58,7 @@ private predicate publicStaticFieldInit(ClassInstanceExpr cie) {
*/
private predicate publicThroughField(RefType t) {
exists(ClassInstanceExpr cie |
cie.getConstructedType() = t and
cie.getConstructedType().getSourceDeclaration() = t and
publicStaticFieldInit(cie)
)
}
@@ -64,7 +66,7 @@ private predicate publicThroughField(RefType t) {
/**
* Holds if `t` and its subtypes are private or anonymous.
*/
private predicate privateConstruction(RefType t) {
private predicate privateConstruction(SrcRefType t) {
(t.isPrivate() or t instanceof AnonymousClass) and
not publicThroughField(t) and
forall(SrcRefType sub | sub.getASourceSupertype+() = t.getSourceDeclaration() |
@@ -122,188 +124,282 @@ private predicate relevant(RefType t) {
}
/** A node with a type that is relevant for dispatch flow. */
private class RelevantNode extends Node {
private class RelevantNode extends DataFlow::Node {
RelevantNode() { relevant(this.getType()) }
}
/**
* Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
* The instance parameter is considered to have index `-1`.
*/
pragma[nomagic]
private predicate viableParamCand(Call call, int i, ParameterNode p) {
exists(DataFlowCallable callable |
callable.asCallable() = dispatchCand(call) and
p.isParameterOf(callable, i) and
p instanceof RelevantNode
)
}
private module TypeTrackingSteps {
class Node = RelevantNode;
/**
* Holds if `arg` is a possible argument to `p` taking virtual dispatch into account.
*/
private predicate viableArgParamCand(ArgumentNode arg, ParameterNode p) {
exists(int i, DataFlowCall call |
viableParamCand(call.asCall(), i, p) and
arg.argumentOf(call, i)
)
}
class LocalSourceNode extends RelevantNode {
LocalSourceNode() {
this.asExpr() instanceof Call or
this.asExpr() instanceof RValue or
this instanceof DataFlow::ParameterNode or
this instanceof DataFlow::ImplicitVarargsArray or
this.asExpr() instanceof ArrayInit or
this.asExpr() instanceof ArrayAccess or
this instanceof DataFlow::FieldValueNode
}
}
/**
* Holds if data may flow from `n1` to `n2` in a single step through a call or a return.
*/
private predicate callFlowStepCand(RelevantNode n1, RelevantNode n2) {
exists(ReturnStmt ret, Method m |
ret.getEnclosingCallable() = m and
ret.getResult() = n1.asExpr() and
m = dispatchCand(n2.asExpr())
)
or
viableArgParamCand(n1, n2)
}
private newtype TContent =
ContentArray() or
ContentArrayArray()
/**
* Holds if data may flow from `n1` to `n2` in a single step that does not go
* through a call or a return.
*/
private predicate flowStep(RelevantNode n1, RelevantNode n2) {
exists(BaseSsaVariable v, BaseSsaVariable def |
def.(BaseSsaUpdate).getDefiningExpr().(VariableAssign).getSource() = n1.asExpr()
or
def.(BaseSsaImplicitInit).isParameterDefinition(n1.asParameter())
or
exists(EnhancedForStmt for |
for.getVariable() = def.(BaseSsaUpdate).getDefiningExpr() and
for.getExpr() = n1.asExpr() and
n1.getType() instanceof Array
)
|
v.getAnUltimateDefinition() = def and
v.getAUse() = n2.asExpr()
)
or
exists(Callable c | n1.(InstanceParameterNode).getCallable() = c |
exists(InstanceAccess ia |
ia = n2.asExpr() and ia.getEnclosingCallable() = c and ia.isOwnInstanceAccess()
class Content extends TContent {
string toString() {
this = ContentArray() and result = "array"
or
this = ContentArrayArray() and result = "array array"
}
}
class ContentFilter extends Content {
Content getAMatchingContent() { result = this }
}
predicate compatibleContents(Content storeContents, Content loadContents) {
storeContents = loadContents
}
predicate simpleLocalSmallStep(Node n1, Node n2) {
exists(BaseSsaVariable v, BaseSsaVariable def |
def.(BaseSsaUpdate).getDefiningExpr().(VariableAssign).getSource() = n1.asExpr()
or
def.(BaseSsaImplicitInit).isParameterDefinition(n1.asParameter())
|
v.getAnUltimateDefinition() = def and
v.getAUse() = n2.asExpr()
)
or
n2.(ImplicitInstanceAccess).getInstanceAccess().(OwnInstanceAccess).getEnclosingCallable() = c
)
or
n2.(FieldValueNode).getField().getAnAssignedValue() = n1.asExpr()
or
n2.asExpr().(FieldRead).getField() = n1.(FieldValueNode).getField()
or
exists(EnumType enum, Method getValue |
enum.getAnEnumConstant().getAnAssignedValue() = n1.asExpr() and
getValue.getDeclaringType() = enum and
(getValue.hasName("values") or getValue.hasName("valueOf")) and
n2.asExpr().(MethodAccess).getMethod() = getValue
)
or
n2.asExpr().(CastingExpr).getExpr() = n1.asExpr()
or
n2.asExpr().(ChooseExpr).getAResultExpr() = n1.asExpr()
or
n2.asExpr().(AssignExpr).getSource() = n1.asExpr()
or
n2.asExpr().(ArrayInit).getAnInit() = n1.asExpr()
or
n2.asExpr().(ArrayCreationExpr).getInit() = n1.asExpr()
or
n2.asExpr().(ArrayAccess).getArray() = n1.asExpr()
or
exists(Argument arg |
n1.asExpr() = arg and arg.isVararg() and n2.(ImplicitVarargsArray).getCall() = arg.getCall()
)
or
exists(AssignExpr a, Variable v |
a.getSource() = n1.asExpr() and
a.getDest().(ArrayAccess).getArray() = v.getAnAccess() and
n2.asExpr() = v.getAnAccess().(RValue)
)
or
exists(Variable v, MethodAccess put, MethodAccess get |
put.getArgument(1) = n1.asExpr() and
put.getMethod().(MapMethod).hasName("put") and
put.getQualifier() = v.getAnAccess() and
get.getQualifier() = v.getAnAccess() and
get.getMethod().(MapMethod).hasName("get") and
n2.asExpr() = get
)
or
exists(Variable v, MethodAccess add |
add.getAnArgument() = n1.asExpr() and
add.getMethod().(CollectionMethod).hasName("add") and
add.getQualifier() = v.getAnAccess()
|
exists(MethodAccess get |
exists(Callable c | n1.(DataFlow::InstanceParameterNode).getCallable() = c |
exists(InstanceAccess ia |
ia = n2.asExpr() and ia.getEnclosingCallable() = c and ia.isOwnInstanceAccess()
)
or
n2.(DataFlow::ImplicitInstanceAccess)
.getInstanceAccess()
.(OwnInstanceAccess)
.getEnclosingCallable() = c
)
or
n2.asExpr().(CastingExpr).getExpr() = n1.asExpr()
or
n2.asExpr().(ChooseExpr).getAResultExpr() = n1.asExpr()
or
n2.asExpr().(AssignExpr).getSource() = n1.asExpr()
or
n2.asExpr().(ArrayCreationExpr).getInit() = n1.asExpr()
}
predicate levelStepNoCall(Node n1, LocalSourceNode n2) {
exists(EnumType enum, Method getValue |
enum.getAnEnumConstant().getAnAssignedValue() = n1.asExpr() and
getValue.getDeclaringType() = enum and
getValue.hasName("valueOf") and
n2.asExpr().(MethodAccess).getMethod() = getValue
)
or
exists(Variable v, MethodAccess put, MethodAccess get |
put.getArgument(1) = n1.asExpr() and
put.getMethod().(MapMethod).hasName("put") and
put.getQualifier() = v.getAnAccess() and
get.getQualifier() = v.getAnAccess() and
get.getMethod().(CollectionMethod).hasName("get") and
get.getMethod().(MapMethod).hasName("get") and
n2.asExpr() = get
)
or
exists(EnhancedForStmt for, BaseSsaVariable ssa, BaseSsaVariable def |
for.getVariable() = def.(BaseSsaUpdate).getDefiningExpr() and
for.getExpr() = v.getAnAccess() and
ssa.getAnUltimateDefinition() = def and
ssa.getAUse() = n2.asExpr()
exists(Variable v, MethodAccess add |
add.getAnArgument() = n1.asExpr() and
add.getMethod().(CollectionMethod).hasName("add") and
add.getQualifier() = v.getAnAccess()
|
exists(MethodAccess get |
get.getQualifier() = v.getAnAccess() and
get.getMethod().(CollectionMethod).hasName("get") and
n2.asExpr() = get
)
or
exists(EnhancedForStmt for, BaseSsaVariable ssa, BaseSsaVariable def |
for.getVariable() = def.(BaseSsaUpdate).getDefiningExpr() and
for.getExpr() = v.getAnAccess() and
ssa.getAnUltimateDefinition() = def and
ssa.getAUse() = n2.asExpr()
)
)
)
}
predicate levelStepCall(Node n1, LocalSourceNode n2) { none() }
predicate storeStep(Node n1, Node n2, Content f) {
exists(EnumType enum, Method getValue |
enum.getAnEnumConstant().getAnAssignedValue() = n1.asExpr() and
getValue.getDeclaringType() = enum and
getValue.hasName("values") and
n2.asExpr().(MethodAccess).getMethod() = getValue and
f = ContentArray()
)
or
n2.asExpr().(ArrayInit).getAnInit() = n1.asExpr() and
f = ContentArray()
or
exists(Argument arg |
n1.asExpr() = arg and
arg.isVararg() and
n2.(DataFlow::ImplicitVarargsArray).getCall() = arg.getCall() and
f = ContentArray()
)
or
exists(AssignExpr a, Variable v |
a.getSource() = n1.asExpr() and
a.getDest().(ArrayAccess).getArray() = v.getAnAccess() and
n2.asExpr() = v.getAnAccess().(RValue) and
f = ContentArray()
)
}
predicate loadStep(Node n1, LocalSourceNode n2, Content f) {
exists(BaseSsaVariable v, BaseSsaVariable def |
exists(EnhancedForStmt for |
for.getVariable() = def.(BaseSsaUpdate).getDefiningExpr() and
for.getExpr() = n1.asExpr() and
n1.getType() instanceof Array and
f = ContentArray()
)
|
v.getAnUltimateDefinition() = def and
v.getAUse() = n2.asExpr()
)
or
n2.asExpr().(ArrayAccess).getArray() = n1.asExpr()
}
predicate loadStoreStep(Node nodeFrom, Node nodeTo, Content f1, Content f2) {
loadStep(nodeFrom, nodeTo, ContentArray()) and
f1 = ContentArrayArray() and
f2 = ContentArray()
or
storeStep(nodeFrom, nodeTo, ContentArray()) and
f1 = ContentArray() and
f2 = ContentArrayArray()
}
predicate withContentStep(Node nodeFrom, LocalSourceNode nodeTo, ContentFilter f) { none() }
predicate withoutContentStep(Node nodeFrom, LocalSourceNode nodeTo, ContentFilter f) { none() }
predicate jumpStep(Node n1, LocalSourceNode n2) {
n2.(DataFlow::FieldValueNode).getField().getAnAssignedValue() = n1.asExpr()
or
n2.asExpr().(FieldRead).getField() = n1.(DataFlow::FieldValueNode).getField()
}
predicate hasFeatureBacktrackStoreTarget() { none() }
}
/**
* Holds if `n` is forward-reachable from a relevant `ClassInstanceExpr`.
*/
private predicate nodeCandFwd(Node n) {
dispatchOrigin(n.asExpr(), _, _)
or
exists(Node mid | nodeCandFwd(mid) | flowStep(mid, n) or callFlowStepCand(mid, n))
private predicate lambdaSource(RelevantNode n) { dispatchOrigin(n.asExpr(), _, _) }
private predicate lambdaSink(RelevantNode n) {
exists(MethodAccess ma | dispatchOrigin(_, ma, _) | n = DataFlow::getInstanceArgument(ma))
}
/**
* Holds if `n` may occur on a dispatch flow path. That is, a path from a
* relevant `ClassInstanceExpr` to a qualifier of a relevant `MethodAccess`.
*/
private predicate nodeCand(Node n) {
exists(MethodAccess ma |
dispatchOrigin(_, ma, _) and
n = getInstanceArgument(ma) and
nodeCandFwd(n)
)
or
exists(Node mid | nodeCand(mid) | flowStep(n, mid) or callFlowStepCand(n, mid)) and
nodeCandFwd(n)
private signature Method methodDispatchSig(MethodAccess ma);
private module TrackLambda<methodDispatchSig/1 lambdaDispatch0> {
private Callable dispatch(Call c) {
result = dispatchCand(c) or
result = lambdaDispatch0(c)
}
/**
* Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
* The instance parameter is considered to have index `-1`.
*/
pragma[nomagic]
private predicate paramCand(Call call, int i, DataFlow::ParameterNode p) {
exists(DataFlowPrivate::DataFlowCallable callable |
callable.asCallable() = dispatch(call) and
p.isParameterOf(callable, i) and
p instanceof RelevantNode
)
}
/**
* Holds if `arg` is a possible argument to `p` taking virtual dispatch into account.
*/
private predicate argParamCand(DataFlowPrivate::ArgumentNode arg, DataFlow::ParameterNode p) {
exists(int i, DataFlowPrivate::DataFlowCall call |
paramCand(call.asCall(), i, p) and
arg.argumentOf(call, i)
)
}
private module TtInput implements TypeTrackingInput {
import TypeTrackingSteps
predicate callStep(Node n1, LocalSourceNode n2) { argParamCand(n1, n2) }
predicate returnStep(Node n1, LocalSourceNode n2) {
exists(ReturnStmt ret, Method m |
ret.getEnclosingCallable() = m and
ret.getResult() = n1.asExpr() and
m = dispatch(n2.asExpr())
)
}
}
private import TypeTracking<TtInput>::TypeTrack<lambdaSource/1>::Graph<lambdaSink/1>
private predicate edgePlus(PathNode n1, PathNode n2) = fastTC(edges/2)(n1, n2)
private predicate pairCand(PathNode p1, PathNode p2, Method m, MethodAccess ma) {
exists(ClassInstanceExpr cie |
dispatchOrigin(cie, ma, m) and
p1.getNode() = DataFlow::exprNode(cie) and
p2.getNode() = DataFlow::getInstanceArgument(ma) and
p1.isSource() and
p2.isSink()
)
}
/**
* Holds if there is flow from a `ClassInstanceExpr` instantiating a type that
* declares or inherits the tracked method `result` to the qualifier of `ma` such
* that `ma` may dispatch to `result`.
*/
Method lambdaDispatch(MethodAccess ma) {
exists(PathNode p1, PathNode p2 |
(p1 = p2 or edgePlus(p1, p2)) and
pairCand(p1, p2, result, ma)
)
}
}
/**
* Holds if `n1 -> n2` is a relevant dispatch flow step.
*/
private predicate step(Node n1, Node n2) {
(flowStep(n1, n2) or callFlowStepCand(n1, n2)) and
nodeCand(n1) and
nodeCand(n2)
}
private Method noDisp(MethodAccess ma) { none() }
private predicate stepPlus(Node n1, Node n2) = fastTC(step/2)(n1, n2)
pragma[nomagic]
private Method d1(MethodAccess ma) { result = TrackLambda<noDisp/1>::lambdaDispatch(ma) }
/**
* Holds if there is flow from a `ClassInstanceExpr` instantiating a type that
* declares or inherits the tracked method `m` to the qualifier of `ma` such
* that `ma` may dispatch to `m`.
*/
pragma[inline]
private predicate hasDispatchFlow(MethodAccess ma, Method m) {
exists(ClassInstanceExpr cie |
dispatchOrigin(cie, ma, m) and
stepPlus(exprNode(cie), getInstanceArgument(ma))
)
}
pragma[nomagic]
private Method d2(MethodAccess ma) { result = TrackLambda<d1/1>::lambdaDispatch(ma) }
pragma[nomagic]
private Method d3(MethodAccess ma) { result = TrackLambda<d2/1>::lambdaDispatch(ma) }
pragma[nomagic]
private Method d4(MethodAccess ma) { result = TrackLambda<d3/1>::lambdaDispatch(ma) }
pragma[nomagic]
private Method d5(MethodAccess ma) { result = TrackLambda<d4/1>::lambdaDispatch(ma) }
pragma[nomagic]
private Method d6(MethodAccess ma) { result = TrackLambda<d5/1>::lambdaDispatch(ma) }
/**
* Gets a viable dispatch target for `ma`. This is the output dispatch relation.
*/
Method viableImpl_out(MethodAccess ma) {
result = viableImpl_inp(ma) and
(hasDispatchFlow(ma, result) or not dispatchOrigin(_, ma, result))
(result = d6(ma) or not dispatchOrigin(_, ma, result))
}

View File

@@ -26,7 +26,12 @@ class AndroidComponent extends Class {
/** The XML element corresponding to this Android component. */
AndroidComponentXmlElement getAndroidComponentXmlElement() {
result.getResolvedComponentName() = this.getQualifiedName()
// Find an element with an identifier matching the qualified name of the component.
// Aliases have two identifiers (name and target), so check both identifiers (if present).
exists(AndroidIdentifierXmlAttribute identifier |
identifier = result.getAnAttribute() and
result.getResolvedIdentifier(identifier) = this.getQualifiedName()
)
}
/** Holds if this Android component is configured as `exported` in an `AndroidManifest.xml` file. */
@@ -52,6 +57,12 @@ class ExportableAndroidComponent extends AndroidComponent {
or
this.hasIntentFilter() and
not this.getAndroidComponentXmlElement().isNotExported()
or
exists(AndroidActivityAliasXmlElement e |
e = this.getAndroidComponentXmlElement() and
not e.isNotExported() and
e.hasAnIntentFilterElement()
)
}
}

View File

@@ -129,6 +129,42 @@ class AndroidApplicationXmlElement extends XmlElement {
*/
class AndroidActivityXmlElement extends AndroidComponentXmlElement {
AndroidActivityXmlElement() { this.getName() = "activity" }
/**
* Gets an `<activity-alias>` element aliasing the activity.
*/
AndroidActivityAliasXmlElement getAnAlias() {
exists(AndroidActivityAliasXmlElement alias | this = alias.getTarget() | result = alias)
}
}
/**
* An `<activity-alias>` element in an Android manifest file.
*/
class AndroidActivityAliasXmlElement extends AndroidComponentXmlElement {
AndroidActivityAliasXmlElement() { this.getName() = "activity-alias" }
/**
* Get and resolve the name of the target activity from the `android:targetActivity` attribute.
*/
string getResolvedTargetActivityName() {
exists(AndroidXmlAttribute attr |
attr = this.getAnAttribute() and attr.getName() = "targetActivity"
|
result = this.getResolvedIdentifier(attr)
)
}
/**
* Gets the `<activity>` element referenced by the `android:targetActivity` attribute.
*/
AndroidActivityXmlElement getTarget() {
exists(AndroidActivityXmlElement activity |
activity.getResolvedComponentName() = this.getResolvedTargetActivityName()
|
result = activity
)
}
}
/**
@@ -212,6 +248,13 @@ class AndroidPermissionXmlAttribute extends XmlAttribute {
predicate isWrite() { this.getName() = "writePermission" }
}
/**
* The attribute `android:name` or `android:targetActivity`.
*/
class AndroidIdentifierXmlAttribute extends AndroidXmlAttribute {
AndroidIdentifierXmlAttribute() { this.getName() = ["name", "targetActivity"] }
}
/**
* The `<path-permission`> element of a `<provider>` in an Android manifest file.
*/
@@ -228,7 +271,7 @@ class AndroidPathPermissionXmlElement extends XmlElement {
class AndroidComponentXmlElement extends XmlElement {
AndroidComponentXmlElement() {
this.getParent() instanceof AndroidApplicationXmlElement and
this.getName().regexpMatch("(activity|service|receiver|provider)")
this.getName().regexpMatch("(activity|activity-alias|service|receiver|provider)")
}
/**
@@ -254,19 +297,30 @@ class AndroidComponentXmlElement extends XmlElement {
)
}
/**
* Gets the value of an identifier attribute, and tries to resolve it into a fully qualified identifier.
*/
string getResolvedIdentifier(AndroidIdentifierXmlAttribute identifier) {
exists(string name | name = identifier.getValue() |
if name.matches(".%")
then
result =
this.getParent()
.(XmlElement)
.getParent()
.(AndroidManifestXmlElement)
.getPackageAttributeValue() + name
else result = name
)
}
/**
* Gets the resolved value of the `android:name` attribute of this component element.
*/
string getResolvedComponentName() {
if this.getComponentName().matches(".%")
then
result =
this.getParent()
.(XmlElement)
.getParent()
.(AndroidManifestXmlElement)
.getPackageAttributeValue() + this.getComponentName()
else result = this.getComponentName()
exists(AndroidXmlAttribute attr | attr = this.getAnAttribute() and attr.getName() = "name" |
result = this.getResolvedIdentifier(attr)
)
}
/**