Dataflow: Switch call context to a set representation.

This commit is contained in:
Anders Schack-Mulligen
2024-05-08 15:18:57 +02:00
parent 972b81bbd1
commit 5a259843bb
4 changed files with 361 additions and 167 deletions

View File

@@ -399,6 +399,21 @@ class CastNode extends ExprNode {
}
}
private predicate id_member(Member x, Member y) { x = y }
private predicate idOf_member(Member x, int y) = equivalenceRelation(id_member/2)(x, y)
private int summarizedCallableId(SummarizedCallable c) {
c =
rank[result](SummarizedCallable c0, int b, int i, string s |
b = 0 and idOf_member(c0.asCallable(), i) and s = ""
or
b = 1 and i = 0 and s = c0.asSyntheticCallable()
|
c0 order by b, i, s
)
}
private newtype TDataFlowCallable =
TSrcCallable(Callable c) or
TSummarizedCallable(SummarizedCallable c) or
@@ -432,10 +447,28 @@ class DataFlowCallable extends TDataFlowCallable {
result = this.asSummarizedCallable().getLocation() or
result = this.asFieldScope().getLocation()
}
/** Gets a best-effort total ordering. */
int totalorder() {
this =
rank[result](DataFlowCallable c, int b, int i |
b = 0 and idOf_member(c.asCallable(), i)
or
b = 1 and i = summarizedCallableId(c.asSummarizedCallable())
or
b = 2 and idOf_member(c.asFieldScope(), i)
|
c order by b, i
)
}
}
class DataFlowExpr = Expr;
private predicate id_call(Call x, Call y) { x = y }
private predicate idOf_call(Call x, int y) = equivalenceRelation(id_call/2)(x, y)
private newtype TDataFlowCall =
TCall(Call c) or
TSummaryCall(SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver) {
@@ -468,6 +501,19 @@ class DataFlowCall extends TDataFlowCall {
) {
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets a best-effort total ordering. */
int totalorder() {
this =
rank[result](DataFlowCall c, int b, int i |
b = 0 and idOf_call(c.asCall(), i)
or
b = 1 and // not guaranteed to be total
exists(SummarizedCallable sc | c = TSummaryCall(sc, _) and i = summarizedCallableId(sc))
|
c order by b, i
)
}
}
/** A source call, that is, a `Call`. */

View File

@@ -73,11 +73,17 @@ signature module InputSig<LocationSig Location> {
string toString();
DataFlowCallable getEnclosingCallable();
/** Gets a best-effort total ordering. */
int totalorder();
}
class DataFlowCallable {
/** Gets a textual representation of this element. */
string toString();
/** Gets a best-effort total ordering. */
int totalorder();
}
class ReturnKind {

View File

@@ -1369,6 +1369,12 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
CcCall ccSomeCall();
predicate instanceofCc(Cc cc);
predicate instanceofCcCall(CcCall cc);
predicate instanceofCcNoCall(CcNoCall cc);
class LocalCc;
DataFlowCallable viableImplCallContextReduced(DataFlowCall call, CcCall ctx);
@@ -1386,8 +1392,8 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
bindingset[call, c]
CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call);
bindingset[c, cc]
LocalCc getLocalCc(DataFlowCallable c, Cc cc);
bindingset[cc]
LocalCc getLocalCc(Cc cc);
bindingset[node1, state1]
bindingset[node2, state2]
@@ -1476,7 +1482,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
or
exists(NodeEx mid, FlowState state0, Typ t0, LocalCc localCc |
fwdFlow(mid, state0, cc, summaryCtx, argT, argAp, t0, ap, apa) and
localCc = getLocalCc(mid.getEnclosingCallable(), cc)
localCc = getLocalCc(cc)
|
localStep(mid, state0, node, state, true, _, localCc) and
t = t0
@@ -1613,7 +1619,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
ApOption argAp, Typ t, Ap ap, boolean emptyAp, ApApprox apa, boolean cc
) {
fwdFlow(arg, state, outercc, summaryCtx, argT, argAp, t, ap, apa) and
(if outercc instanceof CcCall then cc = true else cc = false) and
(if instanceofCcCall(outercc) then cc = true else cc = false) and
if ap instanceof ApNil then emptyAp = true else emptyAp = false
}
@@ -1669,6 +1675,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
DataFlowCall call, ArgNodeEx arg, CcCall ctx
) {
callEdgeArgParamRestricted(call, _, arg, _, _, _) and
instanceofCcCall(ctx) and
result = viableImplCallContextReducedInlineLate(call, ctx)
}
@@ -1684,6 +1691,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
bindingset[call, ctx]
pragma[inline_late]
private predicate viableImplNotCallContextReducedInlineLate(DataFlowCall call, Cc ctx) {
instanceofCc(ctx) and
viableImplNotCallContextReduced(call, ctx)
}
@@ -1693,6 +1701,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
DataFlowCall call, ArgNodeEx arg, Cc outercc
) {
callEdgeArgParamRestricted(call, _, arg, _, _, _) and
instanceofCc(outercc) and
viableImplNotCallContextReducedInlineLate(call, outercc)
}
@@ -1837,6 +1846,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
RetNodeEx ret, FlowState state, CcNoCall cc, ParamNodeOption summaryCtx, TypOption argT,
ApOption argAp, Typ t, Ap ap, ApApprox apa
) {
instanceofCcNoCall(cc) and
fwdFlow(ret, state, cc, summaryCtx, argT, argAp, t, ap, apa)
}
@@ -1896,6 +1906,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
pragma[nomagic]
private predicate fwdFlow1Param(ParamNodeEx p, FlowState state, CcCall cc, Typ t0, Ap ap) {
instanceofCcCall(cc) and
fwdFlow1(p, state, cc, _, _, _, t0, _, ap, _)
}
@@ -1962,6 +1973,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
ApApprox argApa, Typ t, Ap ap, ApApprox apa
) {
exists(ReturnKindExt kind |
instanceofCcCall(ccc) and
fwdFlow(pragma[only_bind_into](ret), state, ccc,
TParamNodeSome(pragma[only_bind_into](summaryCtx.asNode())), TypOption::some(argT),
pragma[only_bind_into](apSome(argAp)), t, ap, pragma[only_bind_into](apa)) and
@@ -2527,10 +2539,16 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
CcCall ccSomeCall() { result = true }
predicate instanceofCc(Cc cc) { any() }
predicate instanceofCcCall(CcCall cc) { any() }
predicate instanceofCcNoCall(CcNoCall cc) { any() }
class LocalCc = Unit;
bindingset[c, cc]
LocalCc getLocalCc(DataFlowCallable c, Cc cc) { any() }
bindingset[cc]
LocalCc getLocalCc(Cc cc) { any() }
DataFlowCallable viableImplCallContextReduced(DataFlowCall call, CcCall ctx) { none() }
@@ -4081,7 +4099,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
state = mid.getState() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
localCC = PrunedCallContextSensitivityStage5::getLocalCc(midnode.getEnclosingCallable(), cc) and
localCC = PrunedCallContextSensitivityStage5::getLocalCc(cc) and
t = mid.getType() and
ap = mid.getAp() and
summaryLabel = mid.getSummaryLabel()
@@ -5144,7 +5162,7 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
boolean isStoreStep
) {
not isUnreachableInCall1(node,
CachedCallContextSensitivity::LocalCallContext::getLocalCc(node.getEnclosingCallable(), cc)) and
CachedCallContextSensitivity::LocalCallContext::getLocalCc(cc)) and
(
localFlowStepEx(mid.getNodeEx(), node, _) and
state = mid.getState() and

View File

@@ -1,6 +1,7 @@
private import codeql.dataflow.DataFlow
private import codeql.typetracking.TypeTracking as Tt
private import codeql.util.Location
private import codeql.util.Option
private import codeql.util.Unit
private import codeql.util.Option
private import codeql.util.internal.MakeSets
@@ -407,27 +408,6 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
result = viableCallableLambda(call, _)
}
private newtype TCallEdge =
TMkCallEdge(DataFlowCall call, DataFlowCallable tgt) { viableCallableExt(call) = tgt }
private module UnreachableSetsInput implements MkSetsInp {
class Key = TCallEdge;
class Value = NodeRegion;
NodeRegion getAValue(TCallEdge edge) {
exists(DataFlowCall call, DataFlowCallable tgt |
edge = TMkCallEdge(call, tgt) and
getNodeRegionEnclosingCallable(result) = tgt and
isUnreachableInCallCached(result, call)
)
}
int totalorder(NodeRegion nr) { result = nr.totalOrder() }
}
private module UnreachableSets = MakeSets<UnreachableSetsInput>;
signature module CallContextSensitivityInputSig {
/** Holds if the edge is possibly needed in the direction `call` to `c`. */
predicate relevantCallEdgeIn(DataFlowCall call, DataFlowCallable c);
@@ -522,14 +502,155 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
)
}
private module CallSetsInput implements MkSetsInp {
class Key = TCallEdge;
class Value = DataFlowCall;
DataFlowCall getAValue(TCallEdge ctxEdge) {
exists(DataFlowCall ctx, DataFlowCallable c |
ctxEdge = TMkCallEdge(ctx, c) and
reducedViableImplInCallContext(result, c, ctx)
)
}
int totalorder(DataFlowCall e) { result = callOrder(e) }
}
private module CallSets = MakeSets<CallSetsInput>;
private module CallSetOption = Option<CallSets::ValueSet>;
private class CallSet = CallSetOption::Option;
private module DispatchSetsInput implements MkSetsInp {
class Key = TCallEdge;
class Value = TCallEdge;
TCallEdge getAValue(TCallEdge ctxEdge) {
exists(DataFlowCall ctx, DataFlowCallable c, DataFlowCall call, DataFlowCallable tgt |
ctxEdge = TMkCallEdge(ctx, c) and
result = TMkCallEdge(call, tgt) and
viableImplInCallContextExtIn(call, ctx) = tgt and
reducedViableImplInCallContext(call, c, ctx)
)
}
int totalorder(TCallEdge e) { result = edgeOrder(e) }
}
private module DispatchSets = MakeSets<DispatchSetsInput>;
private module DispatchSetsOption = Option<DispatchSets::ValueSet>;
private class DispatchSet = DispatchSetsOption::Option;
private predicate relevantCtx(TCallEdge ctx) {
exists(CallSets::getValueSet(ctx)) or exists(getUnreachableSet(ctx))
}
pragma[nomagic]
private predicate hasCtx(
TCallEdge ctx, CallSet calls, DispatchSet tgts, UnreachableSetOption unreachable
) {
relevantCtx(ctx) and
(
CallSets::getValueSet(ctx) = calls.asSome()
or
not exists(CallSets::getValueSet(ctx)) and calls.isNone()
) and
(
DispatchSets::getValueSet(ctx) = tgts.asSome()
or
not exists(DispatchSets::getValueSet(ctx)) and tgts.isNone()
) and
(
getUnreachableSet(ctx) = unreachable.asSome()
or
not exists(getUnreachableSet(ctx)) and unreachable.isNone()
)
}
private newtype TCallContext =
TAnyCallContext() or
TSpecificCall(CallSet calls, DispatchSet tgts, UnreachableSetOption unreachable) {
hasCtx(_, calls, tgts, unreachable)
} or
TSomeCall() or
TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) }
/**
* A call context to restrict the targets of virtual dispatch and prune local flow.
*
* There are four cases:
* - `TAnyCallContext()` : No restrictions on method flow.
* - `TSpecificCall(DataFlowCall call)` : Flow entered through the
* given `call`. This call improves the set of viable
* dispatch targets for at least one method call in the current callable
* or helps prune unreachable nodes in the current callable.
* - `TSomeCall()` : Flow entered through a parameter. The
* originating call does not improve the set of dispatch targets for any
* method call in the current callable and was therefore not recorded.
* - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and
* this dispatch target of `call` implies a reduced set of dispatch origins
* to which data may flow if it should reach a `return` statement.
*/
abstract private class CallContext extends TCallContext {
abstract string toString();
}
abstract private class CallContextCall extends CallContext { }
abstract private class CallContextNoCall extends CallContext { }
private class CallContextAny extends CallContextNoCall, TAnyCallContext {
override string toString() { result = "CcAny" }
}
private class CallContextSpecificCall extends CallContextCall, TSpecificCall {
override string toString() { result = "CcCallSpecific" }
}
private class CallContextSomeCall extends CallContextCall, TSomeCall {
override string toString() { result = "CcSomeCall" }
}
private class CallContextReturn extends CallContextNoCall, TReturn {
override string toString() {
exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
}
}
pragma[nomagic]
CallContextCall getSpecificCallContextCall(DataFlowCall call, DataFlowCallable c) {
exists(CallSet calls, DispatchSet tgts, UnreachableSetOption unreachable |
hasCtx(TMkCallEdge(call, c), calls, tgts, unreachable) and
result = TSpecificCall(calls, tgts, unreachable)
)
}
pragma[nomagic]
predicate callContextAffectsDispatch(DataFlowCall call, CallContext ctx) {
exists(CallSet calls | ctx = TSpecificCall(calls, _, _) | calls.asSome().contains(call))
}
CallContextNoCall getSpecificCallContextReturn(DataFlowCallable c, DataFlowCall call) {
result = TReturn(c, call)
}
signature module PrunedViableImplInputSig {
predicate reducedViableImplInCallContext(
DataFlowCall call, DataFlowCallable c, DataFlowCall ctx
);
predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call);
predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable c);
CallContextCall getSpecificCallContextCall(DataFlowCall call, DataFlowCallable c);
predicate callContextAffectsDispatch(DataFlowCall call, CallContext ctx);
CallContextNoCall getSpecificCallContextReturn(DataFlowCallable c, DataFlowCall call);
}
/**
@@ -542,7 +663,10 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
class CcCall = CallContextCall;
pragma[inline]
predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) }
predicate matchesCall(CcCall cc, DataFlowCall call) {
cc = Input2::getSpecificCallContextCall(call, _) or
cc = ccSomeCall()
}
class CcNoCall = CallContextNoCall;
@@ -550,6 +674,12 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
predicate instanceofCc(Cc cc) { any() }
predicate instanceofCcCall(CcCall cc) { any() }
predicate instanceofCcNoCall(CcNoCall cc) { any() }
/**
* Gets a viable run-time dispatch target for the call `call` in the
* context `ctx`. This is restricted to those calls for which a context
@@ -557,24 +687,15 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
*/
pragma[nomagic]
DataFlowCallable viableImplCallContextReduced(DataFlowCall call, CallContextCall ctx) {
exists(DataFlowCall outer | ctx = TSpecificCall(outer) |
result = viableImplInCallContextExtIn(call, outer) and
Input2::reducedViableImplInCallContext(call, _, outer)
exists(DispatchSet tgts | ctx = TSpecificCall(_, tgts, _) |
tgts.asSome().contains(TMkCallEdge(call, result))
)
}
/** Holds if `call` does not have a reduced set of dispatch targets in call context `ctx`. */
bindingset[call, ctx]
predicate viableImplNotCallContextReduced(DataFlowCall call, CallContext ctx) {
exists(DataFlowCall outer | ctx = TSpecificCall(outer) |
not Input2::reducedViableImplInCallContext(call, _, outer)
)
or
ctx instanceof CallContextSomeCall
or
ctx instanceof CallContextAny
or
ctx instanceof CallContextReturn
not Input2::callContextAffectsDispatch(call, ctx)
}
/**
@@ -602,7 +723,7 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
callEnclosingCallable(call0, callable) and
ctx = TReturn(c0, call0) and
c0 = viableImplInCallContextExtOut(call0, result) and
Input2::reducedViableImplInReturn(c0, call0)
reducedViableImplInReturn(c0, call0)
)
}
@@ -627,9 +748,9 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
/** Gets the call context when returning from `c` to `call`. */
bindingset[call, c]
CallContextNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) {
if Input2::reducedViableImplInReturn(c, call)
then result = TReturn(c, call)
else result = TAnyCallContext()
result = Input2::getSpecificCallContextReturn(c, call)
or
not exists(Input2::getSpecificCallContextReturn(c, call)) and result = TAnyCallContext()
}
/**
@@ -652,13 +773,13 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
module NoLocalCallContext {
class LocalCc = Unit;
bindingset[c, cc]
LocalCc getLocalCc(DataFlowCallable c, CallContext cc) { any() }
bindingset[cc]
LocalCc getLocalCc(CallContext cc) { any() }
bindingset[call, c]
CallContextCall getCallContextCall(DataFlowCall call, DataFlowCallable c) {
if recordDataFlowCallSiteDispatch(call, c)
then result = TSpecificCall(call)
then result = Input2::getSpecificCallContextCall(call, c)
else result = TSomeCall()
}
}
@@ -666,15 +787,26 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
module LocalCallContext {
class LocalCc = LocalCallContext;
bindingset[c, cc]
LocalCc getLocalCc(DataFlowCallable c, CallContext cc) {
result = getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), c)
private UnreachableSet getUnreachable(CallContext ctx) {
exists(UnreachableSetOption unreachable | ctx = TSpecificCall(_, _, unreachable) |
result = unreachable.asSome()
)
}
private LocalCallContext getLocalCallContext(CallContext ctx) {
if exists(getUnreachable(ctx))
then result = TSpecificLocalCall(getUnreachable(ctx))
else result instanceof LocalCallContextAny
}
bindingset[cc]
pragma[inline_late]
LocalCc getLocalCc(CallContext cc) { result = getLocalCallContext(cc) }
bindingset[call, c]
CallContextCall getCallContextCall(DataFlowCall call, DataFlowCallable c) {
if recordDataFlowCallSite(call, c)
then result = TSpecificCall(call)
then result = Input2::getSpecificCallContextCall(call, c)
else result = TSomeCall()
}
}
@@ -682,16 +814,24 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
private predicate reducedViableImplInCallContextAlias = reducedViableImplInCallContext/3;
private predicate reducedViableImplInReturnAlias = reducedViableImplInReturn/2;
private predicate recordDataFlowCallSiteUnreachableAlias = recordDataFlowCallSiteUnreachable/2;
private predicate getSpecificCallContextCallAlias = getSpecificCallContextCall/2;
private predicate callContextAffectsDispatchAlias = callContextAffectsDispatch/2;
private predicate getSpecificCallContextReturnAlias = getSpecificCallContextReturn/2;
private module DefaultPrunedViableImplInput implements PrunedViableImplInputSig {
predicate reducedViableImplInCallContext = reducedViableImplInCallContextAlias/3;
predicate reducedViableImplInReturn = reducedViableImplInReturnAlias/2;
predicate recordDataFlowCallSiteUnreachable = recordDataFlowCallSiteUnreachableAlias/2;
predicate getSpecificCallContextCall = getSpecificCallContextCallAlias/2;
predicate callContextAffectsDispatch = callContextAffectsDispatchAlias/2;
predicate getSpecificCallContextReturn = getSpecificCallContextReturnAlias/2;
}
import PrunedViableImpl<DefaultPrunedViableImplInput>
@@ -888,15 +1028,36 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
Impl1::reducedViableImplInReturn(c, call)
}
cached
CcCall getSpecificCallContextCall(DataFlowCall call, DataFlowCallable c) {
result = Impl1::getSpecificCallContextCall(call, c)
}
cached
predicate callContextAffectsDispatch(DataFlowCall call, Cc ctx) {
Impl1::callContextAffectsDispatch(call, ctx)
}
cached
CcNoCall getSpecificCallContextReturn(DataFlowCallable c, DataFlowCall call) {
result = Impl1::getSpecificCallContextReturn(c, call)
}
private module PrunedViableImplInput implements Impl1::PrunedViableImplInputSig {
predicate reducedViableImplInCallContext =
CachedCallContextSensitivity::reducedViableImplInCallContext/3;
predicate reducedViableImplInReturn =
CachedCallContextSensitivity::reducedViableImplInReturn/2;
predicate recordDataFlowCallSiteUnreachable =
CachedCallContextSensitivity::recordDataFlowCallSiteUnreachable/2;
predicate getSpecificCallContextCall =
CachedCallContextSensitivity::getSpecificCallContextCall/2;
predicate callContextAffectsDispatch =
CachedCallContextSensitivity::callContextAffectsDispatch/2;
predicate getSpecificCallContextReturn =
CachedCallContextSensitivity::getSpecificCallContextReturn/2;
}
private module Impl2 = Impl1::PrunedViableImpl<PrunedViableImplInput>;
@@ -904,14 +1065,21 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
import Impl2
cached
DataFlowCallable viableImplCallContextReduced(DataFlowCall call, CallContextCall ctx) {
predicate instanceofCc(Cc cc) { any() }
cached
predicate instanceofCcCall(CcCall cc) { any() }
cached
predicate instanceofCcNoCall(CcNoCall cc) { any() }
cached
DataFlowCallable viableImplCallContextReduced(DataFlowCall call, CcCall ctx) {
result = Impl2::viableImplCallContextReduced(call, ctx)
}
cached
DataFlowCall viableImplCallContextReducedReverse(
DataFlowCallable callable, CallContextNoCall ctx
) {
DataFlowCall viableImplCallContextReducedReverse(DataFlowCallable callable, CcNoCall ctx) {
result = Impl2::viableImplCallContextReducedReverse(callable, ctx)
}
}
@@ -1314,16 +1482,62 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
ContentApprox getContentApproxCached(Content c) { result = getContentApprox(c) }
cached
newtype TCallContext =
TAnyCallContext() or
TSpecificCall(DataFlowCall call) {
CachedCallContextSensitivity::recordDataFlowCallSite(call, _)
} or
TSomeCall() or
TReturn(DataFlowCallable c, DataFlowCall call) {
CachedCallContextSensitivity::reducedViableImplInReturn(c, call)
newtype TCallEdge =
TMkCallEdge(DataFlowCall call, DataFlowCallable tgt) { viableCallableExt(call) = tgt }
cached
int edgeOrder(TCallEdge edge) {
edge =
rank[result](TCallEdge e, DataFlowCall call, DataFlowCallable tgt |
e = TMkCallEdge(call, tgt)
|
e order by call.totalorder(), tgt.totalorder()
)
}
cached
int callOrder(DataFlowCall call) { result = call.totalorder() }
private module UnreachableSetsInput implements MkSetsInp {
class Key = TCallEdge;
class Value = NodeRegion;
NodeRegion getAValue(TCallEdge edge) {
exists(DataFlowCall call, DataFlowCallable tgt |
edge = TMkCallEdge(call, tgt) and
getNodeRegionEnclosingCallable(result) = tgt and
isUnreachableInCallCached(result, call)
)
}
int totalorder(NodeRegion nr) { result = nr.totalOrder() }
}
private module UnreachableSets = MakeSets<UnreachableSetsInput>;
/** A set of nodes that is unreachable in some call context. */
cached
class UnreachableSet instanceof UnreachableSets::ValueSet {
cached
string toString() { result = "Unreachable" }
cached
predicate contains(Node n) { exists(NodeRegion nr | super.contains(nr) and nr.contains(n)) }
cached
DataFlowCallable getEnclosingCallable() {
exists(NodeRegion nr | super.contains(nr) and result = getNodeRegionEnclosingCallable(nr))
}
}
cached
UnreachableSet getUnreachableSet(TCallEdge edge) { result = UnreachableSets::getValueSet(edge) }
private module UnreachableSetOption = Option<UnreachableSet>;
class UnreachableSetOption = UnreachableSetOption::Option;
cached
newtype TReturnPosition =
TReturnPosition0(DataFlowCallable c, ReturnKindExt kind) {
@@ -1808,76 +2022,6 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
string toString() { if this.isSome() then result = "Some(..)" else result = "None()" }
}
/**
* A call context to restrict the targets of virtual dispatch, prune local flow,
* and match the call sites of flow into a method with flow out of a method.
*
* There are four cases:
* - `TAnyCallContext()` : No restrictions on method flow.
* - `TSpecificCall(DataFlowCall call)` : Flow entered through the
* given `call`. This call improves the set of viable
* dispatch targets for at least one method call in the current callable
* or helps prune unreachable nodes in the current callable.
* - `TSomeCall()` : Flow entered through a parameter. The
* originating call does not improve the set of dispatch targets for any
* method call in the current callable and was therefore not recorded.
* - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and
* this dispatch target of `call` implies a reduced set of dispatch origins
* to which data may flow if it should reach a `return` statement.
*/
abstract private class CallContext extends TCallContext {
abstract string toString();
/** Holds if this call context is relevant for `callable`. */
abstract predicate relevantFor(DataFlowCallable callable);
}
abstract private class CallContextNoCall extends CallContext { }
private class CallContextAny extends CallContextNoCall, TAnyCallContext {
override string toString() { result = "CcAny" }
override predicate relevantFor(DataFlowCallable callable) { any() }
}
abstract private class CallContextCall extends CallContext {
/** Holds if this call context may be `call`. */
bindingset[call]
abstract predicate matchesCall(DataFlowCall call);
}
private class CallContextSpecificCall extends CallContextCall, TSpecificCall {
override string toString() {
exists(DataFlowCall call | this = TSpecificCall(call) | result = "CcCall(" + call + ")")
}
override predicate relevantFor(DataFlowCallable callable) {
CachedCallContextSensitivity::recordDataFlowCallSite(this.getCall(), callable)
}
override predicate matchesCall(DataFlowCall call) { call = this.getCall() }
DataFlowCall getCall() { this = TSpecificCall(result) }
}
private class CallContextSomeCall extends CallContextCall, TSomeCall {
override string toString() { result = "CcSomeCall" }
override predicate relevantFor(DataFlowCallable callable) { any() }
override predicate matchesCall(DataFlowCall call) { any() }
}
private class CallContextReturn extends CallContextNoCall, TReturn {
override string toString() {
exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
}
override predicate relevantFor(DataFlowCallable callable) {
exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable))
}
}
/**
* A call context that is relevant for pruning local flow.
*/
@@ -1897,42 +2041,22 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall {
LocalCallContextSpecificCall() { this = TSpecificLocalCall(ns) }
UnreachableSets::ValueSet ns;
UnreachableSet ns;
override string toString() { result = "LocalCcCall" }
override predicate relevantFor(DataFlowCallable callable) {
exists(NodeRegion nr | ns.contains(nr) and callable = getNodeRegionEnclosingCallable(nr))
ns.getEnclosingCallable() = callable
}
/** Holds if this call context makes `n` unreachable. */
predicate unreachable(Node n) { exists(NodeRegion nr | ns.contains(nr) and nr.contains(n)) }
predicate unreachable(Node n) { ns.contains(n) }
}
private DataFlowCallable getNodeRegionEnclosingCallable(NodeRegion nr) {
exists(Node n | nr.contains(n) | getNodeEnclosingCallable(n) = result)
}
private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) {
exists(NodeRegion nr |
getNodeRegionEnclosingCallable(nr) = callable and isUnreachableInCallCached(nr, call)
)
}
/**
* Gets the local call context given the call context and the callable that
* the contexts apply to.
*/
private LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) {
ctx.relevantFor(callable) and
if relevantLocalCCtx(ctx.(CallContextSpecificCall).getCall(), callable)
then
result =
TSpecificLocalCall(UnreachableSets::getValueSet(TMkCallEdge(ctx.(CallContextSpecificCall)
.getCall(), callable)))
else result instanceof LocalCallContextAny
}
/**
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.