Merge pull request #17308 from hvitved/dataflow/flow-through-restriction

Data flow: Reduce non-linear recursion in `fwdFlow0`
This commit is contained in:
Tom Hvitved
2024-09-03 11:30:57 +02:00
committed by GitHub

View File

@@ -545,6 +545,8 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
private module Stage1 implements StageSig { private module Stage1 implements StageSig {
class Ap = Unit; class Ap = Unit;
class ApNil = Ap;
private class Cc = boolean; private class Cc = boolean;
/* Begin: Stage 1 logic. */ /* Begin: Stage 1 logic. */
@@ -1297,6 +1299,8 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
private signature module StageSig { private signature module StageSig {
class Ap; class Ap;
class ApNil extends Ap;
predicate revFlow(NodeEx node); predicate revFlow(NodeEx node);
predicate revFlowAp(NodeEx node, Ap ap); predicate revFlowAp(NodeEx node, Ap ap);
@@ -1560,20 +1564,17 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
apa = getApprox(ap) apa = getApprox(ap)
) )
or or
// flow into a callable // flow into a callable without summary context
exists(boolean allowsFlowThrough | fwdFlowInNoFlowThrough(node, apa, state, cc, t, ap) and
fwdFlowIn(node, apa, state, cc, t, ap, allowsFlowThrough) and summaryCtx = TSummaryCtxNone() and
if allowsFlowThrough = true // When the call contexts of source and sink needs to match then there's
then ( // never any reason to enter a callable except to find a summary. See also
summaryCtx = TSummaryCtxSome(node, state, t, ap) // the comment in `PathNodeMid::isAtSink`.
) else ( not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext
summaryCtx = TSummaryCtxNone() and or
// When the call contexts of source and sink needs to match then there's // flow into a callable with summary context (non-linear recursion)
// never any reason to enter a callable except to find a summary. See also fwdFlowInFlowThrough(node, apa, state, cc, t, ap) and
// the comment in `PathNodeMid::isAtSink`. summaryCtx = TSummaryCtxSome(node, state, t, ap)
not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
)
or or
// flow out of a callable // flow out of a callable
fwdFlowOut(_, _, node, state, cc, summaryCtx, t, ap, apa) fwdFlowOut(_, _, node, state, cc, summaryCtx, t, ap, apa)
@@ -1593,7 +1594,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
private newtype TSummaryCtx = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, Typ t, Ap ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, Typ t, Ap ap) {
fwdFlowIn(p, _, state, _, t, ap, true) fwdFlowInFlowThrough(p, _, state, _, t, ap)
} }
/** /**
@@ -1721,12 +1722,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
if ap instanceof ApNil then emptyAp = true else emptyAp = false if ap instanceof ApNil then emptyAp = true else emptyAp = false
} }
private signature module FwdFlowInInputSig { private signature predicate flowThroughSig();
default predicate callRestriction(DataFlowCall call) { any() }
bindingset[p, apa]
default predicate parameterRestriction(ParamNodeEx p, ApApprox apa) { any() }
}
/** /**
* Exposes the inlined predicate `fwdFlowIn`, which is used to calculate both * Exposes the inlined predicate `fwdFlowIn`, which is used to calculate both
@@ -1736,19 +1732,40 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
* need to record the argument that flows into the parameter. * need to record the argument that flows into the parameter.
* *
* For flow through, we do need to record the argument, however, we can restrict * For flow through, we do need to record the argument, however, we can restrict
* this to arguments that may actually flow through, using `callRestriction` and * this to arguments that may actually flow through, which reduces the
* `parameterRestriction`, which reduces the argument-to-parameter fan-in * argument-to-parameter fan-in significantly.
* significantly.
*/ */
private module FwdFlowIn<FwdFlowInInputSig I> { private module FwdFlowIn<flowThroughSig/0 flowThrough> {
pragma[nomagic] pragma[nomagic]
private predicate callEdgeArgParamRestricted( private predicate callEdgeArgParamRestricted(
DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, boolean emptyAp,
boolean allowsFieldFlow, ApApprox apa ApApprox apa
) { ) {
PrevStage::callEdgeArgParam(call, c, arg, p, allowsFieldFlow, apa) and exists(boolean allowsFieldFlow |
I::callRestriction(call) and PrevStage::callEdgeArgParam(call, c, arg, p, allowsFieldFlow, apa)
I::parameterRestriction(p, apa) |
if
PrevStage::callMayFlowThroughRev(call) and
PrevStage::parameterMayFlowThrough(p, apa)
then
emptyAp = true and
apa instanceof PrevStage::ApNil and
flowThrough()
or
emptyAp = false and
allowsFieldFlow = true and
if allowsFieldFlowThrough(call, c) then flowThrough() else not flowThrough()
else (
not flowThrough() and
(
emptyAp = true and
apa instanceof PrevStage::ApNil
or
emptyAp = false and
allowsFieldFlow = true
)
)
)
} }
pragma[nomagic] pragma[nomagic]
@@ -1780,10 +1797,10 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
bindingset[call] bindingset[call]
pragma[inline_late] pragma[inline_late]
private predicate callEdgeArgParamRestrictedInlineLate( private predicate callEdgeArgParamRestrictedInlineLate(
DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, boolean emptyAp,
boolean allowsFieldFlow, ApApprox apa ApApprox apa
) { ) {
callEdgeArgParamRestricted(call, c, arg, p, allowsFieldFlow, apa) callEdgeArgParamRestricted(call, c, arg, p, emptyAp, apa)
} }
bindingset[call, ctx] bindingset[call, ctx]
@@ -1807,44 +1824,35 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
private predicate fwdFlowInCand( private predicate fwdFlowInCand(
DataFlowCall call, ArgNodeEx arg, FlowState state, Cc outercc, DataFlowCallable inner, DataFlowCall call, ArgNodeEx arg, FlowState state, Cc outercc, DataFlowCallable inner,
ParamNodeEx p, SummaryCtx summaryCtx, Typ t, Ap ap, boolean emptyAp, ApApprox apa, ParamNodeEx p, SummaryCtx summaryCtx, Typ t, Ap ap, boolean emptyAp, ApApprox apa,
boolean cc, boolean allowsFlowThrough boolean cc
) { ) {
exists(boolean allowsFieldFlow | fwdFlowIntoArg(arg, state, outercc, summaryCtx, t, ap, emptyAp, apa, cc) and
fwdFlowIntoArg(arg, state, outercc, summaryCtx, t, ap, emptyAp, apa, cc) and (
( inner = viableImplCallContextReducedInlineLate(call, arg, outercc)
inner = viableImplCallContextReducedInlineLate(call, arg, outercc) or
or viableImplArgNotCallContextReduced(call, arg, outercc)
viableImplArgNotCallContextReduced(call, arg, outercc) ) and
) and not outBarrier(arg, state) and
not outBarrier(arg, state) and not inBarrier(p, state) and
not inBarrier(p, state) and callEdgeArgParamRestrictedInlineLate(call, inner, arg, p, emptyAp, apa)
callEdgeArgParamRestrictedInlineLate(call, inner, arg, p, allowsFieldFlow, apa) and
(if allowsFieldFlow = false then emptyAp = true else any()) and
if allowsFieldFlowThrough(call, inner)
then allowsFlowThrough = true
else allowsFlowThrough = emptyAp
)
} }
pragma[inline] pragma[inline]
private predicate fwdFlowInCandTypeFlowDisabled( private predicate fwdFlowInCandTypeFlowDisabled(
DataFlowCall call, ArgNodeEx arg, FlowState state, Cc outercc, DataFlowCallable inner, DataFlowCall call, ArgNodeEx arg, FlowState state, Cc outercc, DataFlowCallable inner,
ParamNodeEx p, SummaryCtx summaryCtx, Typ t, Ap ap, ApApprox apa, boolean cc, ParamNodeEx p, SummaryCtx summaryCtx, Typ t, Ap ap, ApApprox apa, boolean cc
boolean allowsFlowThrough
) { ) {
not enableTypeFlow() and not enableTypeFlow() and
fwdFlowInCand(call, arg, state, outercc, inner, p, summaryCtx, t, ap, _, apa, cc, fwdFlowInCand(call, arg, state, outercc, inner, p, summaryCtx, t, ap, _, apa, cc)
allowsFlowThrough)
} }
pragma[nomagic] pragma[nomagic]
private predicate fwdFlowInCandTypeFlowEnabled( private predicate fwdFlowInCandTypeFlowEnabled(
DataFlowCall call, ArgNodeEx arg, Cc outercc, DataFlowCallable inner, ParamNodeEx p, DataFlowCall call, ArgNodeEx arg, Cc outercc, DataFlowCallable inner, ParamNodeEx p,
boolean emptyAp, ApApprox apa, boolean cc, boolean allowsFlowThrough boolean emptyAp, ApApprox apa, boolean cc
) { ) {
enableTypeFlow() and enableTypeFlow() and
fwdFlowInCand(call, arg, _, outercc, inner, p, _, _, _, emptyAp, apa, cc, fwdFlowInCand(call, arg, _, outercc, inner, p, _, _, _, emptyAp, apa, cc)
allowsFlowThrough)
} }
pragma[nomagic] pragma[nomagic]
@@ -1859,10 +1867,9 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
pragma[nomagic] pragma[nomagic]
private predicate fwdFlowInValidEdgeTypeFlowEnabled( private predicate fwdFlowInValidEdgeTypeFlowEnabled(
DataFlowCall call, ArgNodeEx arg, Cc outercc, DataFlowCallable inner, ParamNodeEx p, DataFlowCall call, ArgNodeEx arg, Cc outercc, DataFlowCallable inner, ParamNodeEx p,
CcCall innercc, boolean emptyAp, ApApprox apa, boolean cc, boolean allowsFlowThrough CcCall innercc, boolean emptyAp, ApApprox apa, boolean cc
) { ) {
fwdFlowInCandTypeFlowEnabled(call, arg, outercc, inner, p, emptyAp, apa, cc, fwdFlowInCandTypeFlowEnabled(call, arg, outercc, inner, p, emptyAp, apa, cc) and
allowsFlowThrough) and
FwdTypeFlow::typeFlowValidEdgeIn(call, inner, cc) and FwdTypeFlow::typeFlowValidEdgeIn(call, inner, cc) and
innercc = getCallContextCall(call, inner) innercc = getCallContextCall(call, inner)
} }
@@ -1871,36 +1878,42 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
predicate fwdFlowIn( predicate fwdFlowIn(
DataFlowCall call, ArgNodeEx arg, DataFlowCallable inner, ParamNodeEx p, DataFlowCall call, ArgNodeEx arg, DataFlowCallable inner, ParamNodeEx p,
FlowState state, Cc outercc, CcCall innercc, SummaryCtx summaryCtx, Typ t, Ap ap, FlowState state, Cc outercc, CcCall innercc, SummaryCtx summaryCtx, Typ t, Ap ap,
ApApprox apa, boolean cc, boolean allowsFlowThrough ApApprox apa, boolean cc
) { ) {
// type flow disabled: linear recursion // type flow disabled: linear recursion
fwdFlowInCandTypeFlowDisabled(call, arg, state, outercc, inner, p, summaryCtx, t, ap, fwdFlowInCandTypeFlowDisabled(call, arg, state, outercc, inner, p, summaryCtx, t, ap,
apa, cc, allowsFlowThrough) and apa, cc) and
fwdFlowInValidEdgeTypeFlowDisabled(call, inner, innercc, pragma[only_bind_into](cc)) fwdFlowInValidEdgeTypeFlowDisabled(call, inner, innercc, pragma[only_bind_into](cc))
or or
// type flow enabled: non-linear recursion // type flow enabled: non-linear recursion
exists(boolean emptyAp | exists(boolean emptyAp |
fwdFlowIntoArg(arg, state, outercc, summaryCtx, t, ap, emptyAp, apa, cc) and fwdFlowIntoArg(arg, state, outercc, summaryCtx, t, ap, emptyAp, apa, cc) and
fwdFlowInValidEdgeTypeFlowEnabled(call, arg, outercc, inner, p, innercc, emptyAp, apa, fwdFlowInValidEdgeTypeFlowEnabled(call, arg, outercc, inner, p, innercc, emptyAp, apa,
cc, allowsFlowThrough) cc)
) )
} }
} }
private module FwdFlowInNoRestriction implements FwdFlowInInputSig { } private predicate bottom() { none() }
private module FwdFlowInNoThrough = FwdFlowIn<bottom/0>;
pragma[nomagic] pragma[nomagic]
private predicate fwdFlowIn( private predicate fwdFlowInNoFlowThrough(
ParamNodeEx p, ApApprox apa, FlowState state, CcCall innercc, Typ t, Ap ap, ParamNodeEx p, ApApprox apa, FlowState state, CcCall innercc, Typ t, Ap ap
boolean allowsFlowThrough
) { ) {
exists(boolean allowsFlowThrough0 | FwdFlowInNoThrough::fwdFlowIn(_, _, _, p, state, _, innercc, _, t, ap, apa, _)
FwdFlowIn<FwdFlowInNoRestriction>::fwdFlowIn(_, _, _, p, state, _, innercc, _, t, ap, }
apa, _, allowsFlowThrough0) and
if PrevStage::parameterMayFlowThrough(p, apa) private predicate top() { any() }
then allowsFlowThrough = allowsFlowThrough0
else allowsFlowThrough = false private module FwdFlowInThrough = FwdFlowIn<top/0>;
)
pragma[nomagic]
private predicate fwdFlowInFlowThrough(
ParamNodeEx p, ApApprox apa, FlowState state, CcCall innercc, Typ t, Ap ap
) {
FwdFlowInThrough::fwdFlowIn(_, _, _, p, state, _, innercc, _, t, ap, apa, _)
} }
pragma[nomagic] pragma[nomagic]
@@ -2000,8 +2013,9 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
DataFlowCall call, DataFlowCallable c, ParamNodeEx p, FlowState state, CcCall innercc, DataFlowCall call, DataFlowCallable c, ParamNodeEx p, FlowState state, CcCall innercc,
Typ t, Ap ap, boolean cc Typ t, Ap ap, boolean cc
) { ) {
FwdFlowIn<FwdFlowInNoRestriction>::fwdFlowIn(call, _, c, p, state, _, innercc, _, t, ap, FwdFlowInNoThrough::fwdFlowIn(call, _, c, p, state, _, innercc, _, t, ap, _, cc)
_, cc, _) or
FwdFlowInThrough::fwdFlowIn(call, _, c, p, state, _, innercc, _, t, ap, _, cc)
} }
pragma[nomagic] pragma[nomagic]
@@ -2104,19 +2118,12 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
fwdFlowThrough0(call, _, cc, state, ccc, summaryCtx, t, ap, apa, ret, _, innerArgApa) fwdFlowThrough0(call, _, cc, state, ccc, summaryCtx, t, ap, apa, ret, _, innerArgApa)
} }
private module FwdFlowThroughRestriction implements FwdFlowInInputSig {
predicate callRestriction = PrevStage::callMayFlowThroughRev/1;
predicate parameterRestriction = PrevStage::parameterMayFlowThrough/2;
}
pragma[nomagic] pragma[nomagic]
private predicate fwdFlowIsEntered0( private predicate fwdFlowIsEntered0(
DataFlowCall call, ArgNodeEx arg, Cc cc, CcCall innerCc, SummaryCtx summaryCtx, DataFlowCall call, ArgNodeEx arg, Cc cc, CcCall innerCc, SummaryCtx summaryCtx,
ParamNodeEx p, FlowState state, Typ t, Ap ap ParamNodeEx p, FlowState state, Typ t, Ap ap
) { ) {
FwdFlowIn<FwdFlowThroughRestriction>::fwdFlowIn(call, arg, _, p, state, cc, innerCc, FwdFlowInThrough::fwdFlowIn(call, arg, _, p, state, cc, innerCc, summaryCtx, t, ap, _, _)
summaryCtx, t, ap, _, _, true)
} }
/** /**
@@ -3067,15 +3074,15 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
pragma[nomagic] pragma[nomagic]
private predicate fwdFlowInStep( private predicate fwdFlowInStep(
ArgNodeEx arg, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc, ArgNodeEx arg, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
SummaryCtx summaryCtx, Typ t, Ap ap, boolean allowsFlowThrough SummaryCtx outerSummaryCtx, SummaryCtx innerSummaryCtx, Typ t, Ap ap
) { ) {
exists(ApApprox apa, boolean allowsFlowThrough0 | FwdFlowInNoThrough::fwdFlowIn(_, arg, _, p, state, outercc, innercc, outerSummaryCtx, t,
FwdFlowIn<FwdFlowInNoRestriction>::fwdFlowIn(_, arg, _, p, state, outercc, innercc, ap, _, _) and
summaryCtx, t, ap, apa, _, allowsFlowThrough0) and innerSummaryCtx = TSummaryCtxNone()
if PrevStage::parameterMayFlowThrough(p, apa) or
then allowsFlowThrough = allowsFlowThrough0 FwdFlowInThrough::fwdFlowIn(_, arg, _, p, state, outercc, innercc, outerSummaryCtx, t,
else allowsFlowThrough = false ap, _, _) and
) innerSummaryCtx = TSummaryCtxSome(p, state, t, ap)
} }
pragma[nomagic] pragma[nomagic]
@@ -3239,15 +3246,10 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
) )
or or
// flow into a callable // flow into a callable
exists( exists(ArgNodeEx arg, Cc outercc, SummaryCtx outerSummaryCtx |
ArgNodeEx arg, boolean allowsFlowThrough, Cc outercc, SummaryCtx outerSummaryCtx
|
pn1 = TPathNodeMid(arg, state, outercc, outerSummaryCtx, t, ap) and pn1 = TPathNodeMid(arg, state, outercc, outerSummaryCtx, t, ap) and
fwdFlowInStep(arg, node, state, outercc, cc, outerSummaryCtx, t, ap, allowsFlowThrough) and fwdFlowInStep(arg, node, state, outercc, cc, outerSummaryCtx, summaryCtx, t, ap) and
label = "" and label = ""
if allowsFlowThrough = true
then summaryCtx = TSummaryCtxSome(node, state, t, ap)
else summaryCtx = TSummaryCtxNone()
) )
or or
// flow out of a callable // flow out of a callable