From 02a81a0897aba3813725206045341bfff7689712 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 29 Jan 2025 11:18:25 +0100 Subject: [PATCH 01/14] Dataflow: Rename signature to preempt name clash. --- .../dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll index debaa1fa5242..6ebb914e3def 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll @@ -67,7 +67,7 @@ module MakeImplCommon Lang> { /** * Holds if `source` is a relevant data flow source. */ - signature predicate sourceNode(Node source); + signature predicate sourceNodeSig(Node source); /** * EXPERIMENTAL: This API is subject to change without notice. @@ -75,7 +75,7 @@ module MakeImplCommon Lang> { * Given a source definition, this constructs a simple forward flow * computation with an access path limit of 1. */ - module SimpleGlobal { + module SimpleGlobal { import TypeTracking::TypeTrack } } From 04db61a0fe4d5c0914c283746cd08bb998a22d9b Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 29 Jan 2025 11:39:14 +0100 Subject: [PATCH 02/14] Dataflow: Move Stage1 to its own file. Stick flow exploration in there as well. --- shared/dataflow/codeql/dataflow/DataFlow.qll | 17 +- .../codeql/dataflow/TaintTracking.qll | 34 +- .../codeql/dataflow/internal/DataFlowImpl.qll | 7571 ++++++----------- .../dataflow/internal/DataFlowImplStage1.qll | 2280 +++++ 4 files changed, 5076 insertions(+), 4826 deletions(-) create mode 100644 shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll diff --git a/shared/dataflow/codeql/dataflow/DataFlow.qll b/shared/dataflow/codeql/dataflow/DataFlow.qll index 0b6ed84da365..b74ea9a0e082 100644 --- a/shared/dataflow/codeql/dataflow/DataFlow.qll +++ b/shared/dataflow/codeql/dataflow/DataFlow.qll @@ -643,6 +643,7 @@ private module PathGraphSigMod { module DataFlowMake Lang> { private import Lang private import internal.DataFlowImpl::MakeImpl + private import internal.DataFlowImplStage1::MakeImplStage1 import Configs /** @@ -700,7 +701,13 @@ module DataFlowMake Lang> { } } - import Impl + private module Stage1 = ImplStage1; + + import Stage1::PartialFlow + + private module Flow = Impl; + + import Flow } /** @@ -723,7 +730,13 @@ module DataFlowMake Lang> { } } - import Impl + private module Stage1 = ImplStage1; + + import Stage1::PartialFlow + + private module Flow = Impl; + + import Flow } signature class PathNodeSig { diff --git a/shared/dataflow/codeql/dataflow/TaintTracking.qll b/shared/dataflow/codeql/dataflow/TaintTracking.qll index 491d7794382b..762583528d48 100644 --- a/shared/dataflow/codeql/dataflow/TaintTracking.qll +++ b/shared/dataflow/codeql/dataflow/TaintTracking.qll @@ -5,6 +5,7 @@ private import DataFlow as DF private import internal.DataFlowImpl +private import internal.DataFlowImplStage1 private import codeql.util.Location /** @@ -47,6 +48,7 @@ module TaintFlowMake< private import TaintTrackingLang private import DF::DataFlowMake as DataFlow private import MakeImpl as DataFlowInternal + private import MakeImplStage1 as DataFlowInternalStage1 private module AddTaintDefaults implements DataFlowInternal::FullStateConfigSig @@ -94,7 +96,13 @@ module TaintFlowMake< import AddTaintDefaults } - import DataFlowInternal::Impl + private module Stage1 = DataFlowInternalStage1::ImplStage1; + + import Stage1::PartialFlow + + private module Flow = DataFlowInternal::Impl; + + import Flow } /** @@ -122,7 +130,13 @@ module TaintFlowMake< import AddTaintDefaults } - import DataFlowInternal::Impl + private module Stage1 = DataFlowInternalStage1::ImplStage1; + + import Stage1::PartialFlow + + private module Flow = DataFlowInternal::Impl; + + import Flow } signature int speculationLimitSig(); @@ -218,7 +232,13 @@ module TaintFlowMake< import AddTaintDefaults> } - import DataFlowInternal::Impl + private module Stage1 = DataFlowInternalStage1::ImplStage1; + + import Stage1::PartialFlow + + private module Flow = DataFlowInternal::Impl; + + import Flow } /** @@ -250,6 +270,12 @@ module TaintFlowMake< import AddTaintDefaults> } - import DataFlowInternal::Impl + private module Stage1 = DataFlowInternalStage1::ImplStage1; + + import Stage1::PartialFlow + + private module Flow = DataFlowInternal::Impl; + + import Flow } } diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll index 2b69e583d28c..9cfc73ce3e56 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll @@ -9,10 +9,12 @@ private import codeql.util.Option private import codeql.util.Boolean private import codeql.util.Location private import codeql.dataflow.DataFlow +private import DataFlowImplStage1 module MakeImpl Lang> { private import Lang private import DataFlowMake + private import MakeImplStage1 private import DataFlowImplCommon::MakeImplCommon private import DataFlowImplCommonPublic @@ -169,4055 +171,2596 @@ module MakeImpl Lang> { /** * Constructs a data flow computation given a full input configuration. */ - module Impl { + module Impl Stage1> { private class FlowState = Config::FlowState; - private module SourceSinkFiltering { - private import codeql.util.AlertFiltering + private predicate inBarrier = Stage1::inBarrier/2; - private module AlertFiltering = AlertFilteringImpl; + private predicate outBarrier = Stage1::outBarrier/2; - pragma[nomagic] - private predicate isFilteredSource(Node source) { - Config::isSource(source, _) and - if Config::observeDiffInformedIncrementalMode() - then AlertFiltering::filterByLocation(Config::getASelectedSourceLocation(source)) - else any() - } - - pragma[nomagic] - private predicate isFilteredSink(Node sink) { - ( - Config::isSink(sink, _) or - Config::isSink(sink) - ) and - if Config::observeDiffInformedIncrementalMode() - then AlertFiltering::filterByLocation(Config::getASelectedSinkLocation(sink)) - else any() - } + private predicate stateBarrier = Stage1::stateBarrier/2; - private predicate hasFilteredSource() { isFilteredSource(_) } + private predicate sourceNode = Stage1::sourceNode/2; - private predicate hasFilteredSink() { isFilteredSink(_) } + private predicate sinkNode = Stage1::sinkNode/2; - predicate isRelevantSource(Node source, FlowState state) { - // If there are filtered sinks, we need to pass through all sources to preserve all alerts - // with filtered sinks. Otherwise the only alerts of interest are those with filtered - // sources, so we can perform the source filtering right here. - Config::isSource(source, state) and - ( - isFilteredSource(source) or - hasFilteredSink() - ) - } + private predicate hasSourceCallCtx = Stage1::hasSourceCallCtx/0; - predicate isRelevantSink(Node sink, FlowState state) { - // If there are filtered sources, we need to pass through all sinks to preserve all alerts - // with filtered sources. Otherwise the only alerts of interest are those with filtered - // sinks, so we can perform the sink filtering right here. - Config::isSink(sink, state) and - ( - isFilteredSink(sink) or - hasFilteredSource() - ) - } + private predicate hasSinkCallCtx = Stage1::hasSinkCallCtx/0; - predicate isRelevantSink(Node sink) { - // If there are filtered sources, we need to pass through all sinks to preserve all alerts - // with filtered sources. Otherwise the only alerts of interest are those with filtered - // sinks, so we can perform the sink filtering right here. - Config::isSink(sink) and - ( - isFilteredSink(sink) or - hasFilteredSource() - ) - } + private predicate jumpStepEx = Stage1::jumpStepEx/2; - bindingset[source, sink] - pragma[inline_late] - predicate isRelevantSourceSinkPair(Node source, Node sink) { - isFilteredSource(source) or - isFilteredSink(sink) - } - } + private predicate additionalJumpStep = Stage1::additionalJumpStep/3; - private import SourceSinkFiltering + private predicate additionalJumpStateStep = Stage1::additionalJumpStateStep/5; - private predicate inBarrier(NodeEx node) { - exists(Node n | - node.asNode() = n and - Config::isBarrierIn(n) and - isRelevantSource(n, _) - ) - } + private predicate localStepNodeCand1 = Stage1::localStepNodeCand1/6; - pragma[nomagic] - private predicate inBarrier(NodeEx node, FlowState state) { - exists(Node n | - node.asNode() = n and - Config::isBarrierIn(n, state) and - isRelevantSource(n, state) - ) - } + private predicate localStateStepNodeCand1 = Stage1::localStateStepNodeCand1/7; - private predicate outBarrier(NodeEx node) { - exists(Node n | - node.asNodeOrImplicitRead() = n and - Config::isBarrierOut(n) - | - isRelevantSink(n, _) + private predicate sourceModel(NodeEx node, string model) { + sourceNode(node, _) and + ( + model = getSourceModel(node) or - isRelevantSink(n) + not exists(getSourceModel(node)) and model = "" ) } - pragma[nomagic] - private predicate outBarrier(NodeEx node, FlowState state) { - exists(Node n | - node.asNodeOrImplicitRead() = n and - Config::isBarrierOut(n, state) - | - isRelevantSink(n, state) + private predicate sinkModel(NodeEx node, string model) { + sinkNode(node, _) and + ( + model = getSinkModel(node) or - isRelevantSink(n) + not exists(getSinkModel(node)) and model = "" ) } - pragma[nomagic] - private predicate fullBarrier(NodeEx node) { - exists(Node n | node.asNode() = n | - Config::isBarrier(n) - or - Config::isBarrierIn(n) and - not isRelevantSource(n, _) - or - Config::isBarrierOut(n) and - not isRelevantSink(n, _) and - not isRelevantSink(n) - ) + bindingset[label1, label2] + pragma[inline_late] + private string mergeLabels(string label1, string label2) { + if label2.matches("Sink:%") + then if label1 = "" then result = label2 else result = label1 + " " + label2 + else + // Big-step, hidden nodes, and summaries all may need to merge labels. + // These cases are expected to involve at most one non-empty label, so + // we'll just discard the 2nd+ label for now. + if label1 = "" + then result = label2 + else result = label1 } pragma[nomagic] - private predicate stateBarrier(NodeEx node, FlowState state) { - exists(Node n | node.asNode() = n | - Config::isBarrier(n, state) - or - Config::isBarrierIn(n, state) and - not isRelevantSource(n, state) - or - Config::isBarrierOut(n, state) and - not isRelevantSink(n, state) and - not isRelevantSink(n) - ) + private predicate allowsFieldFlowThrough(DataFlowCall call, DataFlowCallable c) { + Stage1::callEdgeReturn(call, c, _, _, _, true) } - pragma[nomagic] - private predicate sourceNode(NodeEx node, FlowState state) { - isRelevantSource(node.asNode(), state) and - not fullBarrier(node) and - not stateBarrier(node, state) - } + private signature module StageSig { + class Ap; - pragma[nomagic] - private predicate sinkNodeWithState(NodeEx node, FlowState state) { - isRelevantSink(node.asNodeOrImplicitRead(), state) and - not fullBarrier(node) and - not stateBarrier(node, state) - } + class ApNil extends Ap; - /** Provides the relevant barriers for a step from `node1` to `node2`. */ - bindingset[node1, node2] - private predicate stepFilter(NodeEx node1, NodeEx node2) { - not outBarrier(node1) and - not inBarrier(node2) and - not fullBarrier(node1) and - not fullBarrier(node2) - } + predicate revFlow(NodeEx node); - /** Provides the relevant barriers for a step from `node1,state1` to `node2,state2`, including stateless barriers for `node1` to `node2`. */ - bindingset[node1, state1, node2, state2] - private predicate stateStepFilter(NodeEx node1, FlowState state1, NodeEx node2, FlowState state2) { - stepFilter(node1, node2) and - not outBarrier(node1, state1) and - not inBarrier(node2, state2) and - not stateBarrier(node1, state1) and - not stateBarrier(node2, state2) - } + bindingset[node, state] + predicate revFlow(NodeEx node, FlowState state, Ap ap); - bindingset[n, cc] - pragma[inline_late] - private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) { - cc.unreachable(n) - } + predicate callMayFlowThroughRev(DataFlowCall call); - /** - * Holds if data can flow in one local step from `node1` to `node2`. - */ - private predicate localFlowStepEx(NodeEx node1, NodeEx node2, string model) { - localFlowStepExImpl(node1, node2, model) and - stepFilter(node1, node2) - } + predicate parameterMayFlowThrough(ParamNodeEx p, boolean emptyAp); - /** - * Holds if the additional step from `node1` to `node2` does not jump between callables. - */ - private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, string model) { - exists(Node n1, Node n2 | - node1.asNodeOrImplicitRead() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2), model) and - getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) - ) - } + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind); - private predicate additionalLocalStateStep( - NodeEx node1, FlowState s1, NodeEx node2, FlowState s2, string model - ) { - exists(Node n1, Node n2 | - node1.asNodeOrImplicitRead() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2, - model) and - getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and - stateStepFilter(node1, s1, node2, s2) - ) - } + predicate storeStepCand( + NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, DataFlowType containerType + ); - /** - * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. - */ - private predicate jumpStepEx(NodeEx node1, NodeEx node2) { - exists(Node n1, Node n2 | - node1.asNode() = n1 and - node2.asNode() = n2 and - jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and - stepFilter(node1, node2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } + predicate readStepCand(NodeEx n1, Content c, NodeEx n2); - /** - * Holds if the additional step from `node1` to `node2` jumps between callables. - */ - private predicate additionalJumpStep(NodeEx node1, NodeEx node2, string model) { - exists(Node n1, Node n2 | - node1.asNodeOrImplicitRead() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2), model) and - getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and - stepFilter(node1, node2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } + predicate callEdgeArgParam( + DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, boolean emptyAp + ); - private predicate additionalJumpStateStep( - NodeEx node1, FlowState s1, NodeEx node2, FlowState s2, string model - ) { - exists(Node n1, Node n2 | - node1.asNodeOrImplicitRead() = n1 and - node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2, - model) and - getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and - stateStepFilter(node1, s1, node2, s2) and - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - ) - } + predicate callEdgeReturn( + DataFlowCall call, DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow + ); - pragma[nomagic] - private predicate readSetEx(NodeEx node1, ContentSet c, NodeEx node2) { - readEx(node1, c, node2) and - stepFilter(node1, node2) - or - exists(Node n | - node2.isImplicitReadNode(n) and - Config::allowImplicitRead(n, c) - | - node1.asNode() = n and - not fullBarrier(node1) - or - node1.isImplicitReadNode(n) - ) - } + predicate relevantCallEdgeIn(DataFlowCall call, DataFlowCallable c); - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate read(NodeEx node1, Content c, NodeEx node2) { - exists(ContentSet cs | - readSetEx(node1, cs, node2) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) + predicate relevantCallEdgeOut(DataFlowCall call, DataFlowCallable c); } - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate expectsContentEx(NodeEx n, Content c) { - exists(ContentSet cs | - expectsContentSet(n, cs) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) - } + private module MkStage { + class ApApprox = PrevStage::Ap; - pragma[nomagic] - private predicate notExpectsContent(NodeEx n) { not expectsContentSet(n, _) } + signature module StageParam { + class Typ { + string toString(); + } - pragma[nomagic] - private predicate storeUnrestricted( - NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, DataFlowType containerType - ) { - storeEx(node1, c, node2, contentType, containerType) and - stepFilter(node1, node2) - } + class Ap { + string toString(); + } - pragma[nomagic] - private predicate hasReadStep(Content c) { read(_, c, _) } + class ApNil extends Ap; - pragma[nomagic] - private predicate store( - NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, DataFlowType containerType - ) { - storeUnrestricted(node1, c, node2, contentType, containerType) and - hasReadStep(c) - } + bindingset[result, ap] + ApApprox getApprox(Ap ap); - /** - * Holds if field flow should be used for the given configuration. - */ - private predicate useFieldFlow() { - Config::fieldFlowBranchLimit() >= 1 and Config::accessPathLimit() > 0 - } + Typ getTyp(DataFlowType t); - private predicate hasSourceCallCtx() { - exists(FlowFeature feature | feature = Config::getAFeature() | - feature instanceof FeatureHasSourceCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) - } + bindingset[c, tail] + Ap apCons(Content c, Ap tail); - private predicate hasSinkCallCtx() { - exists(FlowFeature feature | feature = Config::getAFeature() | - feature instanceof FeatureHasSinkCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) - } + /** + * An approximation of `Content` that corresponds to the precision level of + * `Ap`, such that the mappings from both `Ap` and `Content` to this type + * are functional. + */ + class ApHeadContent; - /** - * Holds if flow from `p` to a return node of kind `kind` is allowed. - * - * We don't expect a parameter to return stored in itself, unless - * explicitly allowed - */ - bindingset[p, kind] - private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) { - exists(ParameterPosition pos | p.isParameterOf(_, pos) | - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - allowParameterReturnInSelfEx(p) - ) - } + ApHeadContent getHeadContent(Ap ap); - private module Stage1 implements StageSig { - class Ap = Unit; + ApHeadContent projectToHeadContent(Content c); - class ApNil = Ap; + class ApOption; - private class Cc = boolean; + ApOption apNone(); - /* Begin: Stage 1 logic. */ - /** - * Holds if `node` is reachable from a source. - * - * The Boolean `cc` records whether the node is reached through an - * argument in a call. - */ - private predicate fwdFlow(NodeEx node, Cc cc) { - sourceNode(node, _) and - if hasSourceCallCtx() then cc = true else cc = false - or - exists(NodeEx mid | fwdFlow(mid, cc) | - localFlowStepEx(mid, node, _) or - additionalLocalFlowStep(mid, node, _) or - additionalLocalStateStep(mid, _, node, _, _) - ) - or - exists(NodeEx mid | fwdFlow(mid, _) and cc = false | - jumpStepEx(mid, node) or - additionalJumpStep(mid, node, _) or - additionalJumpStateStep(mid, _, node, _, _) - ) - or - // store - exists(NodeEx mid | - useFieldFlow() and - fwdFlow(mid, cc) and - store(mid, _, node, _, _) - ) - or - // read - exists(ContentSet c | - fwdFlowReadSet(c, node, cc) and - fwdFlowConsCandSet(c, _) - ) - or - // flow into a callable - fwdFlowIn(_, _, _, node) and - cc = true - or - // flow out of a callable - fwdFlowOut(_, _, node, false) and - cc = false - or - // flow through a callable - exists(DataFlowCall call, ReturnKindExtOption kind, ReturnKindExtOption disallowReturnKind | - fwdFlowOutFromArg(call, kind, node) and - fwdFlowIsEntered(call, disallowReturnKind, cc) and - kind != disallowReturnKind - ) - } + ApOption apSome(Ap ap); - // inline to reduce the number of iterations - pragma[inline] - private predicate fwdFlowIn(DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p) { - // call context cannot help reduce virtual dispatch - fwdFlow(arg, cc) and - viableParamArgEx(call, p, arg) and - not fullBarrier(p) and - ( - cc = false - or - cc = true and - not CachedCallContextSensitivity::reducedViableImplInCallContext(call, _, _) - ) - or - // call context may help reduce virtual dispatch - exists(DataFlowCallable target | - fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target) and - target = viableImplInSomeFwdFlowCallContextExt(call) and - cc = true - ) - } + class Cc { + string toString(); + } - pragma[nomagic] - private ReturnKindExtOption getDisallowedReturnKind0(ParamNodeEx p) { - if allowParameterReturnInSelfEx(p) - then result.isNone() - else p.isParameterOf(_, result.asSome().(ParamUpdateReturnKind).getPosition()) - } + class CcCall extends Cc; - bindingset[p] - pragma[inline_late] - private ReturnKindExtOption getDisallowedReturnKind(ParamNodeEx p) { - result = getDisallowedReturnKind0(p) - } + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, ReturnKindExtOption disallowReturnKind, Cc cc - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, _, cc, p) and - disallowReturnKind = getDisallowedReturnKind(p) - ) - } + class CcNoCall extends Cc; - pragma[nomagic] - private predicate fwdFlowInReducedViableImplInSomeCallContext( - DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target - ) { - fwdFlow(arg, true) and - viableParamArgEx(call, p, arg) and - CachedCallContextSensitivity::reducedViableImplInCallContext(call, _, _) and - target = p.getEnclosingCallable() and - not fullBarrier(p) - } + Cc ccNone(); - /** - * Gets a viable dispatch target of `call` in the context `ctx`. This is - * restricted to those `call`s for which a context might make a difference, - * and to `ctx`s that are reachable in `fwdFlow`. - */ - pragma[nomagic] - private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(DataFlowCall call) { - exists(DataFlowCall ctx | - fwdFlowIsEntered(ctx, _, _) and - result = viableImplInCallContextExt(call, ctx) - ) - } + CcCall ccSomeCall(); - private predicate fwdFlow(NodeEx node) { fwdFlow(node, _) } + /* + * The following `instanceof` predicates are necessary for proper + * caching, since we're able to cache predicates, but not the underlying + * types. + */ - pragma[nomagic] - private predicate fwdFlowReadSet(ContentSet c, NodeEx node, Cc cc) { - exists(NodeEx mid | - fwdFlow(mid, cc) and - readSetEx(mid, c, node) - ) - } + predicate instanceofCc(Cc cc); - /** - * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Content c) { - exists(NodeEx mid, NodeEx node | - not fullBarrier(node) and - useFieldFlow() and - fwdFlow(mid, _) and - store(mid, c, node, _, _) - ) - } + predicate instanceofCcCall(CcCall cc); - /** - * Holds if `cs` may be interpreted in a read as the target of some store - * into `c`, in the flow covered by `fwdFlow`. - */ - pragma[nomagic] - private predicate fwdFlowConsCandSet(ContentSet cs, Content c) { - fwdFlowConsCand(c) and - c = cs.getAReadContent() - } + predicate instanceofCcNoCall(CcNoCall cc); - pragma[nomagic] - private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc) { - exists(RetNodeEx ret | - fwdFlow(ret, cc) and - ret.getReturnPosition() = pos - ) - } + class LocalCc; - // inline to reduce the number of iterations - pragma[inline] - private predicate fwdFlowOut(DataFlowCall call, ReturnKindExt kind, NodeEx out, Cc cc) { - exists(ReturnPosition pos | - fwdFlowReturnPosition(pos, cc) and - viableReturnPosOutEx(call, pos, out) and - not fullBarrier(out) and - kind = pos.getKind() - ) - } + DataFlowCallable viableImplCallContextReduced(DataFlowCall call, CcCall ctx); - pragma[nomagic] - private predicate fwdFlowOutFromArg(DataFlowCall call, ReturnKindExtOption kind, NodeEx out) { - fwdFlowOut(call, kind.asSome(), out, true) - } + bindingset[call, ctx] + predicate viableImplNotCallContextReduced(DataFlowCall call, Cc ctx); - private predicate stateStepFwd(FlowState state1, FlowState state2) { - exists(NodeEx node1 | - additionalLocalStateStep(node1, state1, _, state2, _) or - additionalJumpStateStep(node1, state1, _, state2, _) - | - fwdFlow(node1) - ) - } + bindingset[call, c] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c); - private predicate fwdFlowState(FlowState state) { - sourceNode(_, state) - or - exists(FlowState state0 | - fwdFlowState(state0) and - stateStepFwd(state0, state) - ) - } + DataFlowCall viableImplCallContextReducedReverse(DataFlowCallable c, CcNoCall ctx); - additional predicate sinkNode(NodeEx node, FlowState state) { - fwdFlow(pragma[only_bind_into](node)) and - fwdFlowState(state) and - isRelevantSink(node.asNodeOrImplicitRead()) - or - fwdFlow(node) and - fwdFlowState(state) and - sinkNodeWithState(node, state) - } + predicate viableImplNotCallContextReducedReverse(CcNoCall ctx); - /** - * Holds if `node` is part of a path from a source to a sink. - * - * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink. - */ - pragma[nomagic] - additional predicate revFlow(NodeEx node, boolean toReturn) { - revFlow0(node, toReturn) and - fwdFlow(node) - } + bindingset[call, c] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call); - pragma[nomagic] - private predicate revFlow0(NodeEx node, boolean toReturn) { - sinkNode(node, _) and - if hasSinkCallCtx() then toReturn = true else toReturn = false - or - exists(NodeEx mid | revFlow(mid, toReturn) | - localFlowStepEx(node, mid, _) or - additionalLocalFlowStep(node, mid, _) or - additionalLocalStateStep(node, _, mid, _, _) - ) - or - exists(NodeEx mid | revFlow(mid, _) and toReturn = false | - jumpStepEx(node, mid) or - additionalJumpStep(node, mid, _) or - additionalJumpStateStep(node, _, mid, _, _) - ) - or - // store - exists(Content c | - revFlowStore(c, node, toReturn) and - revFlowConsCand(c) - ) - or - // read - exists(NodeEx mid, ContentSet c | - readSetEx(node, c, mid) and - fwdFlowConsCandSet(c, _) and - revFlow(mid, toReturn) - ) - or - // flow into a callable - revFlowIn(_, _, node, false) and - toReturn = false - or - // flow out of a callable - exists(ReturnPosition pos | - revFlowOut(pos) and - node.(RetNodeEx).getReturnPosition() = pos and - toReturn = true - ) - or - // flow through a callable - exists(DataFlowCall call, ReturnKindExtOption kind, ReturnKindExtOption disallowReturnKind | - revFlowIsReturned(call, kind, toReturn) and - revFlowInToReturn(call, disallowReturnKind, node) and - kind != disallowReturnKind - ) - } - - /** - * Holds if `c` is the target of a read in the flow covered by `revFlow`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Content c) { - exists(NodeEx mid, NodeEx node, ContentSet cs | - fwdFlow(node) and - readSetEx(node, cs, mid) and - fwdFlowConsCandSet(cs, c) and - revFlow(pragma[only_bind_into](mid), _) - ) - } - - pragma[nomagic] - private predicate revFlowStore(Content c, NodeEx node, boolean toReturn) { - exists(NodeEx mid | - revFlow(mid, toReturn) and - fwdFlowConsCand(c) and - store(node, c, mid, _, _) - ) - } - - /** - * Holds if `c` is the target of both a read and a store in the flow covered - * by `revFlow`. - */ - pragma[nomagic] - additional predicate revFlowIsReadAndStored(Content c) { - revFlowConsCand(c) and - revFlowStore(c, _, _) - } + bindingset[cc] + LocalCc getLocalCc(Cc cc); - pragma[nomagic] - additional predicate viableReturnPosOutNodeCandFwd1( - DataFlowCall call, ReturnPosition pos, NodeEx out - ) { - fwdFlowReturnPosition(pos, _) and - viableReturnPosOutEx(call, pos, out) - } + bindingset[node1, state1] + bindingset[node2, state2] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + Typ t, LocalCc lcc, string label + ); - pragma[nomagic] - private predicate revFlowOut(ReturnPosition pos) { - exists(NodeEx out | - revFlow(out, _) and - viableReturnPosOutNodeCandFwd1(_, pos, out) - ) - } + bindingset[node, state, t0, ap] + predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t); - pragma[nomagic] - additional predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParamNodeEx p, ArgNodeEx arg - ) { - fwdFlowIn(call, arg, _, p) - } + bindingset[node, ap, isStoreStep] + predicate stepFilter(NodeEx node, Ap ap, boolean isStoreStep); - // inline to reduce the number of iterations - pragma[inline] - private predicate revFlowIn(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, boolean toReturn) { - revFlow(p, toReturn) and - viableParamArgNodeCandFwd1(call, p, arg) - } + bindingset[t1, t2] + predicate typecheck(Typ t1, Typ t2); - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ReturnKindExtOption disallowReturnKind, ArgNodeEx arg - ) { - exists(ParamNodeEx p | - revFlowIn(call, p, arg, true) and - disallowReturnKind = getDisallowedReturnKind(p) - ) + default predicate enableTypeFlow() { any() } } - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, ReturnKindExtOption kind, boolean toReturn - ) { - exists(NodeEx out | - revFlow(out, toReturn) and - fwdFlowOutFromArg(call, kind, out) - ) - } + module Stage implements StageSig { + import Param - private predicate stateStepRev(FlowState state1, FlowState state2) { - exists(NodeEx node1, NodeEx node2 | - additionalLocalStateStep(node1, state1, node2, state2, _) or - additionalJumpStateStep(node1, state1, node2, state2, _) - | - revFlow(node1, _) and - revFlow(node2, _) and - fwdFlowState(state1) and - fwdFlowState(state2) - ) - } + private module TypOption = Option; - pragma[nomagic] - additional predicate revFlowState(FlowState state) { - exists(NodeEx node | - sinkNode(node, state) and - revFlow(node, _) and - fwdFlowState(state) - ) - or - exists(FlowState state0 | - revFlowState(state0) and - stateStepRev(state, state0) - ) - } + private class TypOption = TypOption::Option; - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, DataFlowType containerType - ) { - revFlowIsReadAndStored(c) and - revFlow(node2) and - store(node1, c, node2, contentType, containerType) - } + private string ppStored(TypOption stored) { + exists(string ppt | ppt = stored.toString() | + if stored.isNone() or ppt = "" then result = "" else result = " : " + ppt + ) + } - pragma[nomagic] - predicate readStepCand(NodeEx n1, Content c, NodeEx n2) { - revFlowIsReadAndStored(c) and - read(n1, c, n2) and - revFlow(n2) - } + bindingset[ap] + private boolean isNil(Ap ap) { + if ap instanceof ApNil then result = true else result = false + } - pragma[nomagic] - predicate revFlow(NodeEx node) { revFlow(node, _) } + /* Begin: Stage logic. */ + pragma[nomagic] + private Typ getNodeTyp(NodeEx node) { + PrevStage::revFlow(node) and result = getTyp(node.getDataFlowType()) + } - bindingset[node, state] - predicate revFlow(NodeEx node, FlowState state, Ap ap) { - revFlow(node, _) and - exists(state) and - exists(ap) - } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow + ) { + exists(ReturnKindExt kind | + PrevStage::callEdgeReturn(call, _, ret, kind, out, allowsFieldFlow) and + PrevStage::callMayFlowThroughRev(call) and + PrevStage::returnMayFlowThrough(ret, kind) + ) + } - private predicate throughFlowNodeCand(NodeEx node) { - revFlow(node, true) and - fwdFlow(node, true) and - not inBarrier(node) and - not outBarrier(node) - } + pragma[nomagic] + private predicate compatibleContainer0(ApHeadContent apc, DataFlowType containerType) { + exists(DataFlowType containerType0, Content c | + PrevStage::storeStepCand(_, c, _, _, containerType0) and + not isTopType(containerType0) and + compatibleTypesCached(containerType0, containerType) and + apc = projectToHeadContent(c) + ) + } - /** Holds if flow may return from `callable`. */ - pragma[nomagic] - private predicate returnFlowCallableNodeCand(DataFlowCallable callable, ReturnKindExt kind) { - exists(RetNodeEx ret | - throughFlowNodeCand(ret) and - callable = ret.getEnclosingCallable() and - kind = ret.getKind() - ) - } + pragma[nomagic] + private predicate topTypeContent(ApHeadContent apc) { + exists(DataFlowType containerType0, Content c | + PrevStage::storeStepCand(_, c, _, _, containerType0) and + isTopType(containerType0) and + apc = projectToHeadContent(c) + ) + } - /** - * Holds if flow may enter through `p` and reach a return node making `p` a - * candidate for the origin of a summary. - */ - pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, boolean emptyAp) { - exists(DataFlowCallable c, ReturnKindExt kind | - throughFlowNodeCand(p) and - returnFlowCallableNodeCand(c, kind) and - p.getEnclosingCallable() = c and - emptyAp = [true, false] and - parameterFlowThroughAllowed(p, kind) - ) - } + bindingset[apc, containerType] + pragma[inline_late] + private predicate compatibleContainer(ApHeadContent apc, DataFlowType containerType) { + compatibleContainer0(apc, containerType) + } - pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind) { - throughFlowNodeCand(ret) and - kind = ret.getKind() - } + /** + * Holds if `node` is reachable with access path `ap` from a source. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `summaryCtx` records the + * corresponding parameter position and access path of that argument. + */ + pragma[nomagic] + additional predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored + ) { + fwdFlow1(node, state, cc, summaryCtx, _, t, ap, stored) + } - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call) { - exists( - ArgNodeEx arg, ReturnKindExtOption kind, ReturnKindExtOption disallowReturnKind, - boolean toReturn - | - revFlow(arg, pragma[only_bind_into](toReturn)) and - revFlowIsReturned(call, kind, pragma[only_bind_into](toReturn)) and - revFlowInToReturn(call, disallowReturnKind, arg) and - kind != disallowReturnKind - ) - } + private predicate fwdFlow1( + NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Typ t, Ap ap, + TypOption stored + ) { + exists(ApApprox apa | + fwdFlow0(node, state, cc, summaryCtx, t0, ap, apa, stored) and + PrevStage::revFlow(node, state, apa) and + filter(node, state, t0, ap, t) and + ( + if node instanceof CastingNodeEx + then + ap instanceof ApNil or + compatibleContainer(getHeadContent(ap), node.getDataFlowType()) or + topTypeContent(getHeadContent(ap)) + else any() + ) + ) + } - predicate callEdgeArgParam( - DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, boolean emptyAp - ) { - exists(boolean allowsFieldFlow | - flowIntoCallNodeCand1(call, arg, p, allowsFieldFlow) and - c = p.getEnclosingCallable() and - ( - emptyAp = true + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, ApApprox apa, + TypOption stored + ) { + sourceNode(node, state) and + (if hasSourceCallCtx() then cc = ccSomeCall() else cc = ccNone()) and + summaryCtx = TSummaryCtxNone() and + t = getNodeTyp(node) and + ap instanceof ApNil and + apa = getApprox(ap) and + stored.isNone() + or + exists(NodeEx mid, FlowState state0, Typ t0, LocalCc localCc | + fwdFlow(mid, state0, cc, summaryCtx, t0, ap, stored) and + apa = getApprox(ap) and + localCc = getLocalCc(cc) + | + localStep(mid, state0, node, state, true, _, localCc, _) and + t = t0 or - allowsFieldFlow = true and emptyAp = false + localStep(mid, state0, node, state, false, t, localCc, _) and + ap instanceof ApNil ) - ) - } + or + fwdFlowJump(node, state, t, ap, stored) and + apa = getApprox(ap) and + cc = ccNone() and + summaryCtx = TSummaryCtxNone() + or + // store + exists(Content c, Ap ap0 | + fwdFlowStore(_, _, ap0, _, c, t, stored, node, state, cc, summaryCtx) and + ap = apCons(c, ap0) and + apa = getApprox(ap) + ) + or + // read + fwdFlowRead(_, _, _, _, _, node, t, ap, stored, state, cc, summaryCtx) and + apa = getApprox(ap) + or + // flow into a callable without summary context + fwdFlowInNoFlowThrough(node, state, cc, t, ap, stored) and + apa = getApprox(ap) and + summaryCtx = TSummaryCtxNone() and + // When the call contexts of source and sink needs to match then there's + // never any reason to enter a callable except to find a summary. See also + // the comment in `PathNodeMid::isAtSink`. + not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext + or + // flow into a callable with summary context (non-linear recursion) + fwdFlowInFlowThrough(node, state, cc, t, ap, stored) and + apa = getApprox(ap) and + summaryCtx = TSummaryCtxSome(node, state, t, ap, stored) + or + // flow out of a callable + fwdFlowOut(_, _, node, state, cc, summaryCtx, t, ap, stored) and + apa = getApprox(ap) + or + // flow through a callable + exists(DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow | + fwdFlowThrough(call, cc, state, summaryCtx, t, ap, stored, ret) and + flowThroughOutOfCall(call, ret, node, allowsFieldFlow) and + apa = getApprox(ap) and + not inBarrier(node, state) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } - predicate callEdgeReturn( - DataFlowCall call, DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow - ) { - flowOutOfCallNodeCand1(call, ret, kind, out, allowsFieldFlow) and - c = ret.getEnclosingCallable() - } + private newtype TSummaryCtx = + TSummaryCtxNone() or + TSummaryCtxSome(ParamNodeEx p, FlowState state, Typ t, Ap ap, TypOption stored) { + fwdFlowInFlowThrough(p, state, _, t, ap, stored) + } - predicate relevantCallEdgeIn(DataFlowCall call, DataFlowCallable c) { - callEdgeArgParam(call, c, _, _, _) - } + /** + * A context for generating flow summaries. This represents flow entry through + * a specific parameter with an access path of a specific shape. + * + * Summaries are only created for parameters that may flow through. + */ + private class SummaryCtx extends TSummaryCtx { + abstract string toString(); - predicate relevantCallEdgeOut(DataFlowCall call, DataFlowCallable c) { - callEdgeReturn(call, c, _, _, _, _) - } - - additional predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, int calledges - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node)) and - fields = count(Content f0 | fwdFlowConsCand(f0)) and - conscand = -1 and - states = count(FlowState state | fwdFlowState(state)) and - tuples = count(NodeEx n, boolean b | fwdFlow(n, b)) and - calledges = -1 - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _)) and - fields = count(Content f0 | revFlowConsCand(f0)) and - conscand = -1 and - states = count(FlowState state | revFlowState(state)) and - tuples = count(NodeEx n, boolean b | revFlow(n, b)) and - calledges = - count(DataFlowCall call, DataFlowCallable c | - callEdgeArgParam(call, c, _, _, _) or - callEdgeReturn(call, c, _, _, _, _) - ) - } - /* End: Stage 1 logic. */ - } - - private predicate sinkNode = Stage1::sinkNode/2; + abstract Location getLocation(); + } - private predicate sourceModel(NodeEx node, string model) { - sourceNode(node, _) and - ( - model = getSourceModel(node) - or - not exists(getSourceModel(node)) and model = "" - ) - } + /** A summary context from which no flow summary can be generated. */ + private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { + override string toString() { result = "" } - private predicate sinkModel(NodeEx node, string model) { - sinkNode(node, _) and - ( - model = getSinkModel(node) - or - not exists(getSinkModel(node)) and model = "" - ) - } + override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) } + } - bindingset[label1, label2] - pragma[inline_late] - private string mergeLabels(string label1, string label2) { - if label2.matches("Sink:%") - then if label1 = "" then result = label2 else result = label1 + " " + label2 - else - // Big-step, hidden nodes, and summaries all may need to merge labels. - // These cases are expected to involve at most one non-empty label, so - // we'll just discard the 2nd+ label for now. - if label1 = "" - then result = label2 - else result = label1 - } + /** A summary context from which a flow summary can be generated. */ + private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { + private ParamNodeEx p; + private FlowState state; + private Typ t; + private Ap ap; + private TypOption stored; - pragma[nomagic] - private predicate localStepNodeCand1( - NodeEx node1, NodeEx node2, boolean preservesValue, DataFlowType t, LocalCallContext lcc, - string label - ) { - Stage1::revFlow(node1) and - Stage1::revFlow(node2) and - ( - preservesValue = true and - localFlowStepEx(node1, node2, label) and - t = node1.getDataFlowType() - or - preservesValue = false and - additionalLocalFlowStep(node1, node2, label) and - t = node2.getDataFlowType() - ) and - lcc.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCall1(node1, lcc) and - not isUnreachableInCall1(node2, lcc) - } + SummaryCtxSome() { this = TSummaryCtxSome(p, state, t, ap, stored) } - pragma[nomagic] - private predicate localStateStepNodeCand1( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, DataFlowType t, - LocalCallContext lcc, string label - ) { - Stage1::revFlow(node1) and - Stage1::revFlow(node2) and - additionalLocalStateStep(node1, state1, node2, state2, label) and - t = node2.getDataFlowType() and - lcc.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCall1(node1, lcc) and - not isUnreachableInCall1(node2, lcc) - } + ParamNodeEx getParamNode() { result = p } - pragma[nomagic] - private predicate viableReturnPosOutNodeCand1(DataFlowCall call, ReturnPosition pos, NodeEx out) { - Stage1::revFlow(out) and - Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out) - } + private string ppTyp() { result = t.toString() and result != "" } - /** - * Holds if data can flow out of `call` from `ret` to `out`, either - * through a `ReturnNode` or through an argument that has been mutated, and - * that this step is part of a path from a source to a sink. - */ - pragma[nomagic] - private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out - ) { - exists(ReturnPosition pos | - viableReturnPosOutNodeCand1(call, pos, out) and - pos = ret.getReturnPosition() and - kind = pos.getKind() and - Stage1::revFlow(ret) and - not outBarrier(ret) and - not inBarrier(out) - ) - } + override string toString() { + result = p + concat(" : " + this.ppTyp()) + " " + ap + ppStored(stored) + } - pragma[nomagic] - private predicate viableParamArgNodeCand1(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { - Stage1::viableParamArgNodeCandFwd1(call, p, arg) and - Stage1::revFlow(arg) - } + override Location getLocation() { result = p.getLocation() } + } - /** - * Holds if data can flow into `call` and that this step is part of a - * path from a source to a sink. - */ - pragma[nomagic] - private predicate flowIntoCallNodeCand1(DataFlowCall call, ArgNodeEx arg, ParamNodeEx p) { - viableParamArgNodeCand1(call, p, arg) and - Stage1::revFlow(p) and - not outBarrier(arg) and - not inBarrier(p) - } + private predicate fwdFlowJump(NodeEx node, FlowState state, Typ t, Ap ap, TypOption stored) { + exists(NodeEx mid | + fwdFlow(mid, state, _, _, t, ap, stored) and + jumpStepEx(mid, node) + ) + or + exists(NodeEx mid | + fwdFlow(mid, state, _, _, _, ap, stored) and + additionalJumpStep(mid, node, _) and + t = getNodeTyp(node) and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0 | + fwdFlow(mid, state0, _, _, _, ap, stored) and + additionalJumpStateStep(mid, state0, node, state, _) and + t = getNodeTyp(node) and + ap instanceof ApNil + ) + } - /** - * Gets an additional term that is added to `branch` and `join` when deciding whether - * the amount of forward or backward branching is within the limit specified by the - * configuration. - */ - pragma[nomagic] - private int getLanguageSpecificFlowIntoCallNodeCand1(ArgNodeEx arg, ParamNodeEx p) { - flowIntoCallNodeCand1(_, arg, p) and - result = getAdditionalFlowIntoCallNodeTerm(arg.projectToNode(), p.projectToNode()) - } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Typ t1, Ap ap1, TypOption stored1, Content c, Typ t2, TypOption stored2, + NodeEx node2, FlowState state, Cc cc, SummaryCtx summaryCtx + ) { + exists(DataFlowType contentType, DataFlowType containerType | + fwdFlow(node1, state, cc, summaryCtx, t1, ap1, stored1) and + not outBarrier(node1, state) and + not inBarrier(node2, state) and + PrevStage::storeStepCand(node1, c, node2, contentType, containerType) and + t2 = getTyp(containerType) and + // We need to typecheck stores here, since reverse flow through a getter + // might have a different type here compared to inside the getter. + typecheck(t1, getTyp(contentType)) and + if ap1 instanceof ApNil then stored2.asSome() = t1 else stored2 = stored1 + ) + } - pragma[nomagic] - private predicate returnCallEdge1( - DataFlowCallable c, SndLevelScopeOption scope, DataFlowCall call, NodeEx out - ) { - exists(RetNodeEx ret | - flowOutOfCallNodeCand1(call, ret, _, out) and - c = ret.getEnclosingCallable() - | - scope = getSecondLevelScopeEx(ret) - or - ret = TParamReturnNode(_, scope) - ) - } + /** + * Holds if forward flow with access path `tail` and type `t1` reaches a + * store of `c` on a container of type `t2` resulting in access path + * `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Typ t2, Ap cons, Content c, Typ t1, Ap tail) { + fwdFlowStore(_, t1, tail, _, c, t2, _, _, _, _, _) and + cons = apCons(c, tail) + } - private int simpleDispatchFanoutOnReturn(DataFlowCall call, NodeEx out) { - result = - strictcount(DataFlowCallable c, SndLevelScopeOption scope | - returnCallEdge1(c, scope, call, out) - ) - } + pragma[nomagic] + private predicate readStepCand(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { + PrevStage::readStepCand(node1, c, node2) and + apc = projectToHeadContent(c) + } - pragma[nomagic] - private predicate returnCallEdgeInCtx1( - DataFlowCallable c, SndLevelScopeOption scope, DataFlowCall call, NodeEx out, DataFlowCall ctx - ) { - returnCallEdge1(c, scope, call, out) and - c = viableImplInCallContextExt(call, ctx) - } + bindingset[node1, apc] + pragma[inline_late] + private predicate readStepCand0(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { + readStepCand(node1, apc, c, node2) + } - private int ctxDispatchFanoutOnReturn(NodeEx out, DataFlowCall ctx) { - exists(DataFlowCall call, DataFlowCallable c | - simpleDispatchFanoutOnReturn(call, out) > 1 and - not Stage1::revFlow(out, false) and - call.getEnclosingCallable() = c and - returnCallEdge1(c, _, ctx, _) and - mayBenefitFromCallContextExt(call, _) and - result = - count(DataFlowCallable tgt, SndLevelScopeOption scope | - returnCallEdgeInCtx1(tgt, scope, call, out, ctx) + pragma[nomagic] + private predicate fwdFlowRead0( + Typ t, Ap ap, TypOption stored, Content c, NodeEx node1, NodeEx node2, FlowState state, + Cc cc, SummaryCtx summaryCtx + ) { + exists(ApHeadContent apc | + fwdFlow(node1, state, cc, summaryCtx, t, ap, stored) and + not outBarrier(node1, state) and + not inBarrier(node2, state) and + apc = getHeadContent(ap) and + readStepCand0(node1, apc, c, node2) ) - ) - } - - private int ctxDispatchFanoutOnReturn(NodeEx out) { - result = max(DataFlowCall ctx | | ctxDispatchFanoutOnReturn(out, ctx)) - } - - private int dispatchFanoutOnReturn(NodeEx out) { - result = ctxDispatchFanoutOnReturn(out) - or - not exists(ctxDispatchFanoutOnReturn(out)) and - result = simpleDispatchFanoutOnReturn(_, out) - } - - /** - * Gets the amount of forward branching on the origin of a cross-call path - * edge in the graph of paths between sources and sinks that ignores call - * contexts. - */ - pragma[nomagic] - private int branch(ArgNodeEx n1) { - result = - strictcount(DataFlowCallable c | - exists(NodeEx n | - flowIntoCallNodeCand1(_, n1, n) and - c = n.getEnclosingCallable() - ) - ) + sum(ParamNodeEx p1 | | getLanguageSpecificFlowIntoCallNodeCand1(n1, p1)) - } + } - /** - * Gets the amount of backward branching on the target of a cross-call path - * edge in the graph of paths between sources and sinks that ignores call - * contexts. - */ - pragma[nomagic] - private int join(ParamNodeEx n2) { - result = - strictcount(DataFlowCallable c | - exists(NodeEx n | - flowIntoCallNodeCand1(_, n, n2) and - c = n.getEnclosingCallable() + pragma[nomagic] + private predicate fwdFlowRead( + NodeEx node1, Typ t1, Ap ap1, TypOption stored1, Content c, NodeEx node2, Typ t2, Ap ap2, + TypOption stored2, FlowState state, Cc cc, SummaryCtx summaryCtx + ) { + exists(Typ ct1, Typ ct2 | + fwdFlowRead0(t1, ap1, stored1, c, node1, node2, state, cc, summaryCtx) and + fwdFlowConsCand(ct1, ap1, c, ct2, ap2) and + typecheck(t1, ct1) and + typecheck(t2, ct2) and + if ap2 instanceof ApNil + then stored2.isNone() and stored1.asSome() = t2 + else ( + stored2 = stored1 and t2 = getNodeTyp(node2) ) - ) + sum(ArgNodeEx arg2 | | getLanguageSpecificFlowIntoCallNodeCand1(arg2, n2)) - } + ) + } - /** - * Holds if data can flow out of `call` from `ret` to `out`, either - * through a `ReturnNode` or through an argument that has been mutated, and - * that this step is part of a path from a source to a sink. The - * `allowsFieldFlow` flag indicates whether the branching is within the limit - * specified by the configuration. - */ - pragma[nomagic] - private predicate flowOutOfCallNodeCand1( - DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow - ) { - flowOutOfCallNodeCand1(call, ret, kind, out) and - exists(int j | - j = dispatchFanoutOnReturn(out) and - j > 0 and - if - j <= Config::fieldFlowBranchLimit() or - ignoreFieldFlowBranchLimit(ret.getEnclosingCallable()) - then allowsFieldFlow = true - else allowsFieldFlow = false - ) - } + pragma[nomagic] + private predicate fwdFlowIntoArg( + ArgNodeEx arg, FlowState state, Cc outercc, SummaryCtx summaryCtx, Typ t, Ap ap, + boolean emptyAp, TypOption stored, boolean cc + ) { + fwdFlow(arg, state, outercc, summaryCtx, t, ap, stored) and + (if instanceofCcCall(outercc) then cc = true else cc = false) and + emptyAp = isNil(ap) + } - pragma[nomagic] - private predicate allowsFieldFlowThrough(DataFlowCall call, DataFlowCallable c) { - exists(RetNodeEx ret | - flowOutOfCallNodeCand1(call, ret, _, _, true) and - c = ret.getEnclosingCallable() - ) - } + private signature predicate flowThroughSig(); - /** - * Holds if data can flow into `call` and that this step is part of a - * path from a source to a sink. The `allowsFieldFlow` flag indicates whether - * the branching is within the limit specified by the configuration. - */ - pragma[nomagic] - private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow - ) { - flowIntoCallNodeCand1(call, arg, p) and - exists(int b, int j | - b = branch(arg) and - j = join(p) and - if - b.minimum(j) <= Config::fieldFlowBranchLimit() or - ignoreFieldFlowBranchLimit(p.getEnclosingCallable()) - then allowsFieldFlow = true - else allowsFieldFlow = false - ) - } + /** + * Exposes the inlined predicate `fwdFlowIn`, which is used to calculate both + * flow in and flow through. + * + * For flow in, only a subset of the columns are needed, specifically we don't + * need to record the argument that flows into the parameter. + * + * For flow through, we do need to record the argument, however, we can restrict + * this to arguments that may actually flow through, which reduces the + * argument-to-parameter fan-in significantly. + */ + private module FwdFlowIn { + pragma[nomagic] + private predicate callEdgeArgParamRestricted( + DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, boolean emptyAp + ) { + PrevStage::callEdgeArgParam(call, c, arg, p, emptyAp) and + if + PrevStage::callMayFlowThroughRev(call) and + PrevStage::parameterMayFlowThrough(p, emptyAp) + then + emptyAp = true and + flowThrough() + or + emptyAp = false and + if allowsFieldFlowThrough(call, c) then flowThrough() else not flowThrough() + else not flowThrough() + } - private signature module StageSig { - class Ap; - - class ApNil extends Ap; + pragma[nomagic] + private DataFlowCallable viableImplCallContextReducedRestricted( + DataFlowCall call, CcCall ctx + ) { + result = viableImplCallContextReduced(call, ctx) and + callEdgeArgParamRestricted(call, result, _, _, _) + } - predicate revFlow(NodeEx node); + bindingset[call, ctx] + pragma[inline_late] + private DataFlowCallable viableImplCallContextReducedInlineLate( + DataFlowCall call, CcCall ctx + ) { + result = viableImplCallContextReducedRestricted(call, ctx) + } - bindingset[node, state] - predicate revFlow(NodeEx node, FlowState state, Ap ap); + bindingset[arg, ctx] + pragma[inline_late] + private DataFlowCallable viableImplCallContextReducedInlineLate( + DataFlowCall call, ArgNodeEx arg, CcCall ctx + ) { + callEdgeArgParamRestricted(call, _, arg, _, _) and + instanceofCcCall(ctx) and + result = viableImplCallContextReducedInlineLate(call, ctx) + } - predicate callMayFlowThroughRev(DataFlowCall call); + bindingset[call] + pragma[inline_late] + private predicate callEdgeArgParamRestrictedInlineLate( + DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, boolean emptyAp + ) { + callEdgeArgParamRestricted(call, c, arg, p, emptyAp) + } - predicate parameterMayFlowThrough(ParamNodeEx p, boolean emptyAp); + bindingset[call, ctx] + pragma[inline_late] + private predicate viableImplNotCallContextReducedInlineLate(DataFlowCall call, Cc ctx) { + instanceofCc(ctx) and + viableImplNotCallContextReduced(call, ctx) + } - predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind); + bindingset[arg, outercc] + pragma[inline_late] + private predicate viableImplArgNotCallContextReduced( + DataFlowCall call, ArgNodeEx arg, Cc outercc + ) { + callEdgeArgParamRestricted(call, _, arg, _, _) and + instanceofCc(outercc) and + viableImplNotCallContextReducedInlineLate(call, outercc) + } - predicate storeStepCand( - NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, DataFlowType containerType - ); + pragma[inline] + private predicate fwdFlowInCand( + DataFlowCall call, ArgNodeEx arg, FlowState state, Cc outercc, DataFlowCallable inner, + ParamNodeEx p, SummaryCtx summaryCtx, Typ t, Ap ap, boolean emptyAp, TypOption stored, + boolean cc + ) { + fwdFlowIntoArg(arg, state, outercc, summaryCtx, t, ap, emptyAp, stored, cc) and + ( + inner = viableImplCallContextReducedInlineLate(call, arg, outercc) + or + viableImplArgNotCallContextReduced(call, arg, outercc) + ) and + not outBarrier(arg, state) and + not inBarrier(p, state) and + callEdgeArgParamRestrictedInlineLate(call, inner, arg, p, emptyAp) + } - predicate readStepCand(NodeEx n1, Content c, NodeEx n2); + pragma[inline] + private predicate fwdFlowInCandTypeFlowDisabled( + DataFlowCall call, ArgNodeEx arg, FlowState state, Cc outercc, DataFlowCallable inner, + ParamNodeEx p, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, boolean cc + ) { + not enableTypeFlow() and + fwdFlowInCand(call, arg, state, outercc, inner, p, summaryCtx, t, ap, _, stored, cc) + } - predicate callEdgeArgParam( - DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, boolean emptyAp - ); + pragma[nomagic] + private predicate fwdFlowInCandTypeFlowEnabled( + DataFlowCall call, ArgNodeEx arg, Cc outercc, DataFlowCallable inner, ParamNodeEx p, + boolean emptyAp, boolean cc + ) { + enableTypeFlow() and + fwdFlowInCand(call, arg, _, outercc, inner, p, _, _, _, emptyAp, _, cc) + } - predicate callEdgeReturn( - DataFlowCall call, DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow - ); + pragma[nomagic] + private predicate fwdFlowInValidEdgeTypeFlowDisabled( + DataFlowCall call, DataFlowCallable inner, CcCall innercc, boolean cc + ) { + not enableTypeFlow() and + FwdTypeFlow::typeFlowValidEdgeIn(call, inner, cc) and + innercc = getCallContextCall(call, inner) + } - predicate relevantCallEdgeIn(DataFlowCall call, DataFlowCallable c); + pragma[nomagic] + private predicate fwdFlowInValidEdgeTypeFlowEnabled( + DataFlowCall call, ArgNodeEx arg, Cc outercc, DataFlowCallable inner, ParamNodeEx p, + CcCall innercc, boolean emptyAp, boolean cc + ) { + fwdFlowInCandTypeFlowEnabled(call, arg, outercc, inner, p, emptyAp, cc) and + FwdTypeFlow::typeFlowValidEdgeIn(call, inner, cc) and + innercc = getCallContextCall(call, inner) + } - predicate relevantCallEdgeOut(DataFlowCall call, DataFlowCallable c); - } + pragma[inline] + predicate fwdFlowIn( + DataFlowCall call, ArgNodeEx arg, DataFlowCallable inner, ParamNodeEx p, + FlowState state, Cc outercc, CcCall innercc, SummaryCtx summaryCtx, Typ t, Ap ap, + TypOption stored, boolean cc + ) { + // type flow disabled: linear recursion + fwdFlowInCandTypeFlowDisabled(call, arg, state, outercc, inner, p, summaryCtx, t, ap, + stored, cc) and + fwdFlowInValidEdgeTypeFlowDisabled(call, inner, innercc, pragma[only_bind_into](cc)) + or + // type flow enabled: non-linear recursion + exists(boolean emptyAp | + fwdFlowIntoArg(arg, state, outercc, summaryCtx, t, ap, emptyAp, stored, cc) and + fwdFlowInValidEdgeTypeFlowEnabled(call, arg, outercc, inner, p, innercc, emptyAp, cc) + ) + } + } - private module MkStage { - class ApApprox = PrevStage::Ap; + private predicate bottom() { none() } - signature module StageParam { - class Typ { - string toString(); - } + private module FwdFlowInNoThrough = FwdFlowIn; - class Ap { - string toString(); + pragma[nomagic] + private predicate fwdFlowInNoFlowThrough( + ParamNodeEx p, FlowState state, CcCall innercc, Typ t, Ap ap, TypOption stored + ) { + FwdFlowInNoThrough::fwdFlowIn(_, _, _, p, state, _, innercc, _, t, ap, stored, _) } - class ApNil extends Ap; + private predicate top() { any() } - bindingset[result, ap] - ApApprox getApprox(Ap ap); + private module FwdFlowInThrough = FwdFlowIn; - Typ getTyp(DataFlowType t); + pragma[nomagic] + private predicate fwdFlowInFlowThrough( + ParamNodeEx p, FlowState state, CcCall innercc, Typ t, Ap ap, TypOption stored + ) { + FwdFlowInThrough::fwdFlowIn(_, _, _, p, state, _, innercc, _, t, ap, stored, _) + } - bindingset[c, tail] - Ap apCons(Content c, Ap tail); + pragma[nomagic] + private DataFlowCall viableImplCallContextReducedReverseRestricted( + DataFlowCallable c, CcNoCall ctx + ) { + result = viableImplCallContextReducedReverse(c, ctx) and + PrevStage::callEdgeReturn(result, c, _, _, _, _) + } - /** - * An approximation of `Content` that corresponds to the precision level of - * `Ap`, such that the mappings from both `Ap` and `Content` to this type - * are functional. - */ - class ApHeadContent; + bindingset[c, ctx] + pragma[inline_late] + private DataFlowCall viableImplCallContextReducedReverseInlineLate( + DataFlowCallable c, CcNoCall ctx + ) { + result = viableImplCallContextReducedReverseRestricted(c, ctx) + } - ApHeadContent getHeadContent(Ap ap); + bindingset[call] + pragma[inline_late] + private predicate flowOutOfCallInlineLate( + DataFlowCall call, DataFlowCallable c, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow + ) { + PrevStage::callEdgeReturn(call, c, ret, _, out, allowsFieldFlow) + } - ApHeadContent projectToHeadContent(Content c); + bindingset[c, ret, innercc] + pragma[inline_late] + pragma[noopt] + private predicate flowOutOfCallNotCallContextReduced( + DataFlowCall call, DataFlowCallable c, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + CcNoCall innercc + ) { + viableImplNotCallContextReducedReverse(innercc) and + PrevStage::callEdgeReturn(call, c, ret, _, out, allowsFieldFlow) + } - class ApOption; + pragma[nomagic] + private predicate fwdFlowIntoRet( + RetNodeEx ret, FlowState state, CcNoCall cc, SummaryCtx summaryCtx, Typ t, Ap ap, + TypOption stored + ) { + instanceofCcNoCall(cc) and + not outBarrier(ret, state) and + fwdFlow(ret, state, cc, summaryCtx, t, ap, stored) + } - ApOption apNone(); + pragma[nomagic] + private predicate fwdFlowOutCand( + DataFlowCall call, RetNodeEx ret, CcNoCall innercc, DataFlowCallable inner, NodeEx out, + boolean allowsFieldFlow + ) { + fwdFlowIntoRet(ret, _, innercc, _, _, _, _) and + inner = ret.getEnclosingCallable() and + ( + call = viableImplCallContextReducedReverseInlineLate(inner, innercc) and + flowOutOfCallInlineLate(call, inner, ret, out, allowsFieldFlow) + or + flowOutOfCallNotCallContextReduced(call, inner, ret, out, allowsFieldFlow, innercc) + ) + } - ApOption apSome(Ap ap); + pragma[nomagic] + private predicate fwdFlowOutValidEdge( + DataFlowCall call, RetNodeEx ret, CcNoCall innercc, DataFlowCallable inner, NodeEx out, + CcNoCall outercc, boolean allowsFieldFlow + ) { + fwdFlowOutCand(call, ret, innercc, inner, out, allowsFieldFlow) and + FwdTypeFlow::typeFlowValidEdgeOut(call, inner) and + outercc = getCallContextReturn(inner, call) + } - class Cc { - string toString(); + pragma[inline] + private predicate fwdFlowOut( + DataFlowCall call, DataFlowCallable inner, NodeEx out, FlowState state, CcNoCall outercc, + SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored + ) { + exists(RetNodeEx ret, CcNoCall innercc, boolean allowsFieldFlow | + fwdFlowIntoRet(ret, state, innercc, summaryCtx, t, ap, stored) and + fwdFlowOutValidEdge(call, ret, innercc, inner, out, outercc, allowsFieldFlow) and + not inBarrier(out, state) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) } - class CcCall extends Cc; + private module FwdTypeFlowInput implements TypeFlowInput { + predicate enableTypeFlow = Param::enableTypeFlow/0; - // TODO: member predicate on CcCall - predicate matchesCall(CcCall cc, DataFlowCall call); - - class CcNoCall extends Cc; - - Cc ccNone(); - - CcCall ccSomeCall(); - - /* - * The following `instanceof` predicates are necessary for proper - * caching, since we're able to cache predicates, but not the underlying - * types. - */ - - predicate instanceofCc(Cc cc); - - predicate instanceofCcCall(CcCall cc); - - predicate instanceofCcNoCall(CcNoCall cc); - - class LocalCc; - - DataFlowCallable viableImplCallContextReduced(DataFlowCall call, CcCall ctx); - - bindingset[call, ctx] - predicate viableImplNotCallContextReduced(DataFlowCall call, Cc ctx); - - bindingset[call, c] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c); - - DataFlowCall viableImplCallContextReducedReverse(DataFlowCallable c, CcNoCall ctx); - - predicate viableImplNotCallContextReducedReverse(CcNoCall ctx); + predicate relevantCallEdgeIn = PrevStage::relevantCallEdgeIn/2; - bindingset[call, c] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call); + predicate relevantCallEdgeOut = PrevStage::relevantCallEdgeOut/2; - bindingset[cc] - LocalCc getLocalCc(Cc cc); + pragma[nomagic] + private predicate dataFlowTakenCallEdgeIn0( + DataFlowCall call, DataFlowCallable c, ParamNodeEx p, FlowState state, CcCall innercc, + Typ t, Ap ap, TypOption stored, boolean cc + ) { + FwdFlowInNoThrough::fwdFlowIn(call, _, c, p, state, _, innercc, _, t, ap, stored, cc) + or + FwdFlowInThrough::fwdFlowIn(call, _, c, p, state, _, innercc, _, t, ap, stored, cc) + } - bindingset[node1, state1] - bindingset[node2, state2] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - Typ t, LocalCc lcc, string label - ); + pragma[nomagic] + private predicate fwdFlow1Param( + ParamNodeEx p, FlowState state, CcCall cc, Typ t0, Ap ap, TypOption stored + ) { + instanceofCcCall(cc) and + fwdFlow1(p, state, cc, _, t0, _, ap, stored) + } - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t); + pragma[nomagic] + predicate dataFlowTakenCallEdgeIn(DataFlowCall call, DataFlowCallable c, boolean cc) { + exists(ParamNodeEx p, FlowState state, CcCall innercc, Typ t, Ap ap, TypOption stored | + dataFlowTakenCallEdgeIn0(call, c, p, state, innercc, t, ap, stored, cc) and + fwdFlow1Param(p, state, innercc, t, ap, stored) + ) + } - bindingset[node, ap, isStoreStep] - predicate stepFilter(NodeEx node, Ap ap, boolean isStoreStep); + pragma[nomagic] + private predicate dataFlowTakenCallEdgeOut0( + DataFlowCall call, DataFlowCallable c, NodeEx node, FlowState state, Cc cc, Typ t, + Ap ap, TypOption stored + ) { + fwdFlowOut(call, c, node, state, cc, _, t, ap, stored) + } - bindingset[t1, t2] - predicate typecheck(Typ t1, Typ t2); + pragma[nomagic] + private predicate fwdFlow1Out( + NodeEx node, FlowState state, Cc cc, Typ t0, Ap ap, TypOption stored + ) { + fwdFlow1(node, state, cc, _, t0, _, ap, stored) and + PrevStage::callEdgeReturn(_, _, _, _, node, _) + } - default predicate enableTypeFlow() { any() } - } + pragma[nomagic] + predicate dataFlowTakenCallEdgeOut(DataFlowCall call, DataFlowCallable c) { + exists(NodeEx node, FlowState state, Cc cc, Typ t, Ap ap, TypOption stored | + dataFlowTakenCallEdgeOut0(call, c, node, state, cc, t, ap, stored) and + fwdFlow1Out(node, state, cc, t, ap, stored) + ) + } - module Stage implements StageSig { - import Param + predicate dataFlowNonCallEntry(DataFlowCallable c, boolean cc) { + exists(NodeEx node, FlowState state | + sourceNode(node, state) and + (if hasSourceCallCtx() then cc = true else cc = false) and + PrevStage::revFlow(node, state, any(PrevStage::ApNil nil)) and + c = node.getEnclosingCallable() + ) + or + exists(NodeEx node | + cc = false and + fwdFlowJump(node, _, _, _, _) and + c = node.getEnclosingCallable() + ) + } + } - private module TypOption = Option; + private module FwdTypeFlow = TypeFlow; - private class TypOption = TypOption::Option; + private predicate flowIntoCallTaken( + DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, boolean emptyAp + ) { + PrevStage::callEdgeArgParam(call, c, arg, p, emptyAp) and + FwdTypeFlowInput::dataFlowTakenCallEdgeIn(call, c, _) + } - private string ppStored(TypOption stored) { - exists(string ppt | ppt = stored.toString() | - if stored.isNone() or ppt = "" then result = "" else result = " : " + ppt + pragma[nomagic] + private predicate fwdFlowRetFromArg( + RetNodeEx ret, FlowState state, CcCall ccc, SummaryCtxSome summaryCtx, Typ t, Ap ap, + TypOption stored + ) { + exists(ReturnKindExt kind, ParamNodeEx p, Ap argAp | + instanceofCcCall(ccc) and + fwdFlow(pragma[only_bind_into](ret), state, ccc, summaryCtx, t, ap, stored) and + summaryCtx = + TSummaryCtxSome(pragma[only_bind_into](p), _, _, pragma[only_bind_into](argAp), _) and + not outBarrier(ret, state) and + kind = ret.getKind() and + Stage1::parameterFlowThroughAllowed(p, kind) and + PrevStage::returnMayFlowThrough(ret, kind) ) } - bindingset[ap] - private boolean isNil(Ap ap) { - if ap instanceof ApNil then result = true else result = false + pragma[inline] + private predicate fwdFlowThrough0( + DataFlowCall call, ArgNodeEx arg, Cc cc, FlowState state, CcCall ccc, + SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, RetNodeEx ret, + SummaryCtxSome innerSummaryCtx + ) { + fwdFlowRetFromArg(ret, state, ccc, innerSummaryCtx, t, ap, stored) and + fwdFlowIsEntered(call, arg, cc, ccc, summaryCtx, innerSummaryCtx) } - /* Begin: Stage logic. */ pragma[nomagic] - private Typ getNodeTyp(NodeEx node) { - PrevStage::revFlow(node) and result = getTyp(node.getDataFlowType()) + private predicate fwdFlowThrough( + DataFlowCall call, Cc cc, FlowState state, SummaryCtx summaryCtx, Typ t, Ap ap, + TypOption stored, RetNodeEx ret + ) { + fwdFlowThrough0(call, _, cc, state, _, summaryCtx, t, ap, stored, ret, _) } pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow + private predicate fwdFlowIsEntered0( + DataFlowCall call, ArgNodeEx arg, Cc cc, CcCall innerCc, SummaryCtx summaryCtx, + ParamNodeEx p, FlowState state, Typ t, Ap ap, TypOption stored ) { - exists(ReturnKindExt kind | - PrevStage::callEdgeReturn(call, _, ret, kind, out, allowsFieldFlow) and - PrevStage::callMayFlowThroughRev(call) and - PrevStage::returnMayFlowThrough(ret, kind) - ) + FwdFlowInThrough::fwdFlowIn(call, arg, _, p, state, cc, innerCc, summaryCtx, t, ap, + stored, _) } + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ pragma[nomagic] - private predicate compatibleContainer0(ApHeadContent apc, DataFlowType containerType) { - exists(DataFlowType containerType0, Content c | - PrevStage::storeStepCand(_, c, _, _, containerType0) and - not isTopType(containerType0) and - compatibleTypesCached(containerType0, containerType) and - apc = projectToHeadContent(c) + private predicate fwdFlowIsEntered( + DataFlowCall call, ArgNodeEx arg, Cc cc, CcCall innerCc, SummaryCtx summaryCtx, + SummaryCtxSome innerSummaryCtx + ) { + exists(ParamNodeEx p, FlowState state, Typ t, Ap ap, TypOption stored | + fwdFlowIsEntered0(call, arg, cc, innerCc, summaryCtx, p, state, t, ap, stored) and + innerSummaryCtx = TSummaryCtxSome(p, state, t, ap, stored) ) } pragma[nomagic] - private predicate topTypeContent(ApHeadContent apc) { - exists(DataFlowType containerType0, Content c | - PrevStage::storeStepCand(_, c, _, _, containerType0) and - isTopType(containerType0) and - apc = projectToHeadContent(c) - ) + private predicate storeStepFwd(NodeEx node1, Ap ap1, Content c, NodeEx node2, Ap ap2) { + fwdFlowStore(node1, _, ap1, _, c, _, _, node2, _, _, _) and + readStepFwd(_, ap2, c, _, ap1) } - bindingset[apc, containerType] - pragma[inline_late] - private predicate compatibleContainer(ApHeadContent apc, DataFlowType containerType) { - compatibleContainer0(apc, containerType) + pragma[nomagic] + private predicate readStepFwd(NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2) { + fwdFlowRead(n1, _, ap1, _, c, n2, _, ap2, _, _, _, _) } - /** - * Holds if `node` is reachable with access path `ap` from a source. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `summaryCtx` records the - * corresponding parameter position and access path of that argument. - */ pragma[nomagic] - additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored + private predicate returnFlowsThrough0( + DataFlowCall call, FlowState state, CcCall ccc, Ap ap, RetNodeEx ret, + SummaryCtxSome innerSummaryCtx ) { - fwdFlow1(node, state, cc, summaryCtx, _, t, ap, stored) + fwdFlowThrough0(call, _, _, state, ccc, _, _, ap, _, ret, innerSummaryCtx) } - private predicate fwdFlow1( - NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Typ t, Ap ap, - TypOption stored + pragma[nomagic] + private predicate returnFlowsThrough( + RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Typ argT, + Ap argAp, TypOption argStored, Ap ap ) { - exists(ApApprox apa | - fwdFlow0(node, state, cc, summaryCtx, t0, ap, apa, stored) and - PrevStage::revFlow(node, state, apa) and - filter(node, state, t0, ap, t) and - ( - if node instanceof CastingNodeEx - then - ap instanceof ApNil or - compatibleContainer(getHeadContent(ap), node.getDataFlowType()) or - topTypeContent(getHeadContent(ap)) - else any() - ) + exists(DataFlowCall call, boolean allowsFieldFlow | + returnFlowsThrough0(call, state, ccc, ap, ret, + TSummaryCtxSome(p, _, argT, argAp, argStored)) and + flowThroughOutOfCall(call, ret, _, allowsFieldFlow) and + pos = ret.getReturnPosition() and + if allowsFieldFlow = false then ap instanceof ApNil else any() ) } pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, ApApprox apa, - TypOption stored + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, Ap argAp ) { - sourceNode(node, state) and - (if hasSourceCallCtx() then cc = ccSomeCall() else cc = ccNone()) and - summaryCtx = TSummaryCtxNone() and - t = getNodeTyp(node) and - ap instanceof ApNil and - apa = getApprox(ap) and - stored.isNone() - or - exists(NodeEx mid, FlowState state0, Typ t0, LocalCc localCc | - fwdFlow(mid, state0, cc, summaryCtx, t0, ap, stored) and - apa = getApprox(ap) and - localCc = getLocalCc(cc) - | - localStep(mid, state0, node, state, true, _, localCc, _) and - t = t0 - or - localStep(mid, state0, node, state, false, t, localCc, _) and + exists(Typ argT, TypOption argStored | + returnFlowsThrough(_, _, _, _, pragma[only_bind_into](p), pragma[only_bind_into](argT), + pragma[only_bind_into](argAp), pragma[only_bind_into](argStored), _) and + flowIntoCallTaken(call, _, pragma[only_bind_into](arg), p, isNil(argAp)) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](argT), pragma[only_bind_into](argAp), + pragma[only_bind_into](argStored)) + ) + } + + pragma[nomagic] + private predicate flowIntoCallAp( + DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, Ap ap + ) { + flowIntoCallTaken(call, c, arg, p, isNil(ap)) and + fwdFlow(arg, _, _, _, _, ap, _) + } + + pragma[nomagic] + private predicate flowOutOfCallAp( + DataFlowCall call, DataFlowCallable c, RetNodeEx ret, ReturnPosition pos, NodeEx out, + Ap ap, boolean allowsFieldFlow + ) { + PrevStage::callEdgeReturn(call, c, ret, _, out, allowsFieldFlow) and + fwdFlow(ret, _, _, _, _, ap, _) and + pos = ret.getReturnPosition() and + (if allowsFieldFlow = false then ap instanceof ApNil else any()) and + ( + // both directions are needed for flow-through + FwdTypeFlowInput::dataFlowTakenCallEdgeIn(call, c, _) or + FwdTypeFlowInput::dataFlowTakenCallEdgeOut(call, c) + ) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink. + * + * The parameter `returnCtx` records whether (and how) the node must be returned + * from the enclosing callable in order to reach a sink, and if so, `returnAp` + * records the access path of the returned value. + */ + pragma[nomagic] + additional predicate revFlow( + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap + ) { + revFlow0(node, state, returnCtx, returnAp, ap) and + fwdFlow(node, state, _, _, _, ap, _) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap + ) { + fwdFlow(node, state, _, _, _, ap, _) and + sinkNode(node, state) and + ( + if hasSinkCallCtx() + then returnCtx = TReturnCtxNoFlowThrough() + else returnCtx = TReturnCtxNone() + ) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, _, _) and + revFlow(mid, state0, returnCtx, returnAp, ap) + ) + or + exists(NodeEx mid, FlowState state0 | + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, _, _) and + revFlow(mid, state0, returnCtx, returnAp, ap) and ap instanceof ApNil ) or - fwdFlowJump(node, state, t, ap, stored) and - apa = getApprox(ap) and - cc = ccNone() and - summaryCtx = TSummaryCtxNone() + revFlowJump(node, state, ap) and + returnCtx = TReturnCtxNone() and + returnAp = apNone() or // store - exists(Content c, Ap ap0 | - fwdFlowStore(_, _, ap0, _, c, t, stored, node, state, cc, summaryCtx) and - ap = apCons(c, ap0) and - apa = getApprox(ap) + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, returnCtx, returnAp) and + revFlowConsCand(ap0, c, ap) ) or // read - fwdFlowRead(_, _, _, _, _, node, t, ap, stored, state, cc, summaryCtx) and - apa = getApprox(ap) + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, returnCtx, returnAp, ap0) and + readStepFwd(node, ap, _, mid, ap0) + ) or - // flow into a callable without summary context - fwdFlowInNoFlowThrough(node, state, cc, t, ap, stored) and - apa = getApprox(ap) and - summaryCtx = TSummaryCtxNone() and - // When the call contexts of source and sink needs to match then there's - // never any reason to enter a callable except to find a summary. See also - // the comment in `PathNodeMid::isAtSink`. - not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext + // flow into a callable + revFlowIn(_, _, node, state, ap) and + returnCtx = TReturnCtxNone() and + returnAp = apNone() or - // flow into a callable with summary context (non-linear recursion) - fwdFlowInFlowThrough(node, state, cc, t, ap, stored) and - apa = getApprox(ap) and - summaryCtx = TSummaryCtxSome(node, state, t, ap, stored) + // flow through a callable + exists(DataFlowCall call, ParamNodeEx p | + revFlowThrough(call, returnCtx, p, state, returnAp, ap) and + flowThroughIntoCall(call, node, p, ap) + ) or // flow out of a callable - fwdFlowOut(_, _, node, state, cc, summaryCtx, t, ap, stored) and - apa = getApprox(ap) - or - // flow through a callable - exists(DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow | - fwdFlowThrough(call, cc, state, summaryCtx, t, ap, stored, ret) and - flowThroughOutOfCall(call, ret, node, allowsFieldFlow) and - apa = getApprox(ap) and - not inBarrier(node, state) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + exists(ReturnPosition pos | + revFlowOut(_, node, pos, state, _, _, _, ap) and + if returnFlowsThrough(node, pos, state, _, _, _, _, _, ap) + then ( + returnCtx = TReturnCtxMaybeFlowThrough(pos) and + returnAp = apSome(ap) + ) else ( + returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() + ) ) } - private newtype TSummaryCtx = - TSummaryCtxNone() or - TSummaryCtxSome(ParamNodeEx p, FlowState state, Typ t, Ap ap, TypOption stored) { - fwdFlowInFlowThrough(p, state, _, t, ap, stored) - } - - /** - * A context for generating flow summaries. This represents flow entry through - * a specific parameter with an access path of a specific shape. - * - * Summaries are only created for parameters that may flow through. - */ - private class SummaryCtx extends TSummaryCtx { - abstract string toString(); - - abstract Location getLocation(); - } - - /** A summary context from which no flow summary can be generated. */ - private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { - override string toString() { result = "" } - - override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) } - } - - /** A summary context from which a flow summary can be generated. */ - private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParamNodeEx p; - private FlowState state; - private Typ t; - private Ap ap; - private TypOption stored; - - SummaryCtxSome() { this = TSummaryCtxSome(p, state, t, ap, stored) } - - ParamNodeEx getParamNode() { result = p } - - private string ppTyp() { result = t.toString() and result != "" } - - override string toString() { - result = p + concat(" : " + this.ppTyp()) + " " + ap + ppStored(stored) - } - - override Location getLocation() { result = p.getLocation() } - } - - private predicate fwdFlowJump(NodeEx node, FlowState state, Typ t, Ap ap, TypOption stored) { + private predicate revFlowJump(NodeEx node, FlowState state, Ap ap) { exists(NodeEx mid | - fwdFlow(mid, state, _, _, t, ap, stored) and - jumpStepEx(mid, node) + jumpStepEx(node, mid) and + revFlow(mid, state, _, _, ap) ) or exists(NodeEx mid | - fwdFlow(mid, state, _, _, _, ap, stored) and - additionalJumpStep(mid, node, _) and - t = getNodeTyp(node) and + additionalJumpStep(node, mid, _) and + revFlow(pragma[only_bind_into](mid), state, _, _, ap) and ap instanceof ApNil ) or exists(NodeEx mid, FlowState state0 | - fwdFlow(mid, state0, _, _, _, ap, stored) and - additionalJumpStateStep(mid, state0, node, state, _) and - t = getNodeTyp(node) and + additionalJumpStateStep(node, state, mid, state0, _) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, ap) and ap instanceof ApNil ) } pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Typ t1, Ap ap1, TypOption stored1, Content c, Typ t2, TypOption stored2, - NodeEx node2, FlowState state, Cc cc, SummaryCtx summaryCtx + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, NodeEx mid, ReturnCtx returnCtx, + ApOption returnAp ) { - exists(DataFlowType contentType, DataFlowType containerType | - fwdFlow(node1, state, cc, summaryCtx, t1, ap1, stored1) and - not outBarrier(node1, state) and - not inBarrier(node2, state) and - PrevStage::storeStepCand(node1, c, node2, contentType, containerType) and - t2 = getTyp(containerType) and - // We need to typecheck stores here, since reverse flow through a getter - // might have a different type here compared to inside the getter. - typecheck(t1, getTyp(contentType)) and - if ap1 instanceof ApNil then stored2.asSome() = t1 else stored2 = stored1 - ) + revFlow(mid, state, returnCtx, returnAp, ap0) and + storeStepFwd(node, ap, c, mid, ap0) } /** - * Holds if forward flow with access path `tail` and type `t1` reaches a - * store of `c` on a container of type `t2` resulting in access path - * `cons`. + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. */ pragma[nomagic] - private predicate fwdFlowConsCand(Typ t2, Ap cons, Content c, Typ t1, Ap tail) { - fwdFlowStore(_, t1, tail, _, c, t2, _, _, _, _, _) and - cons = apCons(c, tail) + private predicate revFlowConsCand(Ap cons, Content c, Ap tail) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0) + ) } - pragma[nomagic] - private predicate readStepCand(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { - PrevStage::readStepCand(node1, c, node2) and - apc = projectToHeadContent(c) - } + private module RevTypeFlowInput implements TypeFlowInput { + predicate enableTypeFlow = Param::enableTypeFlow/0; - bindingset[node1, apc] - pragma[inline_late] - private predicate readStepCand0(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { - readStepCand(node1, apc, c, node2) - } + predicate relevantCallEdgeIn(DataFlowCall call, DataFlowCallable c) { + flowOutOfCallAp(call, c, _, _, _, _, _) + } - pragma[nomagic] - private predicate fwdFlowRead0( - Typ t, Ap ap, TypOption stored, Content c, NodeEx node1, NodeEx node2, FlowState state, - Cc cc, SummaryCtx summaryCtx - ) { - exists(ApHeadContent apc | - fwdFlow(node1, state, cc, summaryCtx, t, ap, stored) and - not outBarrier(node1, state) and - not inBarrier(node2, state) and - apc = getHeadContent(ap) and - readStepCand0(node1, apc, c, node2) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - NodeEx node1, Typ t1, Ap ap1, TypOption stored1, Content c, NodeEx node2, Typ t2, Ap ap2, - TypOption stored2, FlowState state, Cc cc, SummaryCtx summaryCtx - ) { - exists(Typ ct1, Typ ct2 | - fwdFlowRead0(t1, ap1, stored1, c, node1, node2, state, cc, summaryCtx) and - fwdFlowConsCand(ct1, ap1, c, ct2, ap2) and - typecheck(t1, ct1) and - typecheck(t2, ct2) and - if ap2 instanceof ApNil - then stored2.isNone() and stored1.asSome() = t2 - else ( - stored2 = stored1 and t2 = getNodeTyp(node2) - ) - ) - } - - pragma[nomagic] - private predicate fwdFlowIntoArg( - ArgNodeEx arg, FlowState state, Cc outercc, SummaryCtx summaryCtx, Typ t, Ap ap, - boolean emptyAp, TypOption stored, boolean cc - ) { - fwdFlow(arg, state, outercc, summaryCtx, t, ap, stored) and - (if instanceofCcCall(outercc) then cc = true else cc = false) and - emptyAp = isNil(ap) - } - - private signature predicate flowThroughSig(); - - /** - * Exposes the inlined predicate `fwdFlowIn`, which is used to calculate both - * flow in and flow through. - * - * For flow in, only a subset of the columns are needed, specifically we don't - * need to record the argument that flows into the parameter. - * - * For flow through, we do need to record the argument, however, we can restrict - * this to arguments that may actually flow through, which reduces the - * argument-to-parameter fan-in significantly. - */ - private module FwdFlowIn { - pragma[nomagic] - private predicate callEdgeArgParamRestricted( - DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, boolean emptyAp - ) { - PrevStage::callEdgeArgParam(call, c, arg, p, emptyAp) and - if - PrevStage::callMayFlowThroughRev(call) and - PrevStage::parameterMayFlowThrough(p, emptyAp) - then - emptyAp = true and - flowThrough() - or - emptyAp = false and - if allowsFieldFlowThrough(call, c) then flowThrough() else not flowThrough() - else not flowThrough() - } - - pragma[nomagic] - private DataFlowCallable viableImplCallContextReducedRestricted( - DataFlowCall call, CcCall ctx - ) { - result = viableImplCallContextReduced(call, ctx) and - callEdgeArgParamRestricted(call, result, _, _, _) - } - - bindingset[call, ctx] - pragma[inline_late] - private DataFlowCallable viableImplCallContextReducedInlineLate( - DataFlowCall call, CcCall ctx - ) { - result = viableImplCallContextReducedRestricted(call, ctx) - } - - bindingset[arg, ctx] - pragma[inline_late] - private DataFlowCallable viableImplCallContextReducedInlineLate( - DataFlowCall call, ArgNodeEx arg, CcCall ctx - ) { - callEdgeArgParamRestricted(call, _, arg, _, _) and - instanceofCcCall(ctx) and - result = viableImplCallContextReducedInlineLate(call, ctx) - } - - bindingset[call] - pragma[inline_late] - private predicate callEdgeArgParamRestrictedInlineLate( - DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, boolean emptyAp - ) { - callEdgeArgParamRestricted(call, c, arg, p, emptyAp) - } - - bindingset[call, ctx] - pragma[inline_late] - private predicate viableImplNotCallContextReducedInlineLate(DataFlowCall call, Cc ctx) { - instanceofCc(ctx) and - viableImplNotCallContextReduced(call, ctx) - } - - bindingset[arg, outercc] - pragma[inline_late] - private predicate viableImplArgNotCallContextReduced( - DataFlowCall call, ArgNodeEx arg, Cc outercc - ) { - callEdgeArgParamRestricted(call, _, arg, _, _) and - instanceofCc(outercc) and - viableImplNotCallContextReducedInlineLate(call, outercc) - } - - pragma[inline] - private predicate fwdFlowInCand( - DataFlowCall call, ArgNodeEx arg, FlowState state, Cc outercc, DataFlowCallable inner, - ParamNodeEx p, SummaryCtx summaryCtx, Typ t, Ap ap, boolean emptyAp, TypOption stored, - boolean cc - ) { - fwdFlowIntoArg(arg, state, outercc, summaryCtx, t, ap, emptyAp, stored, cc) and - ( - inner = viableImplCallContextReducedInlineLate(call, arg, outercc) - or - viableImplArgNotCallContextReduced(call, arg, outercc) - ) and - not outBarrier(arg, state) and - not inBarrier(p, state) and - callEdgeArgParamRestrictedInlineLate(call, inner, arg, p, emptyAp) - } - - pragma[inline] - private predicate fwdFlowInCandTypeFlowDisabled( - DataFlowCall call, ArgNodeEx arg, FlowState state, Cc outercc, DataFlowCallable inner, - ParamNodeEx p, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, boolean cc - ) { - not enableTypeFlow() and - fwdFlowInCand(call, arg, state, outercc, inner, p, summaryCtx, t, ap, _, stored, cc) - } - - pragma[nomagic] - private predicate fwdFlowInCandTypeFlowEnabled( - DataFlowCall call, ArgNodeEx arg, Cc outercc, DataFlowCallable inner, ParamNodeEx p, - boolean emptyAp, boolean cc - ) { - enableTypeFlow() and - fwdFlowInCand(call, arg, _, outercc, inner, p, _, _, _, emptyAp, _, cc) + predicate relevantCallEdgeOut(DataFlowCall call, DataFlowCallable c) { + flowIntoCallAp(call, c, _, _, _) } pragma[nomagic] - private predicate fwdFlowInValidEdgeTypeFlowDisabled( - DataFlowCall call, DataFlowCallable inner, CcCall innercc, boolean cc - ) { - not enableTypeFlow() and - FwdTypeFlow::typeFlowValidEdgeIn(call, inner, cc) and - innercc = getCallContextCall(call, inner) + predicate dataFlowTakenCallEdgeIn(DataFlowCall call, DataFlowCallable c, boolean cc) { + exists(RetNodeEx ret | + revFlowOut(call, ret, _, _, _, cc, _, _) and + c = ret.getEnclosingCallable() + ) } pragma[nomagic] - private predicate fwdFlowInValidEdgeTypeFlowEnabled( - DataFlowCall call, ArgNodeEx arg, Cc outercc, DataFlowCallable inner, ParamNodeEx p, - CcCall innercc, boolean emptyAp, boolean cc - ) { - fwdFlowInCandTypeFlowEnabled(call, arg, outercc, inner, p, emptyAp, cc) and - FwdTypeFlow::typeFlowValidEdgeIn(call, inner, cc) and - innercc = getCallContextCall(call, inner) + predicate dataFlowTakenCallEdgeOut(DataFlowCall call, DataFlowCallable c) { + revFlowIn(call, c, _, _, _) } - pragma[inline] - predicate fwdFlowIn( - DataFlowCall call, ArgNodeEx arg, DataFlowCallable inner, ParamNodeEx p, - FlowState state, Cc outercc, CcCall innercc, SummaryCtx summaryCtx, Typ t, Ap ap, - TypOption stored, boolean cc - ) { - // type flow disabled: linear recursion - fwdFlowInCandTypeFlowDisabled(call, arg, state, outercc, inner, p, summaryCtx, t, ap, - stored, cc) and - fwdFlowInValidEdgeTypeFlowDisabled(call, inner, innercc, pragma[only_bind_into](cc)) + predicate dataFlowNonCallEntry(DataFlowCallable c, boolean cc) { + exists(NodeEx node, FlowState state, ApNil nil | + fwdFlow(node, state, _, _, _, nil, _) and + sinkNode(node, state) and + (if hasSinkCallCtx() then cc = true else cc = false) and + c = node.getEnclosingCallable() + ) or - // type flow enabled: non-linear recursion - exists(boolean emptyAp | - fwdFlowIntoArg(arg, state, outercc, summaryCtx, t, ap, emptyAp, stored, cc) and - fwdFlowInValidEdgeTypeFlowEnabled(call, arg, outercc, inner, p, innercc, emptyAp, cc) + exists(NodeEx node | + cc = false and + revFlowJump(node, _, _) and + c = node.getEnclosingCallable() ) } } - private predicate bottom() { none() } - - private module FwdFlowInNoThrough = FwdFlowIn; + private module RevTypeFlow = TypeFlow; pragma[nomagic] - private predicate fwdFlowInNoFlowThrough( - ParamNodeEx p, FlowState state, CcCall innercc, Typ t, Ap ap, TypOption stored + private predicate flowIntoCallApValid( + DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, Ap ap ) { - FwdFlowInNoThrough::fwdFlowIn(_, _, _, p, state, _, innercc, _, t, ap, stored, _) + flowIntoCallAp(call, c, arg, p, ap) and + RevTypeFlow::typeFlowValidEdgeOut(call, c) } - private predicate top() { any() } - - private module FwdFlowInThrough = FwdFlowIn; - pragma[nomagic] - private predicate fwdFlowInFlowThrough( - ParamNodeEx p, FlowState state, CcCall innercc, Typ t, Ap ap, TypOption stored + private predicate flowOutOfCallApValid( + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Ap ap, boolean cc ) { - FwdFlowInThrough::fwdFlowIn(_, _, _, p, state, _, innercc, _, t, ap, stored, _) + exists(DataFlowCallable c | + flowOutOfCallAp(call, c, ret, pos, out, ap, _) and + RevTypeFlow::typeFlowValidEdgeIn(call, c, cc) + ) } - pragma[nomagic] - private DataFlowCall viableImplCallContextReducedReverseRestricted( - DataFlowCallable c, CcNoCall ctx + private predicate revFlowIn( + DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, FlowState state, Ap ap ) { - result = viableImplCallContextReducedReverse(c, ctx) and - PrevStage::callEdgeReturn(result, c, _, _, _, _) + exists(ParamNodeEx p | + revFlow(p, state, TReturnCtxNone(), _, ap) and + flowIntoCallApValid(call, c, arg, p, ap) + ) } - bindingset[c, ctx] - pragma[inline_late] - private DataFlowCall viableImplCallContextReducedReverseInlineLate( - DataFlowCallable c, CcNoCall ctx + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, + ReturnCtx returnCtx, boolean cc, ApOption returnAp, Ap ap ) { - result = viableImplCallContextReducedReverseRestricted(c, ctx) + exists(NodeEx out | + revFlow(out, state, returnCtx, returnAp, ap) and + flowOutOfCallApValid(call, ret, pos, out, ap, cc) and + if returnCtx instanceof TReturnCtxNone then cc = false else cc = true + ) } - bindingset[call] - pragma[inline_late] - private predicate flowOutOfCallInlineLate( - DataFlowCall call, DataFlowCallable c, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow + pragma[nomagic] + private predicate revFlowParamToReturn( + ParamNodeEx p, FlowState state, ReturnPosition pos, Ap returnAp, Ap ap ) { - PrevStage::callEdgeReturn(call, c, ret, _, out, allowsFieldFlow) - } - - bindingset[c, ret, innercc] - pragma[inline_late] - pragma[noopt] - private predicate flowOutOfCallNotCallContextReduced( - DataFlowCall call, DataFlowCallable c, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - CcNoCall innercc - ) { - viableImplNotCallContextReducedReverse(innercc) and - PrevStage::callEdgeReturn(call, c, ret, _, out, allowsFieldFlow) + revFlow(pragma[only_bind_into](p), state, TReturnCtxMaybeFlowThrough(pos), + apSome(returnAp), pragma[only_bind_into](ap)) and + Stage1::parameterFlowThroughAllowed(p, pos.getKind()) and + PrevStage::parameterMayFlowThrough(p, isNil(ap)) } pragma[nomagic] - private predicate fwdFlowIntoRet( - RetNodeEx ret, FlowState state, CcNoCall cc, SummaryCtx summaryCtx, Typ t, Ap ap, - TypOption stored + private predicate revFlowThrough( + DataFlowCall call, ReturnCtx returnCtx, ParamNodeEx p, FlowState state, ApOption returnAp, + Ap ap ) { - instanceofCcNoCall(cc) and - not outBarrier(ret, state) and - fwdFlow(ret, state, cc, summaryCtx, t, ap, stored) + exists(ReturnPosition pos, Ap innerReturnAp | + revFlowParamToReturn(p, state, pos, innerReturnAp, ap) and + revFlowIsReturned(call, returnCtx, returnAp, pos, innerReturnAp) + ) } + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ pragma[nomagic] - private predicate fwdFlowOutCand( - DataFlowCall call, RetNodeEx ret, CcNoCall innercc, DataFlowCallable inner, NodeEx out, - boolean allowsFieldFlow + private predicate revFlowIsReturned( + DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap ) { - fwdFlowIntoRet(ret, _, innercc, _, _, _, _) and - inner = ret.getEnclosingCallable() and - ( - call = viableImplCallContextReducedReverseInlineLate(inner, innercc) and - flowOutOfCallInlineLate(call, inner, ret, out, allowsFieldFlow) - or - flowOutOfCallNotCallContextReduced(call, inner, ret, out, allowsFieldFlow, innercc) + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, pos, state, returnCtx, _, returnAp, ap) and + returnFlowsThrough(ret, pos, state, ccc, _, _, _, _, ap) and + matchesCall(ccc, call) ) } pragma[nomagic] - private predicate fwdFlowOutValidEdge( - DataFlowCall call, RetNodeEx ret, CcNoCall innercc, DataFlowCallable inner, NodeEx out, - CcNoCall outercc, boolean allowsFieldFlow + predicate storeStepCand( + NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, + DataFlowType containerType ) { - fwdFlowOutCand(call, ret, innercc, inner, out, allowsFieldFlow) and - FwdTypeFlow::typeFlowValidEdgeOut(call, inner) and - outercc = getCallContextReturn(inner, call) + exists(Ap ap2, Ap ap1 | + PrevStage::storeStepCand(node1, c, node2, contentType, containerType) and + revFlowStore(ap2, c, ap1, node1, _, node2, _, _) and + revFlowConsCand(ap2, c, ap1) + ) } - pragma[inline] - private predicate fwdFlowOut( - DataFlowCall call, DataFlowCallable inner, NodeEx out, FlowState state, CcNoCall outercc, - SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored - ) { - exists(RetNodeEx ret, CcNoCall innercc, boolean allowsFieldFlow | - fwdFlowIntoRet(ret, state, innercc, summaryCtx, t, ap, stored) and - fwdFlowOutValidEdge(call, ret, innercc, inner, out, outercc, allowsFieldFlow) and - not inBarrier(out, state) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + predicate readStepCand(NodeEx node1, Content c, NodeEx node2) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2)) and + readStepFwd(node1, ap1, c, node2, ap2) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _) ) } - private module FwdTypeFlowInput implements TypeFlowInput { - predicate enableTypeFlow = Param::enableTypeFlow/0; - - predicate relevantCallEdgeIn = PrevStage::relevantCallEdgeIn/2; - - predicate relevantCallEdgeOut = PrevStage::relevantCallEdgeOut/2; - - pragma[nomagic] - private predicate dataFlowTakenCallEdgeIn0( - DataFlowCall call, DataFlowCallable c, ParamNodeEx p, FlowState state, CcCall innercc, - Typ t, Ap ap, TypOption stored, boolean cc - ) { - FwdFlowInNoThrough::fwdFlowIn(call, _, c, p, state, _, innercc, _, t, ap, stored, cc) - or - FwdFlowInThrough::fwdFlowIn(call, _, c, p, state, _, innercc, _, t, ap, stored, cc) - } - - pragma[nomagic] - private predicate fwdFlow1Param( - ParamNodeEx p, FlowState state, CcCall cc, Typ t0, Ap ap, TypOption stored - ) { - instanceofCcCall(cc) and - fwdFlow1(p, state, cc, _, t0, _, ap, stored) - } - - pragma[nomagic] - predicate dataFlowTakenCallEdgeIn(DataFlowCall call, DataFlowCallable c, boolean cc) { - exists(ParamNodeEx p, FlowState state, CcCall innercc, Typ t, Ap ap, TypOption stored | - dataFlowTakenCallEdgeIn0(call, c, p, state, innercc, t, ap, stored, cc) and - fwdFlow1Param(p, state, innercc, t, ap, stored) - ) - } - - pragma[nomagic] - private predicate dataFlowTakenCallEdgeOut0( - DataFlowCall call, DataFlowCallable c, NodeEx node, FlowState state, Cc cc, Typ t, - Ap ap, TypOption stored - ) { - fwdFlowOut(call, c, node, state, cc, _, t, ap, stored) - } + predicate revFlow(NodeEx node, FlowState state, Ap ap) { revFlow(node, state, _, _, ap) } - pragma[nomagic] - private predicate fwdFlow1Out( - NodeEx node, FlowState state, Cc cc, Typ t0, Ap ap, TypOption stored - ) { - fwdFlow1(node, state, cc, _, t0, _, ap, stored) and - PrevStage::callEdgeReturn(_, _, _, _, node, _) - } + pragma[nomagic] + predicate revFlow(NodeEx node) { revFlow(node, _, _, _, _) } - pragma[nomagic] - predicate dataFlowTakenCallEdgeOut(DataFlowCall call, DataFlowCallable c) { - exists(NodeEx node, FlowState state, Cc cc, Typ t, Ap ap, TypOption stored | - dataFlowTakenCallEdgeOut0(call, c, node, state, cc, t, ap, stored) and - fwdFlow1Out(node, state, cc, t, ap, stored) - ) - } + private predicate fwdConsCand(Content c, Ap ap) { storeStepFwd(_, ap, c, _, _) } - predicate dataFlowNonCallEntry(DataFlowCallable c, boolean cc) { - exists(NodeEx node, FlowState state | - sourceNode(node, state) and - (if hasSourceCallCtx() then cc = true else cc = false) and - PrevStage::revFlow(node, state, any(PrevStage::ApNil nil)) and - c = node.getEnclosingCallable() - ) - or - exists(NodeEx node | - cc = false and - fwdFlowJump(node, _, _, _, _) and - c = node.getEnclosingCallable() - ) - } + private predicate revConsCand(Content c, Ap ap) { + exists(Ap ap2 | + revFlowStore(ap2, c, ap, _, _, _, _, _) and + revFlowConsCand(ap2, c, ap) + ) } - private module FwdTypeFlow = TypeFlow; + private predicate validAp(Ap ap) { + revFlow(_, _, _, _, ap) and ap instanceof ApNil + or + exists(Content head, Ap tail | + consCand(head, tail) and + ap = apCons(head, tail) + ) + } - private predicate flowIntoCallTaken( - DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, boolean emptyAp - ) { - PrevStage::callEdgeArgParam(call, c, arg, p, emptyAp) and - FwdTypeFlowInput::dataFlowTakenCallEdgeIn(call, c, _) + additional predicate consCand(Content c, Ap ap) { + revConsCand(c, ap) and + validAp(ap) } pragma[nomagic] - private predicate fwdFlowRetFromArg( - RetNodeEx ret, FlowState state, CcCall ccc, SummaryCtxSome summaryCtx, Typ t, Ap ap, - TypOption stored + private predicate parameterFlowsThroughRev( + ParamNodeEx p, Ap ap, ReturnPosition pos, Ap returnAp ) { - exists(ReturnKindExt kind, ParamNodeEx p, Ap argAp | - instanceofCcCall(ccc) and - fwdFlow(pragma[only_bind_into](ret), state, ccc, summaryCtx, t, ap, stored) and - summaryCtx = - TSummaryCtxSome(pragma[only_bind_into](p), _, _, pragma[only_bind_into](argAp), _) and - not outBarrier(ret, state) and - kind = ret.getKind() and - parameterFlowThroughAllowed(p, kind) and - PrevStage::returnMayFlowThrough(ret, kind) - ) + revFlow(p, _, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap) and + Stage1::parameterFlowThroughAllowed(p, pos.getKind()) } - pragma[inline] - private predicate fwdFlowThrough0( - DataFlowCall call, ArgNodeEx arg, Cc cc, FlowState state, CcCall ccc, - SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, RetNodeEx ret, - SummaryCtxSome innerSummaryCtx - ) { - fwdFlowRetFromArg(ret, state, ccc, innerSummaryCtx, t, ap, stored) and - fwdFlowIsEntered(call, arg, cc, ccc, summaryCtx, innerSummaryCtx) + pragma[nomagic] + private predicate parameterMayFlowThroughAp(ParamNodeEx p, Ap ap) { + exists(ReturnPosition pos | + returnFlowsThrough(_, pos, _, _, p, _, ap, _, _) and + parameterFlowsThroughRev(p, ap, pos, _) + ) } pragma[nomagic] - private predicate fwdFlowThrough( - DataFlowCall call, Cc cc, FlowState state, SummaryCtx summaryCtx, Typ t, Ap ap, - TypOption stored, RetNodeEx ret - ) { - fwdFlowThrough0(call, _, cc, state, _, summaryCtx, t, ap, stored, ret, _) + predicate parameterMayFlowThrough(ParamNodeEx p, boolean emptyAp) { + exists(Ap ap | + parameterMayFlowThroughAp(p, ap) and + emptyAp = isNil(ap) + ) } pragma[nomagic] - private predicate fwdFlowIsEntered0( - DataFlowCall call, ArgNodeEx arg, Cc cc, CcCall innerCc, SummaryCtx summaryCtx, - ParamNodeEx p, FlowState state, Typ t, Ap ap, TypOption stored - ) { - FwdFlowInThrough::fwdFlowIn(call, arg, _, p, state, cc, innerCc, summaryCtx, t, ap, - stored, _) + private predicate nodeMayUseSummary0(NodeEx n, ParamNodeEx p, FlowState state, Ap ap) { + exists(Ap ap0 | + parameterMayFlowThrough(p, _) and + revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, ap0) and + fwdFlow(n, state, any(CcCall ccc), TSummaryCtxSome(p, _, _, ap, _), _, ap0, _) + ) } /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. + * Holds if `ap` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. */ pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, ArgNodeEx arg, Cc cc, CcCall innerCc, SummaryCtx summaryCtx, - SummaryCtxSome innerSummaryCtx - ) { - exists(ParamNodeEx p, FlowState state, Typ t, Ap ap, TypOption stored | - fwdFlowIsEntered0(call, arg, cc, innerCc, summaryCtx, p, state, t, ap, stored) and - innerSummaryCtx = TSummaryCtxSome(p, state, t, ap, stored) + additional predicate nodeMayUseSummary(NodeEx n, FlowState state, Ap ap) { + exists(ParamNodeEx p | + parameterMayFlowThroughAp(p, ap) and + nodeMayUseSummary0(n, p, state, ap) ) } pragma[nomagic] - private predicate storeStepFwd(NodeEx node1, Ap ap1, Content c, NodeEx node2, Ap ap2) { - fwdFlowStore(node1, _, ap1, _, c, _, _, node2, _, _, _) and - readStepFwd(_, ap2, c, _, ap1) + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind) { + exists(ParamNodeEx p, ReturnPosition pos, Ap argAp, Ap ap | + returnFlowsThrough(ret, pos, _, _, p, _, argAp, _, ap) and + parameterFlowsThroughRev(p, argAp, pos, ap) and + kind = pos.getKind() + ) } pragma[nomagic] - private predicate readStepFwd(NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2) { - fwdFlowRead(n1, _, ap1, _, c, n2, _, ap2, _, _, _, _) + private predicate revFlowThroughArg( + DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, + Ap ap + ) { + exists(ParamNodeEx p | + revFlowThrough(call, returnCtx, p, state, returnAp, ap) and + flowThroughIntoCall(call, arg, p, ap) + ) } pragma[nomagic] - private predicate returnFlowsThrough0( - DataFlowCall call, FlowState state, CcCall ccc, Ap ap, RetNodeEx ret, - SummaryCtxSome innerSummaryCtx - ) { - fwdFlowThrough0(call, _, _, state, ccc, _, _, ap, _, ret, innerSummaryCtx) - } - - pragma[nomagic] - private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Typ argT, - Ap argAp, TypOption argStored, Ap ap - ) { - exists(DataFlowCall call, boolean allowsFieldFlow | - returnFlowsThrough0(call, state, ccc, ap, ret, - TSummaryCtxSome(p, _, argT, argAp, argStored)) and - flowThroughOutOfCall(call, ret, _, allowsFieldFlow) and - pos = ret.getReturnPosition() and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, Ap argAp - ) { - exists(Typ argT, TypOption argStored | - returnFlowsThrough(_, _, _, _, pragma[only_bind_into](p), pragma[only_bind_into](argT), - pragma[only_bind_into](argAp), pragma[only_bind_into](argStored), _) and - flowIntoCallTaken(call, _, pragma[only_bind_into](arg), p, isNil(argAp)) and - fwdFlow(arg, _, _, _, pragma[only_bind_into](argT), pragma[only_bind_into](argAp), - pragma[only_bind_into](argStored)) + predicate callMayFlowThroughRev(DataFlowCall call) { + exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | + revFlow(arg, state, returnCtx, returnAp, ap) and + revFlowThroughArg(call, arg, state, returnCtx, returnAp, ap) ) } - pragma[nomagic] - private predicate flowIntoCallAp( - DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, Ap ap - ) { - flowIntoCallTaken(call, c, arg, p, isNil(ap)) and - fwdFlow(arg, _, _, _, _, ap, _) - } - - pragma[nomagic] - private predicate flowOutOfCallAp( - DataFlowCall call, DataFlowCallable c, RetNodeEx ret, ReturnPosition pos, NodeEx out, - Ap ap, boolean allowsFieldFlow + predicate callEdgeArgParam( + DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, boolean emptyAp ) { - PrevStage::callEdgeReturn(call, c, ret, _, out, allowsFieldFlow) and - fwdFlow(ret, _, _, _, _, ap, _) and - pos = ret.getReturnPosition() and - (if allowsFieldFlow = false then ap instanceof ApNil else any()) and - ( + exists(FlowState state, Ap ap | + flowIntoCallAp(call, c, arg, p, ap) and + revFlow(arg, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and + revFlow(p, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and + emptyAp = isNil(ap) + | // both directions are needed for flow-through - FwdTypeFlowInput::dataFlowTakenCallEdgeIn(call, c, _) or - FwdTypeFlowInput::dataFlowTakenCallEdgeOut(call, c) + RevTypeFlowInput::dataFlowTakenCallEdgeIn(call, c, _) or + RevTypeFlowInput::dataFlowTakenCallEdgeOut(call, c) ) } - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink. - * - * The parameter `returnCtx` records whether (and how) the node must be returned - * from the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. - */ - pragma[nomagic] - additional predicate revFlow( - NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap - ) { - revFlow0(node, state, returnCtx, returnAp, ap) and - fwdFlow(node, state, _, _, _, ap, _) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap + predicate callEdgeReturn( + DataFlowCall call, DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow ) { - fwdFlow(node, state, _, _, _, ap, _) and - sinkNode(node, state) and - ( - if hasSinkCallCtx() - then returnCtx = TReturnCtxNoFlowThrough() - else returnCtx = TReturnCtxNone() - ) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, _, _) and - revFlow(mid, state0, returnCtx, returnAp, ap) - ) - or - exists(NodeEx mid, FlowState state0 | - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, _, _) and - revFlow(mid, state0, returnCtx, returnAp, ap) and - ap instanceof ApNil - ) - or - revFlowJump(node, state, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, returnCtx, returnAp) and - revFlowConsCand(ap0, c, ap) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, returnCtx, returnAp, ap0) and - readStepFwd(node, ap, _, mid, ap0) - ) - or - // flow into a callable - revFlowIn(_, _, node, state, ap) and - returnCtx = TReturnCtxNone() and - returnAp = apNone() - or - // flow through a callable - exists(DataFlowCall call, ParamNodeEx p | - revFlowThrough(call, returnCtx, p, state, returnAp, ap) and - flowThroughIntoCall(call, node, p, ap) - ) - or - // flow out of a callable - exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, _, ap) and - if returnFlowsThrough(node, pos, state, _, _, _, _, _, ap) - then ( - returnCtx = TReturnCtxMaybeFlowThrough(pos) and - returnAp = apSome(ap) - ) else ( - returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone() - ) + exists(FlowState state, ReturnPosition pos, Ap ap | + flowOutOfCallAp(call, c, ret, pos, out, ap, allowsFieldFlow) and + revFlow(ret, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and + revFlow(out, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and + kind = pos.getKind() and + RevTypeFlowInput::dataFlowTakenCallEdgeIn(call, c, _) ) } - private predicate revFlowJump(NodeEx node, FlowState state, Ap ap) { - exists(NodeEx mid | - jumpStepEx(node, mid) and - revFlow(mid, state, _, _, ap) - ) - or - exists(NodeEx mid | - additionalJumpStep(node, mid, _) and - revFlow(pragma[only_bind_into](mid), state, _, _, ap) and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0 | - additionalJumpStateStep(node, state, mid, state0, _) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, ap) and - ap instanceof ApNil - ) + predicate relevantCallEdgeIn(DataFlowCall call, DataFlowCallable c) { + callEdgeArgParam(call, c, _, _, _) } - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, NodeEx mid, ReturnCtx returnCtx, - ApOption returnAp - ) { - revFlow(mid, state, returnCtx, returnAp, ap0) and - storeStepFwd(node, ap, c, mid, ap0) + predicate relevantCallEdgeOut(DataFlowCall call, DataFlowCallable c) { + callEdgeReturn(call, c, _, _, _, _) } + /** Holds if `node1` can step to `node2` in one or more local steps. */ + bindingset[node1, state1] + bindingset[node2, state2] + signature predicate localStepSig( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + DataFlowType t, LocalCallContext lcc, string label + ); + /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. + * Provides a big-step relation for local flow steps. + * + * The big-step releation is based on the `localStepInput` relation, + * restricted to nodes that are forwards and backwards reachable in + * this stage. */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0) - ) - } - - private module RevTypeFlowInput implements TypeFlowInput { - predicate enableTypeFlow = Param::enableTypeFlow/0; - - predicate relevantCallEdgeIn(DataFlowCall call, DataFlowCallable c) { - flowOutOfCallAp(call, c, _, _, _, _, _) - } - - predicate relevantCallEdgeOut(DataFlowCall call, DataFlowCallable c) { - flowIntoCallAp(call, c, _, _, _) - } - - pragma[nomagic] - predicate dataFlowTakenCallEdgeIn(DataFlowCall call, DataFlowCallable c, boolean cc) { - exists(RetNodeEx ret | - revFlowOut(call, ret, _, _, _, cc, _, _) and - c = ret.getEnclosingCallable() - ) - } - - pragma[nomagic] - predicate dataFlowTakenCallEdgeOut(DataFlowCall call, DataFlowCallable c) { - revFlowIn(call, c, _, _, _) + additional module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends NodeEx { + FlowCheckNode() { + revFlow(this) and + ( + flowCheckNode(this) or + Config::neverSkip(this.asNode()) + ) + } } - predicate dataFlowNonCallEntry(DataFlowCallable c, boolean cc) { - exists(NodeEx node, FlowState state, ApNil nil | - fwdFlow(node, state, _, _, _, nil, _) and - sinkNode(node, state) and - (if hasSinkCallCtx() then cc = true else cc = false) and - c = node.getEnclosingCallable() - ) - or - exists(NodeEx node | - cc = false and - revFlowJump(node, _, _) and - c = node.getEnclosingCallable() + private predicate additionalLocalStateStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, DataFlowType t, + LocalCallContext lcc, string label + ) { + exists(ApNil nil | + revFlow(node1, state1, pragma[only_bind_into](nil)) and + revFlow(node2, state2, pragma[only_bind_into](nil)) and + localStepInput(node1, state1, node2, state2, false, t, lcc, label) and + state1 != state2 ) } - } - private module RevTypeFlow = TypeFlow; - - pragma[nomagic] - private predicate flowIntoCallApValid( - DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, Ap ap - ) { - flowIntoCallAp(call, c, arg, p, ap) and - RevTypeFlow::typeFlowValidEdgeOut(call, c) - } - - pragma[nomagic] - private predicate flowOutOfCallApValid( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Ap ap, boolean cc - ) { - exists(DataFlowCallable c | - flowOutOfCallAp(call, c, ret, pos, out, ap, _) and - RevTypeFlow::typeFlowValidEdgeIn(call, c, cc) - ) - } - - private predicate revFlowIn( - DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, FlowState state, Ap ap - ) { - exists(ParamNodeEx p | - revFlow(p, state, TReturnCtxNone(), _, ap) and - flowIntoCallApValid(call, c, arg, p, ap) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, - ReturnCtx returnCtx, boolean cc, ApOption returnAp, Ap ap - ) { - exists(NodeEx out | - revFlow(out, state, returnCtx, returnAp, ap) and - flowOutOfCallApValid(call, ret, pos, out, ap, cc) and - if returnCtx instanceof TReturnCtxNone then cc = false else cc = true - ) - } - - pragma[nomagic] - private predicate revFlowParamToReturn( - ParamNodeEx p, FlowState state, ReturnPosition pos, Ap returnAp, Ap ap - ) { - revFlow(pragma[only_bind_into](p), state, TReturnCtxMaybeFlowThrough(pos), - apSome(returnAp), pragma[only_bind_into](ap)) and - parameterFlowThroughAllowed(p, pos.getKind()) and - PrevStage::parameterMayFlowThrough(p, isNil(ap)) - } - - pragma[nomagic] - private predicate revFlowThrough( - DataFlowCall call, ReturnCtx returnCtx, ParamNodeEx p, FlowState state, ApOption returnAp, - Ap ap - ) { - exists(ReturnPosition pos, Ap innerReturnAp | - revFlowParamToReturn(p, state, pos, innerReturnAp, ap) and - revFlowIsReturned(call, returnCtx, returnAp, pos, innerReturnAp) - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, _, returnAp, ap) and - returnFlowsThrough(ret, pos, state, ccc, _, _, _, _, ap) and - matchesCall(ccc, call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType - ) { - exists(Ap ap2, Ap ap1 | - PrevStage::storeStepCand(node1, c, node2, contentType, containerType) and - revFlowStore(ap2, c, ap1, node1, _, node2, _, _) and - revFlowConsCand(ap2, c, ap1) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2)) and - readStepFwd(node1, ap1, c, node2, ap2) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Ap ap) { revFlow(node, state, _, _, ap) } - - pragma[nomagic] - predicate revFlow(NodeEx node) { revFlow(node, _, _, _, _) } - - private predicate fwdConsCand(Content c, Ap ap) { storeStepFwd(_, ap, c, _, _) } - - private predicate revConsCand(Content c, Ap ap) { - exists(Ap ap2 | - revFlowStore(ap2, c, ap, _, _, _, _, _) and - revFlowConsCand(ap2, c, ap) - ) - } - - private predicate validAp(Ap ap) { - revFlow(_, _, _, _, ap) and ap instanceof ApNil - or - exists(Content head, Ap tail | - consCand(head, tail) and - ap = apCons(head, tail) - ) - } - - additional predicate consCand(Content c, Ap ap) { - revConsCand(c, ap) and - validAp(ap) - } - - pragma[nomagic] - private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition pos, Ap returnAp - ) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap) and - parameterFlowThroughAllowed(p, pos.getKind()) - } - - pragma[nomagic] - private predicate parameterMayFlowThroughAp(ParamNodeEx p, Ap ap) { - exists(ReturnPosition pos | - returnFlowsThrough(_, pos, _, _, p, _, ap, _, _) and - parameterFlowsThroughRev(p, ap, pos, _) - ) - } - - pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, boolean emptyAp) { - exists(Ap ap | - parameterMayFlowThroughAp(p, ap) and - emptyAp = isNil(ap) - ) - } - - pragma[nomagic] - private predicate nodeMayUseSummary0(NodeEx n, ParamNodeEx p, FlowState state, Ap ap) { - exists(Ap ap0 | - parameterMayFlowThrough(p, _) and - revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, ap0) and - fwdFlow(n, state, any(CcCall ccc), TSummaryCtxSome(p, _, _, ap, _), _, ap0, _) - ) - } - - /** - * Holds if `ap` is recorded as the summary context for flow reaching `node` - * and remains relevant for the following pruning stage. - */ - pragma[nomagic] - additional predicate nodeMayUseSummary(NodeEx n, FlowState state, Ap ap) { - exists(ParamNodeEx p | - parameterMayFlowThroughAp(p, ap) and - nodeMayUseSummary0(n, p, state, ap) - ) - } - - pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind) { - exists(ParamNodeEx p, ReturnPosition pos, Ap argAp, Ap ap | - returnFlowsThrough(ret, pos, _, _, p, _, argAp, _, ap) and - parameterFlowsThroughRev(p, argAp, pos, ap) and - kind = pos.getKind() - ) - } - - pragma[nomagic] - private predicate revFlowThroughArg( - DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, - Ap ap - ) { - exists(ParamNodeEx p | - revFlowThrough(call, returnCtx, p, state, returnAp, ap) and - flowThroughIntoCall(call, arg, p, ap) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call) { - exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, returnCtx, returnAp, ap) and - revFlowThroughArg(call, arg, state, returnCtx, returnAp, ap) - ) - } - - predicate callEdgeArgParam( - DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, boolean emptyAp - ) { - exists(FlowState state, Ap ap | - flowIntoCallAp(call, c, arg, p, ap) and - revFlow(arg, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and - revFlow(p, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and - emptyAp = isNil(ap) - | - // both directions are needed for flow-through - RevTypeFlowInput::dataFlowTakenCallEdgeIn(call, c, _) or - RevTypeFlowInput::dataFlowTakenCallEdgeOut(call, c) - ) - } - - predicate callEdgeReturn( - DataFlowCall call, DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, NodeEx out, - boolean allowsFieldFlow - ) { - exists(FlowState state, ReturnPosition pos, Ap ap | - flowOutOfCallAp(call, c, ret, pos, out, ap, allowsFieldFlow) and - revFlow(ret, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and - revFlow(out, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and - kind = pos.getKind() and - RevTypeFlowInput::dataFlowTakenCallEdgeIn(call, c, _) - ) - } - - predicate relevantCallEdgeIn(DataFlowCall call, DataFlowCallable c) { - callEdgeArgParam(call, c, _, _, _) - } - - predicate relevantCallEdgeOut(DataFlowCall call, DataFlowCallable c) { - callEdgeReturn(call, c, _, _, _, _) - } - - /** Holds if `node1` can step to `node2` in one or more local steps. */ - bindingset[node1, state1] - bindingset[node2, state2] - signature predicate localStepSig( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - DataFlowType t, LocalCallContext lcc, string label - ); - - /** - * Provides a big-step relation for local flow steps. - * - * The big-step releation is based on the `localStepInput` relation, - * restricted to nodes that are forwards and backwards reachable in - * this stage. - */ - additional module LocalFlowBigStep { - /** - * A node where some checking is required, and hence the big-step relation - * is not allowed to step over. - */ - private class FlowCheckNode extends NodeEx { - FlowCheckNode() { - revFlow(this) and - ( - flowCheckNode(this) or - Config::neverSkip(this.asNode()) - ) - } - } - - private predicate additionalLocalStateStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, DataFlowType t, - LocalCallContext lcc, string label - ) { - exists(ApNil nil | - revFlow(node1, state1, pragma[only_bind_into](nil)) and - revFlow(node2, state2, pragma[only_bind_into](nil)) and - localStepInput(node1, state1, node2, state2, false, t, lcc, label) and - state1 != state2 - ) - } - - /** - * Holds if `node` can be the first node in a maximal subsequence of local - * flow steps in a dataflow path. - */ - private predicate localFlowEntry(NodeEx node, FlowState state, Ap ap) { - revFlow(node, state, ap) and - ( - sourceNode(node, state) - or - jumpStepEx(_, node) - or - additionalJumpStep(_, node, _) - or - additionalJumpStateStep(_, _, node, state, _) - or - node instanceof ParamNodeEx - or - node instanceof OutNodeEx - or - storeStepCand(_, _, node, _, _) - or - readStepCand(_, _, node) - or - node instanceof FlowCheckNode - or - exists(FlowState s | - additionalLocalStateStep(_, s, node, state, _, _, _) and - s != state - ) - ) - } - - /** - * Holds if `node` can be the last node in a maximal subsequence of local - * flow steps in a dataflow path. - */ - private predicate localFlowExit(NodeEx node, FlowState state, Ap ap) { - revFlow(node, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and - ( - exists(NodeEx next, Ap apNext | revFlow(next, pragma[only_bind_into](state), apNext) | - jumpStepEx(node, next) and - apNext = ap - or - additionalJumpStep(node, next, _) and - apNext = ap and - ap instanceof ApNil - or - callEdgeArgParam(_, _, node, next, _) and - apNext = ap - or - callEdgeReturn(_, _, node, _, next, _) and - apNext = ap - or - storeStepCand(node, _, next, _, _) - or - readStepCand(node, _, next) - ) - or - exists(NodeEx next, FlowState s | - revFlow(next, s, pragma[only_bind_into](ap)) and ap instanceof ApNil - | - additionalJumpStateStep(node, state, next, s, _) - or - additionalLocalStateStep(node, state, next, s, _) and - s != state - ) - or - node instanceof FlowCheckNode - or - sinkNode(node, state) and - ap instanceof ApNil - ) - } - - /** - * Holds if the local path from `node1` to `node2` is a prefix of a maximal - * subsequence of local flow steps in a dataflow path. - * - * This is the transitive closure of `[additional]localFlowStep` beginning - * at `localFlowEntry`. - */ - pragma[nomagic] - private predicate localFlowStepPlus( - NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, - LocalCallContext cc, string label - ) { - not inBarrier(node2, state) and - not outBarrier(node1, state) and - exists(NodeEx mid, boolean preservesValue2, DataFlowType t2, string label2, Ap ap | - localStepInput(mid, state, node2, state, preservesValue2, t2, cc, label2) and - revFlow(node2, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and - not outBarrier(mid, state) and - (preservesValue = true or ap instanceof ApNil) - | - node1 = mid and - localFlowEntry(node1, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and - preservesValue = preservesValue2 and - label = label2 and - t = t2 and - node1 != node2 - or - exists(boolean preservesValue1, DataFlowType t1, string label1 | - localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue1, t1, - cc, label1) and - not mid instanceof FlowCheckNode and - preservesValue = preservesValue2.booleanAnd(preservesValue1) and - label = mergeLabels(label1, label2) and - if preservesValue2 = true then t = t1 else t = t2 - ) - ) - } - - /** - * Holds if `node1` can step to `node2` in one or more local steps and this - * path can occur as a maximal subsequence of local steps in a dataflow path. - */ - pragma[nomagic] - predicate localFlowBigStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - DataFlowType t, LocalCallContext callContext, string label - ) { - exists(Ap ap | - localFlowStepPlus(node1, state1, node2, preservesValue, t, callContext, label) and - localFlowExit(node2, state1, ap) and - state1 = state2 and - node1 != node2 - | - preservesValue = true or ap instanceof ApNil - ) - or - additionalLocalStateStep(node1, state1, node2, state2, t, callContext, label) and - preservesValue = false - } - - /** - * Holds if `node1` can step to `node2` in one or more local steps and this - * path can occur as a maximal subsequence of local steps in a dataflow path. - * - * This predicate should be used when `localStepInput` is already a big-step - * relation, which will do the same as `localFlowBigStep`, but avoids potential - * worst-case quadratic complexity. - */ - pragma[nomagic] - predicate localFlowBigStepTc( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - DataFlowType t, LocalCallContext callContext, string label - ) { - exists(Ap ap | - localFlowEntry(node1, pragma[only_bind_into](state1), pragma[only_bind_into](ap)) and - localStepInput(node1, state1, node2, state2, preservesValue, t, callContext, label) and - localFlowExit(node2, pragma[only_bind_into](state2), pragma[only_bind_into](ap)) and - state1 = state2 - | - preservesValue = true or ap instanceof ApNil - ) - or - additionalLocalStateStep(node1, state1, node2, state2, t, callContext, label) and - preservesValue = false - } - } - - /** - * Provides a graph representation of the data flow in this stage suitable for use in a `path-problem` query. - */ - additional module Graph { - private newtype TPathNode = - TPathNodeMid( - NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, - TypOption stored - ) { - fwdFlow(node, state, cc, summaryCtx, t, ap, stored) and - revFlow(node, state, _, _, ap) - } or - TPathNodeSink(NodeEx node, FlowState state) { - exists(PathNodeMid sink | - sink.isAtSink() and - node = sink.toNormalSinkNodeEx() and - state = sink.getState() - ) - } or - TPathNodeSrcGrp() or - TPathNodeSinkGrp() - - class PathNodeImpl extends TPathNode { - abstract NodeEx getNodeEx(); - - /** Gets the `FlowState` of this node. */ - abstract FlowState getState(); - - /** Holds if this node is a source. */ - abstract predicate isSource(); - - /** Holds if this node is a sink. */ - predicate isSink() { this instanceof TPathNodeSink } - - abstract PathNodeImpl getASuccessorImpl(string label); - - pragma[nomagic] - PathNodeImpl getAnImplicitReadSuccessorAtSink(string label) { - exists(PathNodeMid readTarget | - result = this.getASuccessorImpl(_) and - localStep(this, readTarget, _) and - readTarget.getNodeEx().isImplicitReadNode(_) - | - // last implicit read, leaving the access path empty - result = readTarget.projectToSink(label) - or - // implicit read, leaving the access path non-empty - exists(result.getAnImplicitReadSuccessorAtSink(label)) and - result = readTarget - ) - } - - private PathNodeImpl getASuccessorIfHidden(string label) { - this.isHidden() and - result = this.getASuccessorImpl(label) - or - result = this.getAnImplicitReadSuccessorAtSink(label) - } - - private PathNodeImpl getASuccessorFromNonHidden(string label) { - result = this.getASuccessorImpl(label) and - not this.isHidden() and - // In cases like - // - // ``` - // x.Field = taint; - // Sink(x); - // ``` - // - // we only want the direct edge - // - // `[post update] x [Field]` -> `x` - // - // and not the two edges - // - // `[post update] x [Field]` -> `x [Field]` - // `x [Field]` -> `x` - // - // which the restriction below ensures. - not result = this.getAnImplicitReadSuccessorAtSink(_) - or - exists(string l1, string l2 | - result = this.getASuccessorFromNonHidden(l1).getASuccessorIfHidden(l2) and - label = mergeLabels(l1, l2) - ) - } - - final PathNodeImpl getANonHiddenSuccessor(string label) { - result = this.getASuccessorFromNonHidden(label) and not result.isHidden() - } - - predicate isHidden() { - not Config::includeHiddenNodes() and - hiddenNode(this.getNodeEx()) and - not this.isSource() and - not this instanceof PathNodeSink - } - - /** Gets a textual representation of this element. */ - abstract string toString(); - - /** Gets the location of this node. */ - Location getLocation() { result = this.getNodeEx().getLocation() } - - predicate isArbitrarySource() { this instanceof TPathNodeSrcGrp } - - predicate isArbitrarySink() { this instanceof TPathNodeSinkGrp } - } - - private class PathNodeSrcGrp extends PathNodeImpl, TPathNodeSrcGrp { - override string toString() { result = "" } - - override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) } - - override NodeEx getNodeEx() { none() } - - override FlowState getState() { none() } - - override PathNodeImpl getASuccessorImpl(string label) { - result.isSource() and label = "" - } - - override predicate isSource() { none() } - } - - private class PathNodeSinkGrp extends PathNodeImpl, TPathNodeSinkGrp { - override string toString() { result = "" } - - override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) } - - override NodeEx getNodeEx() { none() } - - override FlowState getState() { none() } - - override PathNodeImpl getASuccessorImpl(string label) { none() } - - override predicate isSource() { none() } - } - - /** - * An intermediate flow graph node. This is a tuple consisting of a node, - * a `FlowState`, a call context, a summary context, a tracked type, and an access path. - */ - private class PathNodeMid extends PathNodeImpl, TPathNodeMid { - NodeEx node; - FlowState state; - Cc cc; - SummaryCtx summaryCtx; - Typ t; - Ap ap; - TypOption stored; - - PathNodeMid() { this = TPathNodeMid(node, state, cc, summaryCtx, t, ap, stored) } - - override NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - private PathNodeMid getSuccMid(string label) { - localStep(this, result, label) - or - nonLocalStep(this, result, label) - } - - private predicate isSourceWithLabel(string labelprefix) { - exists(string model | - this.isSource() and - sourceModel(node, model) and - model != "" and - labelprefix = "Src:" + model + " " - ) - } - - /** If this node corresponds to a sink, gets the normal node for that sink. */ - pragma[nomagic] - NodeEx toNormalSinkNodeEx() { - exists(Node n | - pragma[only_bind_out](node.asNodeOrImplicitRead()) = n and - (isRelevantSink(n) or isRelevantSink(n, _)) and - result.asNode() = n - ) - } - - override PathNodeImpl getASuccessorImpl(string label) { - // an intermediate step to another intermediate node - exists(string l2 | result = this.getSuccMid(l2) | - not this.isSourceWithLabel(_) and label = l2 - or - exists(string l1 | - this.isSourceWithLabel(l1) and - label = l1 + l2 - ) - ) - or - // a final step to a sink - exists(string l2, string sinkLabel | - result = this.getSuccMid(l2).projectToSink(sinkLabel) - | - not this.isSourceWithLabel(_) and - label = mergeLabels(l2, sinkLabel) - or - exists(string l1 | - this.isSourceWithLabel(l1) and - label = l1 + mergeLabels(l2, sinkLabel) - ) - ) - } - - private string ppType() { - exists(string ppt | ppt = t.toString() | - if ppt = "" then result = "" else result = " : " + ppt - ) - } - - private string ppAp() { - exists(string s | s = ap.toString() | - if s = "" then result = "" else result = " " + s - ) - } - - private string ppCtx() { result = " <" + cc + ">" } - - private string ppSummaryCtx() { - summaryCtx instanceof SummaryCtxNone and result = "" - or - summaryCtx instanceof SummaryCtxSome and - result = " <" + summaryCtx + ">" - } - - override string toString() { - result = node.toString() + this.ppType() + this.ppAp() + ppStored(stored) - } - - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - string toStringWithContext() { - result = - node.toString() + this.ppType() + this.ppAp() + ppStored(stored) + this.ppCtx() + - this.ppSummaryCtx() - } - - override predicate isSource() { - sourceNode(node, state) and - (if hasSourceCallCtx() then cc = ccSomeCall() else cc = ccNone()) and - summaryCtx = TSummaryCtxNone() and - t = getNodeTyp(node) and - ap instanceof ApNil - } - - predicate isAtSink() { - sinkNode(node, state) and - ap instanceof ApNil and - // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall` - // is exactly what we need to check. - // For `FeatureEqualSourceSinkCallContext` the initial call - // context was set to `CallContextSomeCall` and jumps are - // disallowed, so `cc instanceof CallContextNoCall` never holds. - // On the other hand, in this case there's never any need to - // enter a call except to identify a summary, so the condition in - // conjunction with setting the summary context enforces this, - // which means that the summary context being empty holds if and - // only if we are in the call context of the source. - if Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext - then summaryCtx = TSummaryCtxNone() - else - if Config::getAFeature() instanceof FeatureHasSinkCallContext - then instanceofCcNoCall(cc) - else any() - } - - PathNodeSink projectToSink(string label) { - exists(string model | - this.isAtSink() and - sinkModel(node, model) and - result.getNodeEx() = this.toNormalSinkNodeEx() and - result.getState() = state and - if model != "" then label = "Sink:" + model else label = "" - ) - } - } - - /** - * A flow graph node corresponding to a sink. This is disjoint from the - * intermediate nodes in order to uniquely correspond to a given sink by - * excluding the call context. - */ - private class PathNodeSink extends PathNodeImpl, TPathNodeSink { - NodeEx node; - FlowState state; - - PathNodeSink() { this = TPathNodeSink(node, state) } - - override NodeEx getNodeEx() { result = node } - - override FlowState getState() { result = state } - - override string toString() { result = node.toString() } - - override PathNodeImpl getASuccessorImpl(string label) { - result.isArbitrarySink() and label = "" - } - - override predicate isSource() { sourceNode(node, state) } - } - - bindingset[p, state, t, ap, stored] - pragma[inline_late] - private SummaryCtxSome mkSummaryCtxSome( - ParamNodeEx p, FlowState state, Typ t, Ap ap, TypOption stored - ) { - result = TSummaryCtxSome(p, state, t, ap, stored) - } - - pragma[nomagic] - private predicate fwdFlowInStep( - ArgNodeEx arg, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc, - SummaryCtx outerSummaryCtx, SummaryCtx innerSummaryCtx, Typ t, Ap ap, TypOption stored - ) { - FwdFlowInNoThrough::fwdFlowIn(_, arg, _, p, state, outercc, innercc, outerSummaryCtx, t, - ap, stored, _) and - innerSummaryCtx = TSummaryCtxNone() - or - FwdFlowInThrough::fwdFlowIn(_, arg, _, p, state, outercc, innercc, outerSummaryCtx, t, - ap, stored, _) and - innerSummaryCtx = mkSummaryCtxSome(p, state, t, ap, stored) - } - - pragma[nomagic] - private predicate fwdFlowThroughStep0( - DataFlowCall call, ArgNodeEx arg, Cc cc, FlowState state, CcCall ccc, - SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, RetNodeEx ret, - SummaryCtxSome innerSummaryCtx - ) { - fwdFlowThrough0(call, arg, cc, state, ccc, summaryCtx, t, ap, stored, ret, - innerSummaryCtx) - } - - bindingset[node, state, cc, summaryCtx, t, ap, stored] - pragma[inline_late] - private PathNodeImpl mkPathNode( - NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, - TypOption stored - ) { - result = TPathNodeMid(node, state, cc, summaryCtx, t, ap, stored) - } - - private PathNodeImpl typeStrengthenToPathNode( - NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, - TypOption stored - ) { - exists(Typ t | - fwdFlow1(node, state, cc, summaryCtx, t0, t, ap, stored) and - result = TPathNodeMid(node, state, cc, summaryCtx, t, ap, stored) - ) - } - - pragma[nomagic] - private predicate fwdFlowThroughStep1( - PathNodeImpl pn1, PathNodeImpl pn2, PathNodeImpl pn3, DataFlowCall call, Cc cc, - FlowState state, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, RetNodeEx ret - ) { - exists( - FlowState state0, ArgNodeEx arg, SummaryCtxSome innerSummaryCtx, ParamNodeEx p, - Typ innerArgT, Ap innerArgAp, TypOption innerArgStored, CcCall ccc - | - fwdFlowThroughStep0(call, arg, cc, state, ccc, summaryCtx, t, ap, stored, ret, - innerSummaryCtx) and - innerSummaryCtx = TSummaryCtxSome(p, state0, innerArgT, innerArgAp, innerArgStored) and - pn1 = mkPathNode(arg, state0, cc, summaryCtx, innerArgT, innerArgAp, innerArgStored) and - pn2 = - typeStrengthenToPathNode(p, state0, ccc, innerSummaryCtx, innerArgT, innerArgAp, - innerArgStored) and - pn3 = mkPathNode(ret, state, ccc, innerSummaryCtx, t, ap, stored) - ) - } - - pragma[nomagic] - private predicate fwdFlowThroughStep2( - PathNodeImpl pn1, PathNodeImpl pn2, PathNodeImpl pn3, NodeEx node, Cc cc, - FlowState state, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored - ) { - exists(DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow | - fwdFlowThroughStep1(pn1, pn2, pn3, call, cc, state, summaryCtx, t, ap, stored, ret) and - flowThroughOutOfCall(call, ret, node, allowsFieldFlow) and - not inBarrier(node, state) and - if allowsFieldFlow = false then ap instanceof ApNil else any() + /** + * Holds if `node` can be the first node in a maximal subsequence of local + * flow steps in a dataflow path. + */ + private predicate localFlowEntry(NodeEx node, FlowState state, Ap ap) { + revFlow(node, state, ap) and + ( + sourceNode(node, state) + or + jumpStepEx(_, node) + or + additionalJumpStep(_, node, _) + or + additionalJumpStateStep(_, _, node, state, _) + or + node instanceof ParamNodeEx + or + node instanceof OutNodeEx + or + storeStepCand(_, _, node, _, _) + or + readStepCand(_, _, node) + or + node instanceof FlowCheckNode + or + exists(FlowState s | + additionalLocalStateStep(_, s, node, state, _, _, _) and + s != state + ) ) } - private predicate localStep( - PathNodeImpl pn1, NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, - Ap ap, TypOption stored, string label, boolean isStoreStep - ) { - exists(NodeEx mid, FlowState state0, Typ t0, LocalCc localCc | - pn1 = TPathNodeMid(mid, state0, cc, summaryCtx, t0, ap, stored) and - localCc = getLocalCc(cc) and - isStoreStep = false - | - localStep(mid, state0, node, state, true, _, localCc, label) and - t = t0 + /** + * Holds if `node` can be the last node in a maximal subsequence of local + * flow steps in a dataflow path. + */ + private predicate localFlowExit(NodeEx node, FlowState state, Ap ap) { + revFlow(node, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and + ( + exists(NodeEx next, Ap apNext | revFlow(next, pragma[only_bind_into](state), apNext) | + jumpStepEx(node, next) and + apNext = ap + or + additionalJumpStep(node, next, _) and + apNext = ap and + ap instanceof ApNil + or + callEdgeArgParam(_, _, node, next, _) and + apNext = ap + or + callEdgeReturn(_, _, node, _, next, _) and + apNext = ap + or + storeStepCand(node, _, next, _, _) + or + readStepCand(node, _, next) + ) or - localStep(mid, state0, node, state, false, t, localCc, label) and + exists(NodeEx next, FlowState s | + revFlow(next, s, pragma[only_bind_into](ap)) and ap instanceof ApNil + | + additionalJumpStateStep(node, state, next, s, _) + or + additionalLocalStateStep(node, state, next, s, _, _, _) and + s != state + ) + or + node instanceof FlowCheckNode + or + sinkNode(node, state) and ap instanceof ApNil ) - or - // store - exists(NodeEx mid, Content c, Typ t0, Ap ap0, TypOption stored0 | - pn1 = TPathNodeMid(mid, state, cc, summaryCtx, t0, ap0, stored0) and - fwdFlowStore(mid, t0, ap0, stored0, c, t, stored, node, state, cc, summaryCtx) and - ap = apCons(c, ap0) and - label = "" and - isStoreStep = true - ) - or - // read - exists(NodeEx mid, Typ t0, Ap ap0, TypOption stored0 | - pn1 = TPathNodeMid(mid, state, cc, summaryCtx, t0, ap0, stored0) and - fwdFlowRead(mid, t0, ap0, stored0, _, node, t, ap, stored, state, cc, summaryCtx) and - label = "" and - isStoreStep = false - ) } - private predicate localStep(PathNodeImpl pn1, PathNodeImpl pn2, string label) { - exists( - NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, - TypOption stored, boolean isStoreStep - | - localStep(pn1, node, state, cc, summaryCtx, t0, ap, stored, label, isStoreStep) and - pn2 = typeStrengthenToPathNode(node, state, cc, summaryCtx, t0, ap, stored) and - stepFilter(node, ap, isStoreStep) - ) - or - summaryStep(pn1, pn2, label) - } - - private predicate summaryLabel(PathNodeImpl pn1, PathNodeImpl pn2, string summaryLabel) { - pn1 = pn2 and - summaryLabel = "" and - subpathsImpl(_, pn1, _, _) - or - exists(PathNodeImpl mid, string l1, string l2 | - summaryLabel(pn1, mid, l1) and - localStep(mid, pn2, l2) and - summaryLabel = mergeLabels(l1, l2) - ) - } - - private predicate summaryStep(PathNodeImpl arg, PathNodeImpl out, string label) { - exists(PathNodeImpl par, PathNodeImpl ret | - subpathsImpl(arg, par, ret, out) and - summaryLabel(par, ret, label) - ) - } - - private predicate nonLocalStep( - PathNodeImpl pn1, NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, - Ap ap, TypOption stored, string label + /** + * Holds if the local path from `node1` to `node2` is a prefix of a maximal + * subsequence of local flow steps in a dataflow path. + * + * This is the transitive closure of `[additional]localFlowStep` beginning + * at `localFlowEntry`. + */ + pragma[nomagic] + private predicate localFlowStepPlus( + NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, + LocalCallContext cc, string label ) { - // jump - exists(NodeEx mid, FlowState state0, Typ t0 | - pn1 = TPathNodeMid(mid, state0, _, _, t0, ap, stored) and - cc = ccNone() and - summaryCtx = TSummaryCtxNone() - | - jumpStepEx(mid, node) and - state = state0 and - not outBarrier(mid, state) and - not inBarrier(node, state) and - t = t0 and - label = "" - or - additionalJumpStep(mid, node, label) and - state = state0 and + not inBarrier(node2, state) and + not outBarrier(node1, state) and + exists(NodeEx mid, boolean preservesValue2, DataFlowType t2, string label2, Ap ap | + localStepInput(mid, state, node2, state, preservesValue2, t2, cc, label2) and + revFlow(node2, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and not outBarrier(mid, state) and - not inBarrier(node, state) and - t = getNodeTyp(node) and - ap instanceof ApNil + (preservesValue = true or ap instanceof ApNil) + | + node1 = mid and + localFlowEntry(node1, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and + preservesValue = preservesValue2 and + label = label2 and + t = t2 and + node1 != node2 or - additionalJumpStateStep(mid, state0, node, state, label) and - t = getNodeTyp(node) and - ap instanceof ApNil - ) - or - // flow into a callable - exists(ArgNodeEx arg, Cc outercc, SummaryCtx outerSummaryCtx | - pn1 = TPathNodeMid(arg, state, outercc, outerSummaryCtx, t, ap, stored) and - fwdFlowInStep(arg, node, state, outercc, cc, outerSummaryCtx, summaryCtx, t, ap, - stored) and - label = "" - ) - or - // flow out of a callable - exists(RetNodeEx ret, CcNoCall innercc, boolean allowsFieldFlow | - pn1 = TPathNodeMid(ret, state, innercc, summaryCtx, t, ap, stored) and - fwdFlowIntoRet(ret, state, innercc, summaryCtx, t, ap, stored) and - fwdFlowOutValidEdge(_, ret, innercc, _, node, cc, allowsFieldFlow) and - not inBarrier(node, state) and - label = "" and - if allowsFieldFlow = false then ap instanceof ApNil else any() + exists(boolean preservesValue1, DataFlowType t1, string label1 | + localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue1, t1, + cc, label1) and + not mid instanceof FlowCheckNode and + preservesValue = preservesValue2.booleanAnd(preservesValue1) and + label = mergeLabels(label1, label2) and + if preservesValue2 = true then t = t1 else t = t2 + ) ) } - private predicate nonLocalStep(PathNodeImpl pn1, PathNodeImpl pn2, string label) { - exists( - NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, - TypOption stored + /** + * Holds if `node1` can step to `node2` in one or more local steps and this + * path can occur as a maximal subsequence of local steps in a dataflow path. + */ + pragma[nomagic] + predicate localFlowBigStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + DataFlowType t, LocalCallContext callContext, string label + ) { + exists(Ap ap | + localFlowStepPlus(node1, state1, node2, preservesValue, t, callContext, label) and + localFlowExit(node2, state1, ap) and + state1 = state2 and + node1 != node2 | - nonLocalStep(pn1, node, state, cc, summaryCtx, t0, ap, stored, label) and - pn2 = typeStrengthenToPathNode(node, state, cc, summaryCtx, t0, ap, stored) and - stepFilter(node, ap, false) + preservesValue = true or ap instanceof ApNil ) + or + additionalLocalStateStep(node1, state1, node2, state2, t, callContext, label) and + preservesValue = false } /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple. + * Holds if `node1` can step to `node2` in one or more local steps and this + * path can occur as a maximal subsequence of local steps in a dataflow path. * - * All of the nodes may be hidden. + * This predicate should be used when `localStepInput` is already a big-step + * relation, which will do the same as `localFlowBigStep`, but avoids potential + * worst-case quadratic complexity. */ - private predicate subpathsImpl( - PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out + pragma[nomagic] + predicate localFlowBigStepTc( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + DataFlowType t, LocalCallContext callContext, string label ) { - exists( - NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, - TypOption stored, PathNodeImpl out0 - | - fwdFlowThroughStep2(arg, par, ret, node, cc, state, summaryCtx, t0, ap, stored) and - out0 = typeStrengthenToPathNode(node, state, cc, summaryCtx, t0, ap, stored) and - stepFilter(node, ap, false) + exists(Ap ap | + localFlowEntry(node1, pragma[only_bind_into](state1), pragma[only_bind_into](ap)) and + localStepInput(node1, state1, node2, state2, preservesValue, t, callContext, label) and + localFlowExit(node2, pragma[only_bind_into](state2), pragma[only_bind_into](ap)) and + state1 = state2 | - out = out0 or out = out0.(PathNodeMid).projectToSink(_) + preservesValue = true or ap instanceof ApNil ) + or + additionalLocalStateStep(node1, state1, node2, state2, t, callContext, label) and + preservesValue = false } + } - module StagePathGraph { - predicate edges(PathNodeImpl a, PathNodeImpl b, string key, string val) { - a.getASuccessorImpl(val) = b and - key = "provenance" - } - - query predicate nodes(PathNodeImpl n, string key, string val) { - key = "semmle.label" and val = n.toString() - } - - query predicate subpaths = subpathsImpl/4; - } - - module Public { - private PathNodeImpl localStep(PathNodeImpl n) { localStep(n, result, _) } - - private predicate localStepToHidden(PathNodeImpl n1, PathNodeImpl n2) { - n2 = localStep(n1) and - n2.isHidden() - } - - private predicate localStepFromHidden(PathNodeImpl n1, PathNodeImpl n2) { - n2 = localStep(n1) and - n1.isHidden() - or - n2 = n1.getAnImplicitReadSuccessorAtSink(_) - } - - bindingset[par, ret] - pragma[inline_late] - private predicate localStepStar(PathNodeImpl par, PathNodeImpl ret) { - localStep*(par) = ret - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple. - * - * `par` and `ret` are not hidden. - */ - pragma[nomagic] - private predicate subpaths1( - PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out - ) { - // direct subpath - subpathsImpl(arg, any(PathNodeImpl n | localStepFromHidden*(n, par)), - any(PathNodeImpl n | localStepToHidden*(ret, n)), out) and - not par.isHidden() and - not ret.isHidden() and - localStepStar(par, ret) - or - // wrapped subpath using hidden nodes, e.g. flow through a callback inside - // a summarized callable - exists(PathNodeImpl par0, PathNodeImpl ret0 | - subpaths1(any(PathNodeImpl n | localStepToHidden*(par0, n)), par, ret, - any(PathNodeImpl n | localStepFromHidden*(n, ret0))) and - subpathsImpl(arg, par0, ret0, out) - ) - } - - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - * - * None of the nodes are hidden. - */ - pragma[nomagic] - private predicate subpaths2( - PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out + /** + * Provides a graph representation of the data flow in this stage suitable for use in a `path-problem` query. + */ + additional module Graph { + private newtype TPathNode = + TPathNodeMid( + NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, + TypOption stored ) { - exists(PathNodeImpl out0 | - subpaths1(any(PathNodeImpl n | localStepToHidden*(arg, n)), par, ret, - any(PathNodeImpl n | localStepFromHidden*(n, out0))) and - not arg.isHidden() and - not out0.isHidden() - | - out = out0 or out = out0.(PathNodeMid).projectToSink(_) + fwdFlow(node, state, cc, summaryCtx, t, ap, stored) and + revFlow(node, state, _, _, ap) + } or + TPathNodeSink(NodeEx node, FlowState state) { + exists(PathNodeMid sink | + sink.isAtSink() and + node = sink.toNormalSinkNodeEx() and + state = sink.getState() ) - } - - /** Holds if `n` is reachable from a source. */ - private predicate fwdReach(PathNodeImpl n) { - n.isArbitrarySource() - or - exists(PathNodeImpl mid | fwdReach(mid) and mid.getANonHiddenSuccessor(_) = n) - } + } or + TPathNodeSrcGrp() or + TPathNodeSinkGrp() - /** Holds if `n` is reachable from a source and can reach a sink. */ - private predicate directReach(PathNodeImpl n) { - fwdReach(n) and - ( - n.isArbitrarySink() or - directReach(n.getANonHiddenSuccessor(_)) - ) - } + class PathNodeImpl extends TPathNode { + abstract NodeEx getNodeEx(); - /** - * Holds if `n` can reach a return node in a summarized subpath that can reach a sink. - */ - private predicate retReach(PathNodeImpl n) { - fwdReach(n) and - ( - exists(PathNodeImpl out | subpaths2(_, _, n, out) | - directReach(out) or retReach(out) - ) - or - exists(PathNodeImpl mid | - retReach(mid) and - n.getANonHiddenSuccessor(_) = mid and - not subpaths2(_, mid, _, _) - ) - ) - } + /** Gets the `FlowState` of this node. */ + abstract FlowState getState(); - /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ - private predicate reach(PathNodeImpl n) { directReach(n) or retReach(n) } + /** Holds if this node is a source. */ + abstract predicate isSource(); - /** - * A `Node` augmented with a call context (except for sinks) and an access path. - * Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated. - */ - class PathNode instanceof PathNodeImpl { - PathNode() { - reach(this) and - not this instanceof PathNodeSrcGrp and - not this instanceof PathNodeSinkGrp - } + /** Holds if this node is a sink. */ + predicate isSink() { this instanceof TPathNodeSink } - /** Gets a textual representation of this element. */ - final string toString() { result = super.toString() } + abstract PathNodeImpl getASuccessorImpl(string label); - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - final string toStringWithContext() { - result = this.(PathNodeMid).toStringWithContext() + pragma[nomagic] + PathNodeImpl getAnImplicitReadSuccessorAtSink(string label) { + exists(PathNodeMid readTarget | + result = this.getASuccessorImpl(_) and + localStep(this, readTarget, _) and + readTarget.getNodeEx().isImplicitReadNode(_) + | + // last implicit read, leaving the access path empty + result = readTarget.projectToSink(label) or - not this instanceof PathNodeMid and result = this.toString() - } - - /** Gets the location of this node. */ - Location getLocation() { result = super.getLocation() } - - /** Gets the underlying `Node`. */ - final Node getNode() { super.getNodeEx().projectToNode() = result } - - /** Gets the parameter node through which data is returned, if any. */ - final ParameterNode asParameterReturnNode() { - result = super.getNodeEx().asParamReturnNode() - } - - /** Gets the `FlowState` of this node. */ - final FlowState getState() { result = super.getState() } - - /** Gets a successor of this node, if any. */ - final PathNode getASuccessor() { result = super.getANonHiddenSuccessor(_) } - - /** Holds if this node is a source. */ - final predicate isSource() { super.isSource() } - - /** Holds if this node is a sink. */ - final predicate isSink() { this instanceof PathNodeSink } - - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - pragma[inline] - deprecated final predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getLocation() - .hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - - /** - * DEPRECATED: This functionality is no longer available. - * - * Holds if this node is a grouping of source nodes. - */ - deprecated final predicate isSourceGroup(string group) { none() } - - /** - * DEPRECATED: This functionality is no longer available. - * - * Holds if this node is a grouping of sink nodes. - */ - deprecated final predicate isSinkGroup(string group) { none() } + // implicit read, leaving the access path non-empty + exists(result.getAnImplicitReadSuccessorAtSink(label)) and + result = readTarget + ) } - /** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */ - private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) { - n1.getANonHiddenSuccessor(_) = n2 and directReach(n2) + private PathNodeImpl getASuccessorIfHidden(string label) { + this.isHidden() and + result = this.getASuccessorImpl(label) + or + result = this.getAnImplicitReadSuccessorAtSink(label) } - private predicate tcSrc(PathNodeImpl n) { n.isSource() } - - private predicate tcSink(PathNodeImpl n) { n.isSink() } + private PathNodeImpl getASuccessorFromNonHidden(string label) { + result = this.getASuccessorImpl(label) and + not this.isHidden() and + // In cases like + // + // ``` + // x.Field = taint; + // Sink(x); + // ``` + // + // we only want the direct edge + // + // `[post update] x [Field]` -> `x` + // + // and not the two edges + // + // `[post update] x [Field]` -> `x [Field]` + // `x [Field]` -> `x` + // + // which the restriction below ensures. + not result = this.getAnImplicitReadSuccessorAtSink(_) + or + exists(string l1, string l2 | + result = this.getASuccessorFromNonHidden(l1).getASuccessorIfHidden(l2) and + label = mergeLabels(l1, l2) + ) + } - private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = - doublyBoundedFastTC(pathSucc/2, tcSrc/1, tcSink/1)(n1, n2) + final PathNodeImpl getANonHiddenSuccessor(string label) { + result = this.getASuccessorFromNonHidden(label) and not result.isHidden() + } - /** - * Holds if data can flow from `source` to `sink`. - * - * The corresponding paths are generated from the end-points and the graph - * included in the module `PathGraph`. - */ - predicate flowPath(PathNode source, PathNode sink) { - ( - // When there are both sources and sinks in the diff range, - // diff-informed dataflow falls back to computing all paths without - // any filtering. To prevent significant alert flip-flopping due to - // minor code changes triggering the fallback, we consistently apply - // source-or-sink filtering here to ensure that we return the same - // paths regardless of whether the fallback is triggered. - if Config::observeDiffInformedIncrementalMode() - then isRelevantSourceSinkPair(source.getNode(), sink.getNode()) - else any() - ) and - exists(PathNodeImpl flowsource, PathNodeImpl flowsink | - source = flowsource and sink = flowsink - | - flowsource.isSource() and - (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and - flowsink.isSink() - ) + predicate isHidden() { + not Config::includeHiddenNodes() and + hiddenNode(this.getNodeEx()) and + not this.isSource() and + not this instanceof PathNodeSink } - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PathGraph implements PathGraphSig { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PathNode a, PathNode b, string key, string val) { - a.(PathNodeImpl).getANonHiddenSuccessor(val) = b and - key = "provenance" - } + /** Gets a textual representation of this element. */ + abstract string toString(); - /** Holds if `n` is a node in the graph of data flow path explanations. */ - query predicate nodes(PathNode n, string key, string val) { - key = "semmle.label" and val = n.toString() - } + /** Gets the location of this node. */ + Location getLocation() { result = this.getNodeEx().getLocation() } - /** - * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through - * a subpath between `par` and `ret` with the connecting edges `arg -> par` and - * `ret -> out` is summarized as the edge `arg -> out`. - */ - query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { - subpaths2(arg, par, ret, out) - } - } - } - } + predicate isArbitrarySource() { this instanceof TPathNodeSrcGrp } - additional predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, int calledges, - int tfnodes, int tftuples - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, _)) and - fields = count(Content f0 | fwdConsCand(f0, _)) and - conscand = count(Content f0, Ap ap | fwdConsCand(f0, ap)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, _, _)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, - TypOption stored | fwdFlow(n, state, cc, summaryCtx, t, ap, stored)) and - calledges = - count(DataFlowCall call, DataFlowCallable c | - FwdTypeFlowInput::dataFlowTakenCallEdgeIn(call, c, _) or - FwdTypeFlowInput::dataFlowTakenCallEdgeOut(call, c) - ) and - FwdTypeFlow::typeFlowStats(tfnodes, tftuples) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _)) and - fields = count(Content f0 | consCand(f0, _)) and - conscand = count(Content f0, Ap ap | consCand(f0, ap)) and - states = count(FlowState state | revFlow(_, state, _, _, _)) and - tuples = - count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | - revFlow(n, state, returnCtx, retAp, ap) - ) and - calledges = - count(DataFlowCall call, DataFlowCallable c | - RevTypeFlowInput::dataFlowTakenCallEdgeIn(call, c, _) or - RevTypeFlowInput::dataFlowTakenCallEdgeOut(call, c) - ) and - RevTypeFlow::typeFlowStats(tfnodes, tftuples) - } - /* End: Stage logic. */ - } - } + predicate isArbitrarySink() { this instanceof TPathNodeSinkGrp } + } - private module BooleanCallContext { - class Cc = Boolean; + private class PathNodeSrcGrp extends PathNodeImpl, TPathNodeSrcGrp { + override string toString() { result = "" } - class CcCall extends Cc { - CcCall() { this = true } - } + override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) } - /** Holds if the call context may be `call`. */ - predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + override NodeEx getNodeEx() { none() } - class CcNoCall extends Cc { - CcNoCall() { this = false } - } + override FlowState getState() { none() } - Cc ccNone() { result = false } + override PathNodeImpl getASuccessorImpl(string label) { + result.isSource() and label = "" + } - CcCall ccSomeCall() { result = true } + override predicate isSource() { none() } + } - predicate instanceofCc(Cc cc) { any() } + private class PathNodeSinkGrp extends PathNodeImpl, TPathNodeSinkGrp { + override string toString() { result = "" } - predicate instanceofCcCall(CcCall cc) { any() } + override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) } - predicate instanceofCcNoCall(CcNoCall cc) { any() } + override NodeEx getNodeEx() { none() } - class LocalCc = Unit; + override FlowState getState() { none() } - bindingset[cc] - LocalCc getLocalCc(Cc cc) { any() } + override PathNodeImpl getASuccessorImpl(string label) { none() } - DataFlowCallable viableImplCallContextReduced(DataFlowCall call, CcCall ctx) { none() } + override predicate isSource() { none() } + } - bindingset[call, ctx] - predicate viableImplNotCallContextReduced(DataFlowCall call, Cc ctx) { any() } + /** + * An intermediate flow graph node. This is a tuple consisting of a node, + * a `FlowState`, a call context, a summary context, a tracked type, and an access path. + */ + private class PathNodeMid extends PathNodeImpl, TPathNodeMid { + NodeEx node; + FlowState state; + Cc cc; + SummaryCtx summaryCtx; + Typ t; + Ap ap; + TypOption stored; - bindingset[call, c] - CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c) { any() } + PathNodeMid() { this = TPathNodeMid(node, state, cc, summaryCtx, t, ap, stored) } - DataFlowCall viableImplCallContextReducedReverse(DataFlowCallable c, CcNoCall ctx) { none() } + override NodeEx getNodeEx() { result = node } - predicate viableImplNotCallContextReducedReverse(CcNoCall ctx) { any() } + override FlowState getState() { result = state } - bindingset[call, c] - CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) { any() } - } + private PathNodeMid getSuccMid(string label) { + localStep(this, result, label) + or + nonLocalStep(this, result, label) + } - private module Stage2Param implements MkStage::StageParam { - private module PrevStage = Stage1; + private predicate isSourceWithLabel(string labelprefix) { + exists(string model | + this.isSource() and + sourceModel(node, model) and + model != "" and + labelprefix = "Src:" + model + " " + ) + } - class Typ = Unit; + /** If this node corresponds to a sink, gets the normal node for that sink. */ + pragma[nomagic] + NodeEx toNormalSinkNodeEx() { + exists(Node n | + pragma[only_bind_out](node.asNodeOrImplicitRead()) = n and + (Stage1::isRelevantSink(n) or Stage1::isRelevantSink(n, _)) and + result.asNode() = n + ) + } - class Ap = Boolean; + override PathNodeImpl getASuccessorImpl(string label) { + // an intermediate step to another intermediate node + exists(string l2 | result = this.getSuccMid(l2) | + not this.isSourceWithLabel(_) and label = l2 + or + exists(string l1 | + this.isSourceWithLabel(l1) and + label = l1 + l2 + ) + ) + or + // a final step to a sink + exists(string l2, string sinkLabel | + result = this.getSuccMid(l2).projectToSink(sinkLabel) + | + not this.isSourceWithLabel(_) and + label = mergeLabels(l2, sinkLabel) + or + exists(string l1 | + this.isSourceWithLabel(l1) and + label = l1 + mergeLabels(l2, sinkLabel) + ) + ) + } - class ApNil extends Ap { - ApNil() { this = false } - } + private string ppType() { + exists(string ppt | ppt = t.toString() | + if ppt = "" then result = "" else result = " : " + ppt + ) + } - bindingset[result, ap] - PrevStage::Ap getApprox(Ap ap) { any() } + private string ppAp() { + exists(string s | s = ap.toString() | + if s = "" then result = "" else result = " " + s + ) + } - Typ getTyp(DataFlowType t) { any() } + private string ppCtx() { result = " <" + cc + ">" } - bindingset[c, tail] - Ap apCons(Content c, Ap tail) { - result = true and - exists(c) and - if tail = true then Config::accessPathLimit() > 1 else any() - } + private string ppSummaryCtx() { + summaryCtx instanceof SummaryCtxNone and result = "" + or + summaryCtx instanceof SummaryCtxSome and + result = " <" + summaryCtx + ">" + } - class ApHeadContent = Unit; + override string toString() { + result = node.toString() + this.ppType() + this.ppAp() + ppStored(stored) + } - pragma[inline] - ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Gets a textual representation of this element, including a textual + * representation of the call context. + */ + string toStringWithContext() { + result = + node.toString() + this.ppType() + this.ppAp() + ppStored(stored) + this.ppCtx() + + this.ppSummaryCtx() + } - ApHeadContent projectToHeadContent(Content c) { any() } + override predicate isSource() { + sourceNode(node, state) and + (if hasSourceCallCtx() then cc = ccSomeCall() else cc = ccNone()) and + summaryCtx = TSummaryCtxNone() and + t = getNodeTyp(node) and + ap instanceof ApNil + } - class ApOption = BooleanOption; + predicate isAtSink() { + sinkNode(node, state) and + ap instanceof ApNil and + // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall` + // is exactly what we need to check. + // For `FeatureEqualSourceSinkCallContext` the initial call + // context was set to `CallContextSomeCall` and jumps are + // disallowed, so `cc instanceof CallContextNoCall` never holds. + // On the other hand, in this case there's never any need to + // enter a call except to identify a summary, so the condition in + // conjunction with setting the summary context enforces this, + // which means that the summary context being empty holds if and + // only if we are in the call context of the source. + if Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext + then summaryCtx = TSummaryCtxNone() + else + if Config::getAFeature() instanceof FeatureHasSinkCallContext + then instanceofCcNoCall(cc) + else any() + } - ApOption apNone() { result = TBooleanNone() } + PathNodeSink projectToSink(string label) { + exists(string model | + this.isAtSink() and + sinkModel(node, model) and + result.getNodeEx() = this.toNormalSinkNodeEx() and + result.getState() = state and + if model != "" then label = "Sink:" + model else label = "" + ) + } + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * A flow graph node corresponding to a sink. This is disjoint from the + * intermediate nodes in order to uniquely correspond to a given sink by + * excluding the call context. + */ + private class PathNodeSink extends PathNodeImpl, TPathNodeSink { + NodeEx node; + FlowState state; - import CachedCallContextSensitivity - import NoLocalCallContext + PathNodeSink() { this = TPathNodeSink(node, state) } - bindingset[node1, state1] - bindingset[node2, state2] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - Typ t, LocalCc lcc, string label - ) { - ( - localStepNodeCand1(node1, node2, preservesValue, _, _, label) and - state1 = state2 - or - localStateStepNodeCand1(node1, state1, node2, state2, _, _, label) and - preservesValue = false - ) and - exists(t) and - exists(lcc) - } + override NodeEx getNodeEx() { result = node } - pragma[nomagic] - private predicate expectsContentCand(NodeEx node) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::revFlowIsReadAndStored(c) and - expectsContentEx(node, c) - ) - } + override FlowState getState() { result = state } - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - PrevStage::revFlowState(state) and - t0 = t and - exists(ap) and - not stateBarrier(node, state) and - ( - notExpectsContent(node) - or - ap = true and - expectsContentCand(node) - ) - } + override string toString() { result = node.toString() } - bindingset[node, ap, isStoreStep] - predicate stepFilter(NodeEx node, Ap ap, boolean isStoreStep) { any() } + override PathNodeImpl getASuccessorImpl(string label) { + result.isArbitrarySink() and label = "" + } - bindingset[t1, t2] - predicate typecheck(Typ t1, Typ t2) { any() } + override predicate isSource() { sourceNode(node, state) } + } - predicate enableTypeFlow() { none() } - } + bindingset[p, state, t, ap, stored] + pragma[inline_late] + private SummaryCtxSome mkSummaryCtxSome( + ParamNodeEx p, FlowState state, Typ t, Ap ap, TypOption stored + ) { + result = TSummaryCtxSome(p, state, t, ap, stored) + } - private module Stage2 = MkStage::Stage; + pragma[nomagic] + private predicate fwdFlowInStep( + ArgNodeEx arg, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc, + SummaryCtx outerSummaryCtx, SummaryCtx innerSummaryCtx, Typ t, Ap ap, TypOption stored + ) { + FwdFlowInNoThrough::fwdFlowIn(_, arg, _, p, state, outercc, innercc, outerSummaryCtx, t, + ap, stored, _) and + innerSummaryCtx = TSummaryCtxNone() + or + FwdFlowInThrough::fwdFlowIn(_, arg, _, p, state, outercc, innercc, outerSummaryCtx, t, + ap, stored, _) and + innerSummaryCtx = mkSummaryCtxSome(p, state, t, ap, stored) + } - private module Stage3Param implements MkStage::StageParam { - private module PrevStage = Stage2; + pragma[nomagic] + private predicate fwdFlowThroughStep0( + DataFlowCall call, ArgNodeEx arg, Cc cc, FlowState state, CcCall ccc, + SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, RetNodeEx ret, + SummaryCtxSome innerSummaryCtx + ) { + fwdFlowThrough0(call, arg, cc, state, ccc, summaryCtx, t, ap, stored, ret, + innerSummaryCtx) + } - class Typ = Unit; + bindingset[node, state, cc, summaryCtx, t, ap, stored] + pragma[inline_late] + private PathNodeImpl mkPathNode( + NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, + TypOption stored + ) { + result = TPathNodeMid(node, state, cc, summaryCtx, t, ap, stored) + } - class Ap = ApproxAccessPathFront; + private PathNodeImpl typeStrengthenToPathNode( + NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, + TypOption stored + ) { + exists(Typ t | + fwdFlow1(node, state, cc, summaryCtx, t0, t, ap, stored) and + result = TPathNodeMid(node, state, cc, summaryCtx, t, ap, stored) + ) + } - class ApNil = ApproxAccessPathFrontNil; + pragma[nomagic] + private predicate fwdFlowThroughStep1( + PathNodeImpl pn1, PathNodeImpl pn2, PathNodeImpl pn3, DataFlowCall call, Cc cc, + FlowState state, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, RetNodeEx ret + ) { + exists( + FlowState state0, ArgNodeEx arg, SummaryCtxSome innerSummaryCtx, ParamNodeEx p, + Typ innerArgT, Ap innerArgAp, TypOption innerArgStored, CcCall ccc + | + fwdFlowThroughStep0(call, arg, cc, state, ccc, summaryCtx, t, ap, stored, ret, + innerSummaryCtx) and + innerSummaryCtx = TSummaryCtxSome(p, state0, innerArgT, innerArgAp, innerArgStored) and + pn1 = mkPathNode(arg, state0, cc, summaryCtx, innerArgT, innerArgAp, innerArgStored) and + pn2 = + typeStrengthenToPathNode(p, state0, ccc, innerSummaryCtx, innerArgT, innerArgAp, + innerArgStored) and + pn3 = mkPathNode(ret, state, ccc, innerSummaryCtx, t, ap, stored) + ) + } - PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + pragma[nomagic] + private predicate fwdFlowThroughStep2( + PathNodeImpl pn1, PathNodeImpl pn2, PathNodeImpl pn3, NodeEx node, Cc cc, + FlowState state, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored + ) { + exists(DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow | + fwdFlowThroughStep1(pn1, pn2, pn3, call, cc, state, summaryCtx, t, ap, stored, ret) and + flowThroughOutOfCall(call, ret, node, allowsFieldFlow) and + not inBarrier(node, state) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } - Typ getTyp(DataFlowType t) { any() } + private predicate localStep( + PathNodeImpl pn1, NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, + Ap ap, TypOption stored, string label, boolean isStoreStep + ) { + exists(NodeEx mid, FlowState state0, Typ t0, LocalCc localCc | + pn1 = TPathNodeMid(mid, state0, cc, summaryCtx, t0, ap, stored) and + localCc = getLocalCc(cc) and + isStoreStep = false + | + localStep(mid, state0, node, state, true, _, localCc, label) and + t = t0 + or + localStep(mid, state0, node, state, false, t, localCc, label) and + ap instanceof ApNil + ) + or + // store + exists(NodeEx mid, Content c, Typ t0, Ap ap0, TypOption stored0 | + pn1 = TPathNodeMid(mid, state, cc, summaryCtx, t0, ap0, stored0) and + fwdFlowStore(mid, t0, ap0, stored0, c, t, stored, node, state, cc, summaryCtx) and + ap = apCons(c, ap0) and + label = "" and + isStoreStep = true + ) + or + // read + exists(NodeEx mid, Typ t0, Ap ap0, TypOption stored0 | + pn1 = TPathNodeMid(mid, state, cc, summaryCtx, t0, ap0, stored0) and + fwdFlowRead(mid, t0, ap0, stored0, _, node, t, ap, stored, state, cc, summaryCtx) and + label = "" and + isStoreStep = false + ) + } - bindingset[c, tail] - Ap apCons(Content c, Ap tail) { result.getAHead() = c and exists(tail) } + private predicate localStep(PathNodeImpl pn1, PathNodeImpl pn2, string label) { + exists( + NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, + TypOption stored, boolean isStoreStep + | + localStep(pn1, node, state, cc, summaryCtx, t0, ap, stored, label, isStoreStep) and + pn2 = typeStrengthenToPathNode(node, state, cc, summaryCtx, t0, ap, stored) and + stepFilter(node, ap, isStoreStep) + ) + or + summaryStep(pn1, pn2, label) + } - class ApHeadContent = ContentApprox; + private predicate summaryLabel(PathNodeImpl pn1, PathNodeImpl pn2, string summaryLabel) { + pn1 = pn2 and + summaryLabel = "" and + subpathsImpl(_, pn1, _, _) + or + exists(PathNodeImpl mid, string l1, string l2 | + summaryLabel(pn1, mid, l1) and + localStep(mid, pn2, l2) and + summaryLabel = mergeLabels(l1, l2) + ) + } - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } + private predicate summaryStep(PathNodeImpl arg, PathNodeImpl out, string label) { + exists(PathNodeImpl par, PathNodeImpl ret | + subpathsImpl(arg, par, ret, out) and + summaryLabel(par, ret, label) + ) + } - predicate projectToHeadContent = getContentApproxCached/1; + private predicate nonLocalStep( + PathNodeImpl pn1, NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, + Ap ap, TypOption stored, string label + ) { + // jump + exists(NodeEx mid, FlowState state0, Typ t0 | + pn1 = TPathNodeMid(mid, state0, _, _, t0, ap, stored) and + cc = ccNone() and + summaryCtx = TSummaryCtxNone() + | + jumpStepEx(mid, node) and + state = state0 and + not outBarrier(mid, state) and + not inBarrier(node, state) and + t = t0 and + label = "" + or + additionalJumpStep(mid, node, label) and + state = state0 and + not outBarrier(mid, state) and + not inBarrier(node, state) and + t = getNodeTyp(node) and + ap instanceof ApNil + or + additionalJumpStateStep(mid, state0, node, state, label) and + t = getNodeTyp(node) and + ap instanceof ApNil + ) + or + // flow into a callable + exists(ArgNodeEx arg, Cc outercc, SummaryCtx outerSummaryCtx | + pn1 = TPathNodeMid(arg, state, outercc, outerSummaryCtx, t, ap, stored) and + fwdFlowInStep(arg, node, state, outercc, cc, outerSummaryCtx, summaryCtx, t, ap, + stored) and + label = "" + ) + or + // flow out of a callable + exists(RetNodeEx ret, CcNoCall innercc, boolean allowsFieldFlow | + pn1 = TPathNodeMid(ret, state, innercc, summaryCtx, t, ap, stored) and + fwdFlowIntoRet(ret, state, innercc, summaryCtx, t, ap, stored) and + fwdFlowOutValidEdge(_, ret, innercc, _, node, cc, allowsFieldFlow) and + not inBarrier(node, state) and + label = "" and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } - class ApOption = ApproxAccessPathFrontOption; + private predicate nonLocalStep(PathNodeImpl pn1, PathNodeImpl pn2, string label) { + exists( + NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, + TypOption stored + | + nonLocalStep(pn1, node, state, cc, summaryCtx, t0, ap, stored, label) and + pn2 = typeStrengthenToPathNode(node, state, cc, summaryCtx, t0, ap, stored) and + stepFilter(node, ap, false) + ) + } - ApOption apNone() { result = TApproxAccessPathFrontNone() } + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple. + * + * All of the nodes may be hidden. + */ + private predicate subpathsImpl( + PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out + ) { + exists( + NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, + TypOption stored, PathNodeImpl out0 + | + fwdFlowThroughStep2(arg, par, ret, node, cc, state, summaryCtx, t0, ap, stored) and + out0 = typeStrengthenToPathNode(node, state, cc, summaryCtx, t0, ap, stored) and + stepFilter(node, ap, false) + | + out = out0 or out = out0.(PathNodeMid).projectToSink(_) + ) + } - ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) } + module StagePathGraph { + predicate edges(PathNodeImpl a, PathNodeImpl b, string key, string val) { + a.getASuccessorImpl(val) = b and + key = "provenance" + } - private module CallContextSensitivityInput implements CallContextSensitivityInputSig { - predicate relevantCallEdgeIn = PrevStage::relevantCallEdgeIn/2; + query predicate nodes(PathNodeImpl n, string key, string val) { + key = "semmle.label" and val = n.toString() + } - predicate relevantCallEdgeOut = PrevStage::relevantCallEdgeOut/2; + query predicate subpaths = subpathsImpl/4; + } - predicate reducedViableImplInCallContextCand = - CachedCallContextSensitivity::reducedViableImplInCallContext/3; + module Public { + private PathNodeImpl localStep(PathNodeImpl n) { localStep(n, result, _) } - predicate reducedViableImplInReturnCand = - CachedCallContextSensitivity::reducedViableImplInReturn/2; - } + private predicate localStepToHidden(PathNodeImpl n1, PathNodeImpl n2) { + n2 = localStep(n1) and + n2.isHidden() + } - import CallContextSensitivity - import NoLocalCallContext + private predicate localStepFromHidden(PathNodeImpl n1, PathNodeImpl n2) { + n2 = localStep(n1) and + n1.isHidden() + or + n2 = n1.getAnImplicitReadSuccessorAtSink(_) + } - bindingset[node1, state1] - bindingset[node2, state2] - private predicate localStepInput( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - DataFlowType t, LocalCallContext lcc, string label - ) { - localStepNodeCand1(node1, node2, preservesValue, t, lcc, label) and - state1 = state2 - or - localStateStepNodeCand1(node1, state1, node2, state2, t, lcc, label) and - preservesValue = false - } + bindingset[par, ret] + pragma[inline_late] + private predicate localStepStar(PathNodeImpl par, PathNodeImpl ret) { + localStep*(par) = ret + } - additional predicate localFlowBigStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - DataFlowType t, LocalCallContext lcc, string label - ) { - PrevStage::LocalFlowBigStep::localFlowBigStep(node1, state1, node2, - state2, preservesValue, t, lcc, label) - } + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple. + * + * `par` and `ret` are not hidden. + */ + pragma[nomagic] + private predicate subpaths1( + PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out + ) { + // direct subpath + subpathsImpl(arg, any(PathNodeImpl n | localStepFromHidden*(n, par)), + any(PathNodeImpl n | localStepToHidden*(ret, n)), out) and + not par.isHidden() and + not ret.isHidden() and + localStepStar(par, ret) + or + // wrapped subpath using hidden nodes, e.g. flow through a callback inside + // a summarized callable + exists(PathNodeImpl par0, PathNodeImpl ret0 | + subpaths1(any(PathNodeImpl n | localStepToHidden*(par0, n)), par, ret, + any(PathNodeImpl n | localStepFromHidden*(n, ret0))) and + subpathsImpl(arg, par0, ret0, out) + ) + } - bindingset[node1, state1] - bindingset[node2, state2] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - Typ t, LocalCc lcc, string label - ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, _, _, label) and - exists(t) and - exists(lcc) - } + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through + * a subpath between `par` and `ret` with the connecting edges `arg -> par` and + * `ret -> out` is summarized as the edge `arg -> out`. + * + * None of the nodes are hidden. + */ + pragma[nomagic] + private predicate subpaths2( + PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out + ) { + exists(PathNodeImpl out0 | + subpaths1(any(PathNodeImpl n | localStepToHidden*(arg, n)), par, ret, + any(PathNodeImpl n | localStepFromHidden*(n, out0))) and + not arg.isHidden() and + not out0.isHidden() + | + out = out0 or out = out0.(PathNodeMid).projectToSink(_) + ) + } - pragma[nomagic] - private predicate expectsContentCand(NodeEx node, Ap ap) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::readStepCand(_, c, _) and - expectsContentEx(node, c) and - c = ap.getAHead() - ) - } + /** Holds if `n` is reachable from a source. */ + private predicate fwdReach(PathNodeImpl n) { + n.isArbitrarySource() + or + exists(PathNodeImpl mid | fwdReach(mid) and mid.getANonHiddenSuccessor(_) = n) + } - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - exists(state) and - t0 = t and - ( - notExpectsContent(node) - or - expectsContentCand(node, ap) - ) - } + /** Holds if `n` is reachable from a source and can reach a sink. */ + private predicate directReach(PathNodeImpl n) { + fwdReach(n) and + ( + n.isArbitrarySink() or + directReach(n.getANonHiddenSuccessor(_)) + ) + } - bindingset[node, ap, isStoreStep] - predicate stepFilter(NodeEx node, Ap ap, boolean isStoreStep) { any() } + /** + * Holds if `n` can reach a return node in a summarized subpath that can reach a sink. + */ + private predicate retReach(PathNodeImpl n) { + fwdReach(n) and + ( + exists(PathNodeImpl out | subpaths2(_, _, n, out) | + directReach(out) or retReach(out) + ) + or + exists(PathNodeImpl mid | + retReach(mid) and + n.getANonHiddenSuccessor(_) = mid and + not subpaths2(_, mid, _, _) + ) + ) + } - bindingset[t1, t2] - predicate typecheck(Typ t1, Typ t2) { any() } - } + /** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ + private predicate reach(PathNodeImpl n) { directReach(n) or retReach(n) } - private module Stage3 = MkStage::Stage; + /** + * A `Node` augmented with a call context (except for sinks) and an access path. + * Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated. + */ + class PathNode instanceof PathNodeImpl { + PathNode() { + reach(this) and + not this instanceof PathNodeSrcGrp and + not this instanceof PathNodeSinkGrp + } - bindingset[node, t0] - private predicate strengthenType(NodeEx node, DataFlowType t0, DataFlowType t) { - if node instanceof CastingNodeEx - then - exists(DataFlowType nt | nt = node.getDataFlowType() | - if typeStrongerThanFilter(nt, t0) - then t = nt - else ( - compatibleTypesFilter(nt, t0) and t = t0 - ) - ) - else t = t0 - } + /** Gets a textual representation of this element. */ + final string toString() { result = super.toString() } - private module Stage4Param implements MkStage::StageParam { - private module PrevStage = Stage3; + /** + * Gets a textual representation of this element, including a textual + * representation of the call context. + */ + final string toStringWithContext() { + result = this.(PathNodeMid).toStringWithContext() + or + not this instanceof PathNodeMid and result = this.toString() + } - class Typ = Unit; + /** Gets the location of this node. */ + Location getLocation() { result = super.getLocation() } - class Ap = AccessPathFront; + /** Gets the underlying `Node`. */ + final Node getNode() { super.getNodeEx().projectToNode() = result } - class ApNil = AccessPathFrontNil; + /** Gets the parameter node through which data is returned, if any. */ + final ParameterNode asParameterReturnNode() { + result = super.getNodeEx().asParamReturnNode() + } - PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() } + /** Gets the `FlowState` of this node. */ + final FlowState getState() { result = super.getState() } - Typ getTyp(DataFlowType t) { any() } + /** Gets a successor of this node, if any. */ + final PathNode getASuccessor() { result = super.getANonHiddenSuccessor(_) } - bindingset[c, tail] - Ap apCons(Content c, Ap tail) { result.getHead() = c and exists(tail) } + /** Holds if this node is a source. */ + final predicate isSource() { super.isSource() } - class ApHeadContent = Content; + /** Holds if this node is a sink. */ + final predicate isSink() { this instanceof PathNodeSink } - pragma[noinline] - ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + pragma[inline] + deprecated final predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getLocation() + .hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } - ApHeadContent projectToHeadContent(Content c) { result = c } + /** + * DEPRECATED: This functionality is no longer available. + * + * Holds if this node is a grouping of source nodes. + */ + deprecated final predicate isSourceGroup(string group) { none() } - class ApOption = AccessPathFrontOption; + /** + * DEPRECATED: This functionality is no longer available. + * + * Holds if this node is a grouping of sink nodes. + */ + deprecated final predicate isSinkGroup(string group) { none() } + } - ApOption apNone() { result = TAccessPathFrontNone() } + /** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */ + private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) { + n1.getANonHiddenSuccessor(_) = n2 and directReach(n2) + } - ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } + private predicate tcSrc(PathNodeImpl n) { n.isSource() } - import BooleanCallContext + private predicate tcSink(PathNodeImpl n) { n.isSink() } - pragma[nomagic] - predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - Typ t, LocalCc lcc, string label - ) { - Stage3Param::localFlowBigStep(node1, state1, node2, state2, preservesValue, _, _, label) and - PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and - PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) and - exists(t) and - exists(lcc) - } + private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = + doublyBoundedFastTC(pathSucc/2, tcSrc/1, tcSink/1)(n1, n2) - pragma[nomagic] - private predicate clearSet(NodeEx node, ContentSet c) { - PrevStage::revFlow(node) and - clearsContentSet(node, c) - } + /** + * Holds if data can flow from `source` to `sink`. + * + * The corresponding paths are generated from the end-points and the graph + * included in the module `PathGraph`. + */ + predicate flowPath(PathNode source, PathNode sink) { + ( + // When there are both sources and sinks in the diff range, + // diff-informed dataflow falls back to computing all paths without + // any filtering. To prevent significant alert flip-flopping due to + // minor code changes triggering the fallback, we consistently apply + // source-or-sink filtering here to ensure that we return the same + // paths regardless of whether the fallback is triggered. + if Config::observeDiffInformedIncrementalMode() + then Stage1::isRelevantSourceSinkPair(source.getNode(), sink.getNode()) + else any() + ) and + exists(PathNodeImpl flowsource, PathNodeImpl flowsink | + source = flowsource and sink = flowsink + | + flowsource.isSource() and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.isSink() + ) + } - pragma[nomagic] - additional predicate clearContent(NodeEx node, Content c, boolean isStoreTarget) { - exists(ContentSet cs | - PrevStage::readStepCand(_, pragma[only_bind_into](c), _) and - c = cs.getAReadContent() and - clearSet(node, cs) and - if PrevStage::storeStepCand(_, _, node, _, _) - then isStoreTarget = true - else isStoreTarget = false - ) - } + /** + * Provides the query predicates needed to include a graph in a path-problem query. + */ + module PathGraph implements PathGraphSig { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PathNode a, PathNode b, string key, string val) { + a.(PathNodeImpl).getANonHiddenSuccessor(val) = b and + key = "provenance" + } - pragma[nomagic] - private predicate clear(NodeEx node, Ap ap) { - // When `node` is the target of a store, we interpret `clearsContent` as - // only pertaining to _earlier_ store steps. In this case, we need to postpone - // checking `clearsContent` to the step creation. - clearContent(node, ap.getHead(), false) - } + /** Holds if `n` is a node in the graph of data flow path explanations. */ + query predicate nodes(PathNode n, string key, string val) { + key = "semmle.label" and val = n.toString() + } - pragma[nomagic] - private predicate clearExceptStore(NodeEx node, Ap ap) { - clearContent(node, ap.getHead(), true) - } + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through + * a subpath between `par` and `ret` with the connecting edges `arg -> par` and + * `ret -> out` is summarized as the edge `arg -> out`. + */ + query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { + subpaths2(arg, par, ret, out) + } + } + } + } - pragma[nomagic] - private predicate expectsContentCand(NodeEx node, Ap ap) { - exists(Content c | - PrevStage::revFlow(node) and - PrevStage::readStepCand(_, c, _) and - expectsContentEx(node, c) and - c = ap.getHead() - ) + additional predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, int calledges, + int tfnodes, int tftuples + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, _)) and + fields = count(Content f0 | fwdConsCand(f0, _)) and + conscand = count(Content f0, Ap ap | fwdConsCand(f0, ap)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, _, _)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, + TypOption stored | fwdFlow(n, state, cc, summaryCtx, t, ap, stored)) and + calledges = + count(DataFlowCall call, DataFlowCallable c | + FwdTypeFlowInput::dataFlowTakenCallEdgeIn(call, c, _) or + FwdTypeFlowInput::dataFlowTakenCallEdgeOut(call, c) + ) and + FwdTypeFlow::typeFlowStats(tfnodes, tftuples) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _)) and + fields = count(Content f0 | consCand(f0, _)) and + conscand = count(Content f0, Ap ap | consCand(f0, ap)) and + states = count(FlowState state | revFlow(_, state, _, _, _)) and + tuples = + count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, state, returnCtx, retAp, ap) + ) and + calledges = + count(DataFlowCall call, DataFlowCallable c | + RevTypeFlowInput::dataFlowTakenCallEdgeIn(call, c, _) or + RevTypeFlowInput::dataFlowTakenCallEdgeOut(call, c) + ) and + RevTypeFlow::typeFlowStats(tfnodes, tftuples) + } + /* End: Stage logic. */ } + } - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - exists(state) and - not clear(node, ap) and - t0 = t and - ( - notExpectsContent(node) - or - expectsContentCand(node, ap) - ) + private module BooleanCallContext { + class Cc = Boolean; + + class CcCall extends Cc { + CcCall() { this = true } } - bindingset[node, ap, isStoreStep] - predicate stepFilter(NodeEx node, Ap ap, boolean isStoreStep) { - if clearExceptStore(node, ap) then isStoreStep = true else any() + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } } - bindingset[t1, t2] - predicate typecheck(Typ t1, Typ t2) { any() } - } + Cc ccNone() { result = false } - private module Stage4 = MkStage::Stage; + CcCall ccSomeCall() { result = true } - /** - * Holds if a length 2 access path approximation with the head `c` is expected - * to be expensive. - */ - private predicate expensiveLen2unfolding(Content c) { - exists(int tails, int nodes, int apLimit, int tupleLimit | - tails = strictcount(AccessPathFront apf | Stage4::consCand(c, apf)) and - nodes = - strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) - or - Stage4::nodeMayUseSummary(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) - ) and - accessPathApproxCostLimits(apLimit, tupleLimit) and - apLimit < tails and - tupleLimit < (tails - 1) * nodes and - not forceHighPrecision(c) - ) - } + predicate instanceofCc(Cc cc) { any() } - private newtype TAccessPathApprox = - TNil() or - TConsNil(Content c) { - Stage4::consCand(c, TFrontNil()) and - not expensiveLen2unfolding(c) - } or - TConsCons(Content c1, Content c2, int len) { - Stage4::consCand(c1, TFrontHead(c2)) and - len in [2 .. Config::accessPathLimit()] and - not expensiveLen2unfolding(c1) - } or - TCons1(Content c, int len) { - len in [1 .. Config::accessPathLimit()] and - expensiveLen2unfolding(c) - } + predicate instanceofCcCall(CcCall cc) { any() } - /** - * Conceptually a list of `Content`s, but only the first two elements of - * the list and its length are tracked. If data flows from a source to a - * given node with a given `AccessPathApprox`, this indicates the sequence - * of dereference operations needed to get from the value in the node to - * the tracked object. - */ - abstract private class AccessPathApprox extends TAccessPathApprox { - abstract string toString(); + predicate instanceofCcNoCall(CcNoCall cc) { any() } - abstract Content getHead(); + class LocalCc = Unit; - abstract int len(); + bindingset[cc] + LocalCc getLocalCc(Cc cc) { any() } - abstract AccessPathFront getFront(); + DataFlowCallable viableImplCallContextReduced(DataFlowCall call, CcCall ctx) { none() } - /** Holds if this is a representation of `head` followed by `tail`. */ - abstract predicate isCons(Content head, AccessPathApprox tail); - } + bindingset[call, ctx] + predicate viableImplNotCallContextReduced(DataFlowCall call, Cc ctx) { any() } - private class AccessPathApproxNil extends AccessPathApprox, TNil { - override string toString() { result = "" } + bindingset[call, c] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c) { any() } - override Content getHead() { none() } + DataFlowCall viableImplCallContextReducedReverse(DataFlowCallable c, CcNoCall ctx) { none() } - override int len() { result = 0 } + predicate viableImplNotCallContextReducedReverse(CcNoCall ctx) { any() } - override AccessPathFront getFront() { result = TFrontNil() } + bindingset[call, c] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) { any() } + } - override predicate isCons(Content head, AccessPathApprox tail) { none() } + private module S1 implements StageSig { + import Stage1 } - abstract private class AccessPathApproxCons extends AccessPathApprox { } + private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; - private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { - private Content c; + class Typ = Unit; - AccessPathApproxConsNil() { this = TConsNil(c) } + class Ap = Boolean; - override string toString() { result = "[" + c.toString() + "]" } + class ApNil extends Ap { + ApNil() { this = false } + } - override Content getHead() { result = c } + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } - override int len() { result = 1 } + Typ getTyp(DataFlowType t) { any() } - override AccessPathFront getFront() { result = TFrontHead(c) } + bindingset[c, tail] + Ap apCons(Content c, Ap tail) { + result = true and + exists(c) and + if tail = true then Config::accessPathLimit() > 1 else any() + } - override predicate isCons(Content head, AccessPathApprox tail) { head = c and tail = TNil() } - } + class ApHeadContent = Unit; - private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { - private Content c1; - private Content c2; - private int len; + pragma[inline] + ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true } - AccessPathApproxConsCons() { this = TConsCons(c1, c2, len) } + ApHeadContent projectToHeadContent(Content c) { any() } - override string toString() { - if len = 2 - then result = "[" + c1.toString() + ", " + c2.toString() + "]" - else result = "[" + c1.toString() + ", " + c2.toString() + ", ... (" + len.toString() + ")]" - } + class ApOption = BooleanOption; - override Content getHead() { result = c1 } + ApOption apNone() { result = TBooleanNone() } - override int len() { result = len } + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } - override AccessPathFront getFront() { result = TFrontHead(c1) } + import CachedCallContextSensitivity + import NoLocalCallContext - override predicate isCons(Content head, AccessPathApprox tail) { - head = c1 and + bindingset[node1, state1] + bindingset[node2, state2] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + Typ t, LocalCc lcc, string label + ) { ( - tail = TConsCons(c2, _, len - 1) - or - len = 2 and - tail = TConsNil(c2) + localStepNodeCand1(node1, node2, preservesValue, _, _, label) and + state1 = state2 or - tail = TCons1(c2, len - 1) - ) + localStateStepNodeCand1(node1, state1, node2, state2, _, _, label) and + preservesValue = false + ) and + exists(t) and + exists(lcc) } - } - - private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { - private Content c; - private int len; - - AccessPathApproxCons1() { this = TCons1(c, len) } - override string toString() { - if len = 1 - then result = "[" + c.toString() + "]" - else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" + pragma[nomagic] + private predicate expectsContentCand(NodeEx node) { + exists(Content c | + PrevStage::revFlow(node) and + PrevStage::revFlowIsReadAndStored(c) and + Stage1::expectsContentEx(node, c) + ) } - override Content getHead() { result = c } - - override int len() { result = len } - - override AccessPathFront getFront() { result = TFrontHead(c) } - - override predicate isCons(Content head, AccessPathApprox tail) { - head = c and + bindingset[node, state, t0, ap] + predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { + PrevStage::revFlowState(state) and + t0 = t and + exists(ap) and + not stateBarrier(node, state) and ( - exists(Content c2 | Stage4::consCand(c, TFrontHead(c2)) | - tail = TConsCons(c2, _, len - 1) - or - len = 2 and - tail = TConsNil(c2) - or - tail = TCons1(c2, len - 1) - ) + Stage1::notExpectsContent(node) or - len = 1 and - Stage4::consCand(c, TFrontNil()) and - tail = TNil() + ap = true and + expectsContentCand(node) ) } - } - private newtype TAccessPathApproxOption = - TAccessPathApproxNone() or - TAccessPathApproxSome(AccessPathApprox apa) + bindingset[node, ap, isStoreStep] + predicate stepFilter(NodeEx node, Ap ap, boolean isStoreStep) { any() } - private class AccessPathApproxOption extends TAccessPathApproxOption { - string toString() { - this = TAccessPathApproxNone() and result = "" - or - this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) - } + bindingset[t1, t2] + predicate typecheck(Typ t1, Typ t2) { any() } + + predicate enableTypeFlow() { none() } } - private module Stage5Param implements MkStage::StageParam { - private module PrevStage = Stage4; + private module Stage2 = MkStage::Stage; - class Typ = DataFlowType; + private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; - class Ap = AccessPathApprox; + class Typ = Unit; - class ApNil = AccessPathApproxNil; + class Ap = ApproxAccessPathFront; - pragma[nomagic] - PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } + class ApNil = ApproxAccessPathFrontNil; - Typ getTyp(DataFlowType t) { result = t } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + + Typ getTyp(DataFlowType t) { any() } bindingset[c, tail] - Ap apCons(Content c, Ap tail) { result.isCons(c, tail) } + Ap apCons(Content c, Ap tail) { result.getAHead() = c and exists(tail) } - class ApHeadContent = Content; + class ApHeadContent = ContentApprox; pragma[noinline] ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - ApHeadContent projectToHeadContent(Content c) { result = c } + predicate projectToHeadContent = getContentApproxCached/1; - class ApOption = AccessPathApproxOption; + class ApOption = ApproxAccessPathFrontOption; - ApOption apNone() { result = TAccessPathApproxNone() } + ApOption apNone() { result = TApproxAccessPathFrontNone() } - ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } + ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) } private module CallContextSensitivityInput implements CallContextSensitivityInputSig { predicate relevantCallEdgeIn = PrevStage::relevantCallEdgeIn/2; @@ -4225,181 +2768,106 @@ module MakeImpl Lang> { predicate relevantCallEdgeOut = PrevStage::relevantCallEdgeOut/2; predicate reducedViableImplInCallContextCand = - Stage3Param::reducedViableImplInCallContext/3; + CachedCallContextSensitivity::reducedViableImplInCallContext/3; - predicate reducedViableImplInReturnCand = Stage3Param::reducedViableImplInReturn/2; + predicate reducedViableImplInReturnCand = + CachedCallContextSensitivity::reducedViableImplInReturn/2; } import CallContextSensitivity - import LocalCallContext - - predicate localStep = - PrevStage::LocalFlowBigStep::localFlowBigStepTc/8; + import NoLocalCallContext - bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - strengthenType(node, t0, t) and - exists(state) and - exists(ap) + bindingset[node1, state1] + bindingset[node2, state2] + private predicate localStepInput( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + DataFlowType t, LocalCallContext lcc, string label + ) { + localStepNodeCand1(node1, node2, preservesValue, t, lcc, label) and + state1 = state2 + or + localStateStepNodeCand1(node1, state1, node2, state2, t, lcc, label) and + preservesValue = false } - pragma[nomagic] - private predicate clearExceptStore(NodeEx node, Ap ap) { - Stage4Param::clearContent(node, ap.getHead(), true) + additional predicate localFlowBigStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + DataFlowType t, LocalCallContext lcc, string label + ) { + PrevStage::LocalFlowBigStep::localFlowBigStep(node1, state1, node2, + state2, preservesValue, t, lcc, label) } - bindingset[node, ap, isStoreStep] - predicate stepFilter(NodeEx node, Ap ap, boolean isStoreStep) { - if clearExceptStore(node, ap) then isStoreStep = true else any() + bindingset[node1, state1] + bindingset[node2, state2] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + Typ t, LocalCc lcc, string label + ) { + localFlowBigStep(node1, state1, node2, state2, preservesValue, _, _, label) and + exists(t) and + exists(lcc) } - bindingset[t1, t2] - predicate typecheck(Typ t1, Typ t2) { compatibleTypesFilter(t1, t2) } - } - - private module Stage5 = MkStage::Stage; - - pragma[nomagic] - private predicate stage5ConsCand(Content c, AccessPathFront apf, int len) { - Stage5::consCand(c, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1)) - } - - /** - * Gets the number of length 2 access path approximations that correspond to `apa`. - */ - private int count1to2unfold(AccessPathApproxCons1 apa) { - exists(Content c, int len | - c = apa.getHead() and - len = apa.len() and - result = strictcount(AccessPathFront apf | stage5ConsCand(c, apf, len)) - ) - } + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap) { + exists(Content c | + PrevStage::revFlow(node) and + PrevStage::readStepCand(_, c, _) and + Stage1::expectsContentEx(node, c) and + c = ap.getAHead() + ) + } - private int countNodesUsingAccessPath(AccessPathApprox apa) { - result = - strictcount(NodeEx n, FlowState state | - Stage5::revFlow(n, state, apa) or Stage5::nodeMayUseSummary(n, state, apa) + bindingset[node, state, t0, ap] + predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { + exists(state) and + t0 = t and + ( + Stage1::notExpectsContent(node) + or + expectsContentCand(node, ap) ) - } + } - /** - * Holds if a length 2 access path approximation matching `apa` is expected - * to be expensive. - */ - private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = count1to2unfold(apa) and - nodes = countNodesUsingAccessPath(apa) and - accessPathCostLimits(apLimit, tupleLimit) and - apLimit < aps and - tupleLimit < (aps - 1) * nodes - ) - } + bindingset[node, ap, isStoreStep] + predicate stepFilter(NodeEx node, Ap ap, boolean isStoreStep) { any() } - private predicate hasTail(AccessPathApprox apa, AccessPathApprox tail) { - exists(Content head | - apa.isCons(head, tail) and - Stage5::consCand(head, tail) - ) + bindingset[t1, t2] + predicate typecheck(Typ t1, Typ t2) { any() } } - private predicate forceUnfold(AccessPathApprox apa) { - forceHighPrecision(apa.getHead()) - or - exists(Content c2 | - apa = TConsCons(_, c2, _) and - forceHighPrecision(c2) - ) - } + private module Stage3 = MkStage::Stage; - /** - * Holds with `unfold = false` if a precise head-tail representation of `apa` is - * expected to be expensive. Holds with `unfold = true` otherwise. - */ - private predicate evalUnfold(AccessPathApprox apa, boolean unfold) { - if forceUnfold(apa) - then unfold = true - else - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa) and - nodes = countNodesUsingAccessPath(apa) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + bindingset[node, t0] + private predicate strengthenType(NodeEx node, DataFlowType t0, DataFlowType t) { + if node instanceof CastingNodeEx + then + exists(DataFlowType nt | nt = node.getDataFlowType() | + if typeStrongerThanFilter(nt, t0) + then t = nt + else ( + compatibleTypesFilter(nt, t0) and t = t0 + ) ) + else t = t0 } - /** - * Gets the number of `AccessPath`s that correspond to `apa`. - */ - private int countAps(AccessPathApprox apa) { - evalUnfold(apa, false) and - result = 1 and - (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa)) - or - evalUnfold(apa, false) and - result = count1to2unfold(apa) and - not expensiveLen1to2unfolding(apa) - or - evalUnfold(apa, true) and - result = countPotentialAps(apa) - } - - /** - * Gets the number of `AccessPath`s that would correspond to `apa` assuming - * that it is expanded to a precise head-tail representation. - */ - language[monotonicAggregates] - private int countPotentialAps(AccessPathApprox apa) { - apa instanceof AccessPathApproxNil and result = 1 - or - result = strictsum(AccessPathApprox tail | hasTail(apa, tail) | countAps(tail)) - } - - private newtype TAccessPath = - TAccessPathNil() or - TAccessPathCons(Content head, AccessPath tail) { - exists(AccessPathApproxCons apa | - not evalUnfold(apa, false) and - head = apa.getHead() and - hasTail(apa, tail.getApprox()) - ) - } or - TAccessPathCons2(Content head1, Content head2, int len) { - exists(AccessPathApproxCons apa, AccessPathApprox tail | - evalUnfold(apa, false) and - not expensiveLen1to2unfolding(apa) and - apa.len() = len and - hasTail(apa, tail) and - head1 = apa.getHead() and - head2 = tail.getHead() - ) - } or - TAccessPathCons1(Content head, int len) { - exists(AccessPathApproxCons apa | - evalUnfold(apa, false) and - expensiveLen1to2unfolding(apa) and - apa.len() = len and - head = apa.getHead() - ) - } - - private module Stage6Param implements MkStage::StageParam { - private module PrevStage = Stage5; + private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; - class Typ = DataFlowType; + class Typ = Unit; - class Ap = AccessPath; + class Ap = AccessPathFront; - class ApNil = AccessPathNil; + class ApNil = AccessPathFrontNil; - pragma[nomagic] - PrevStage::Ap getApprox(Ap ap) { result = ap.getApprox() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() } - Typ getTyp(DataFlowType t) { result = t } + Typ getTyp(DataFlowType t) { any() } bindingset[c, tail] - pragma[inline_late] - Ap apCons(Content c, Ap tail) { result.isCons(c, tail) } + Ap apCons(Content c, Ap tail) { result.getHead() = c and exists(tail) } class ApHeadContent = Content; @@ -4408,41 +2876,77 @@ module MakeImpl Lang> { ApHeadContent projectToHeadContent(Content c) { result = c } - private module ApOption = Option; + class ApOption = AccessPathFrontOption; - class ApOption = ApOption::Option; + ApOption apNone() { result = TAccessPathFrontNone() } - ApOption apNone() { result.isNone() } + ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - ApOption apSome(Ap ap) { result = ApOption::some(ap) } + import BooleanCallContext - private module CallContextSensitivityInput implements CallContextSensitivityInputSig { - predicate relevantCallEdgeIn = PrevStage::relevantCallEdgeIn/2; + pragma[nomagic] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + Typ t, LocalCc lcc, string label + ) { + Stage3Param::localFlowBigStep(node1, state1, node2, state2, preservesValue, _, _, label) and + PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and + PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) and + exists(t) and + exists(lcc) + } - predicate relevantCallEdgeOut = PrevStage::relevantCallEdgeOut/2; + pragma[nomagic] + private predicate clearSet(NodeEx node, ContentSet c) { + PrevStage::revFlow(node) and + clearsContentSet(node, c) + } - predicate reducedViableImplInCallContextCand = - Stage5Param::reducedViableImplInCallContext/3; + pragma[nomagic] + additional predicate clearContent(NodeEx node, Content c, boolean isStoreTarget) { + exists(ContentSet cs | + PrevStage::readStepCand(_, pragma[only_bind_into](c), _) and + c = cs.getAReadContent() and + clearSet(node, cs) and + if PrevStage::storeStepCand(_, _, node, _, _) + then isStoreTarget = true + else isStoreTarget = false + ) + } - predicate reducedViableImplInReturnCand = Stage5Param::reducedViableImplInReturn/2; + pragma[nomagic] + private predicate clear(NodeEx node, Ap ap) { + // When `node` is the target of a store, we interpret `clearsContent` as + // only pertaining to _earlier_ store steps. In this case, we need to postpone + // checking `clearsContent` to the step creation. + clearContent(node, ap.getHead(), false) } - import CallContextSensitivity - import LocalCallContext + pragma[nomagic] + private predicate clearExceptStore(NodeEx node, Ap ap) { + clearContent(node, ap.getHead(), true) + } - predicate localStep = - PrevStage::LocalFlowBigStep::localFlowBigStepTc/8; + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap) { + exists(Content c | + PrevStage::revFlow(node) and + PrevStage::readStepCand(_, c, _) and + Stage1::expectsContentEx(node, c) and + c = ap.getHead() + ) + } bindingset[node, state, t0, ap] predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { - strengthenType(node, t0, t) and exists(state) and - exists(ap) - } - - pragma[nomagic] - private predicate clearExceptStore(NodeEx node, Ap ap) { - Stage4Param::clearContent(node, ap.getHead(), true) + not clear(node, ap) and + t0 = t and + ( + Stage1::notExpectsContent(node) + or + expectsContentCand(node, ap) + ) } bindingset[node, ap, isStoreStep] @@ -4451,1327 +2955,754 @@ module MakeImpl Lang> { } bindingset[t1, t2] - predicate typecheck(Typ t1, Typ t2) { compatibleTypesFilter(t1, t2) } + predicate typecheck(Typ t1, Typ t2) { any() } } - module Stage6 = MkStage::Stage; + private module Stage4 = MkStage::Stage; /** - * A list of `Content`s. - * - * If data flows from a source to a given node with a given `AccessPath`, - * this indicates the sequence of dereference operations needed to get from - * the value in the node to the tracked object. + * Holds if a length 2 access path approximation with the head `c` is expected + * to be expensive. */ - private class AccessPath extends TAccessPath { - /** Gets the head of this access path, if any. */ - abstract Content getHead(); + private predicate expensiveLen2unfolding(Content c) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | Stage4::consCand(c, apf)) and + nodes = + strictcount(NodeEx n, FlowState state | + Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) + or + Stage4::nodeMayUseSummary(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes and + not forceHighPrecision(c) + ) + } - /** Holds if this is a representation of `head` followed by `tail`. */ - pragma[nomagic] - abstract predicate isCons(Content head, AccessPath tail); + private newtype TAccessPathApprox = + TNil() or + TConsNil(Content c) { + Stage4::consCand(c, TFrontNil()) and + not expensiveLen2unfolding(c) + } or + TConsCons(Content c1, Content c2, int len) { + Stage4::consCand(c1, TFrontHead(c2)) and + len in [2 .. Config::accessPathLimit()] and + not expensiveLen2unfolding(c1) + } or + TCons1(Content c, int len) { + len in [1 .. Config::accessPathLimit()] and + expensiveLen2unfolding(c) + } - /** Gets the front of this access path. */ - abstract AccessPathFront getFront(); + /** + * Conceptually a list of `Content`s, but only the first two elements of + * the list and its length are tracked. If data flows from a source to a + * given node with a given `AccessPathApprox`, this indicates the sequence + * of dereference operations needed to get from the value in the node to + * the tracked object. + */ + abstract private class AccessPathApprox extends TAccessPathApprox { + abstract string toString(); - /** Gets the approximation of this access path. */ - abstract AccessPathApprox getApprox(); + abstract Content getHead(); - /** Gets the length of this access path. */ - abstract int length(); + abstract int len(); - /** Gets a textual representation of this access path. */ - abstract string toString(); - } + abstract AccessPathFront getFront(); - private class AccessPathNil extends AccessPath, TAccessPathNil { - override Content getHead() { none() } + /** Holds if this is a representation of `head` followed by `tail`. */ + abstract predicate isCons(Content head, AccessPathApprox tail); + } - override predicate isCons(Content head, AccessPath tail) { none() } + private class AccessPathApproxNil extends AccessPathApprox, TNil { + override string toString() { result = "" } - override AccessPathFrontNil getFront() { result = TFrontNil() } + override Content getHead() { none() } - override AccessPathApproxNil getApprox() { result = TNil() } + override int len() { result = 0 } - override int length() { result = 0 } + override AccessPathFront getFront() { result = TFrontNil() } - override string toString() { result = "" } + override predicate isCons(Content head, AccessPathApprox tail) { none() } } - private class AccessPathCons extends AccessPath, TAccessPathCons { - private Content head_; - private AccessPath tail_; - - AccessPathCons() { this = TAccessPathCons(head_, tail_) } + abstract private class AccessPathApproxCons extends AccessPathApprox { } - override Content getHead() { result = head_ } + private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { + private Content c; - override predicate isCons(Content head, AccessPath tail) { head = head_ and tail = tail_ } + AccessPathApproxConsNil() { this = TConsNil(c) } - override AccessPathFrontHead getFront() { result = TFrontHead(head_) } + override string toString() { result = "[" + c.toString() + "]" } - override AccessPathApproxCons getApprox() { - result = TConsNil(head_) and tail_ = TAccessPathNil() - or - result = TConsCons(head_, tail_.getHead(), this.length()) - or - result = TCons1(head_, this.length()) - } + override Content getHead() { result = c } - override int length() { result = 1 + tail_.length() } + override int len() { result = 1 } - private string toStringImpl(boolean needsSuffix) { - tail_ = TAccessPathNil() and - needsSuffix = false and - result = head_.toString() + "]" - or - result = head_ + ", " + tail_.(AccessPathCons).toStringImpl(needsSuffix) - or - exists(Content c2, Content c3, int len | tail_ = TAccessPathCons2(c2, c3, len) | - result = head_ + ", " + c2 + ", " + c3 + ", ... (" and len > 2 and needsSuffix = true - or - result = head_ + ", " + c2 + ", " + c3 + "]" and len = 2 and needsSuffix = false - ) - or - exists(Content c2, int len | tail_ = TAccessPathCons1(c2, len) | - result = head_ + ", " + c2 + ", ... (" and len > 1 and needsSuffix = true - or - result = head_ + ", " + c2 + "]" and len = 1 and needsSuffix = false - ) - } + override AccessPathFront getFront() { result = TFrontHead(c) } - override string toString() { - result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" - or - result = "[" + this.toStringImpl(false) - } + override predicate isCons(Content head, AccessPathApprox tail) { head = c and tail = TNil() } } - private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { - private Content head1; - private Content head2; + private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { + private Content c1; + private Content c2; private int len; - AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } - - override Content getHead() { result = head1 } + AccessPathApproxConsCons() { this = TConsCons(c1, c2, len) } - override predicate isCons(Content head, AccessPath tail) { - head = head1 and - Stage5::consCand(head1, tail.getApprox()) and - tail.getHead() = head2 and - tail.length() = len - 1 + override string toString() { + if len = 2 + then result = "[" + c1.toString() + ", " + c2.toString() + "]" + else result = "[" + c1.toString() + ", " + c2.toString() + ", ... (" + len.toString() + ")]" } - override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + override Content getHead() { result = c1 } - override AccessPathApproxCons getApprox() { - result = TConsCons(head1, head2, len) or - result = TCons1(head1, len) - } + override int len() { result = len } - override int length() { result = len } + override AccessPathFront getFront() { result = TFrontHead(c1) } - override string toString() { - if len = 2 - then result = "[" + head1.toString() + ", " + head2.toString() + "]" - else - result = - "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + override predicate isCons(Content head, AccessPathApprox tail) { + head = c1 and + ( + tail = TConsCons(c2, _, len - 1) + or + len = 2 and + tail = TConsNil(c2) + or + tail = TCons1(c2, len - 1) + ) } } - private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { - private Content head_; + private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private Content c; private int len; - AccessPathCons1() { this = TAccessPathCons1(head_, len) } - - override Content getHead() { result = head_ } + AccessPathApproxCons1() { this = TCons1(c, len) } - override predicate isCons(Content head, AccessPath tail) { - head = head_ and - Stage5::consCand(head_, tail.getApprox()) and - tail.length() = len - 1 + override string toString() { + if len = 1 + then result = "[" + c.toString() + "]" + else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" } - override AccessPathFrontHead getFront() { result = TFrontHead(head_) } + override Content getHead() { result = c } - override AccessPathApproxCons getApprox() { result = TCons1(head_, len) } + override int len() { result = len } - override int length() { result = len } + override AccessPathFront getFront() { result = TFrontHead(c) } - override string toString() { - if len = 1 - then result = "[" + head_.toString() + "]" - else result = "[" + head_.toString() + ", ... (" + len.toString() + ")]" + override predicate isCons(Content head, AccessPathApprox tail) { + head = c and + ( + exists(Content c2 | Stage4::consCand(c, TFrontHead(c2)) | + tail = TConsCons(c2, _, len - 1) + or + len = 2 and + tail = TConsNil(c2) + or + tail = TCons1(c2, len - 1) + ) + or + len = 1 and + Stage4::consCand(c, TFrontNil()) and + tail = TNil() + ) } } - private module S6Graph = Stage6::Graph; + private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) - private module S6 = S6Graph::Public; + private class AccessPathApproxOption extends TAccessPathApproxOption { + string toString() { + this = TAccessPathApproxNone() and result = "" + or + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) + } + } - import S6 + private module Stage5Param implements MkStage::StageParam { + private module PrevStage = Stage4; - /** - * Holds if data can flow from `source` to `sink`. - */ - predicate flow(Node source, Node sink) { - exists(PathNode source0, PathNode sink0 | - flowPath(source0, sink0) and source0.getNode() = source and sink0.getNode() = sink - ) - } + class Typ = DataFlowType; - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowTo(Node sink) { exists(PathNode n | n.isSink() and n.getNode() = sink) } + class Ap = AccessPathApprox; - /** - * Holds if data can flow from some source to `sink`. - */ - predicate flowToExpr(DataFlowExpr sink) { flowTo(exprNode(sink)) } + class ApNil = AccessPathApproxNil; - /** - * INTERNAL: Only for debugging. - * - * Calculates per-stage metrics for data flow. - */ - predicate stageStats = Debug::stageStats/10; + pragma[nomagic] + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private module Stage1alias = Stage1; + Typ getTyp(DataFlowType t) { result = t } - private module Stage2alias = Stage2; + bindingset[c, tail] + Ap apCons(Content c, Ap tail) { result.isCons(c, tail) } - private module Stage3alias = Stage3; + class ApHeadContent = Content; - private module Stage4alias = Stage4; + pragma[noinline] + ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - private module Stage5alias = Stage5; + ApHeadContent projectToHeadContent(Content c) { result = c } - /** - * INTERNAL: Subject to change without notice. - * - * Contains references to individual pruning stages. - */ - module Stages { - module Stage1 = Stage1alias; + class ApOption = AccessPathApproxOption; - module Stage2 = Stage2alias; + ApOption apNone() { result = TAccessPathApproxNone() } - module Stage3 = Stage3alias; + ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - module Stage4 = Stage4alias; + private module CallContextSensitivityInput implements CallContextSensitivityInputSig { + predicate relevantCallEdgeIn = PrevStage::relevantCallEdgeIn/2; - module Stage5 = Stage5alias; - } + predicate relevantCallEdgeOut = PrevStage::relevantCallEdgeOut/2; - /** - * INTERNAL: Only for debugging. - * - * Contains references to individual pruning stages and stage statistics. - */ - module Debug { - import Stages + predicate reducedViableImplInCallContextCand = + Stage3Param::reducedViableImplInCallContext/3; - predicate stageStats1( - int n, string stage, int nodes, int fields, int conscand, int states, int tuples, - int calledges, int tfnodes, int tftuples - ) { - stage = "1 Fwd" and - n = 10 and - Stage1::stats(true, nodes, fields, conscand, states, tuples, calledges) and - tfnodes = -1 and - tftuples = -1 - or - stage = "1 Rev" and - n = 15 and - Stage1::stats(false, nodes, fields, conscand, states, tuples, calledges) and - tfnodes = -1 and - tftuples = -1 + predicate reducedViableImplInReturnCand = Stage3Param::reducedViableImplInReturn/2; } - predicate stageStats2( - int n, string stage, int nodes, int fields, int conscand, int states, int tuples, - int calledges, int tfnodes, int tftuples - ) { - stageStats1(n, stage, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) - or - stage = "2 Fwd" and - n = 20 and - Stage2::stats(true, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) - or - stage = "2 Rev" and - n = 25 and - Stage2::stats(false, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) - } + import CallContextSensitivity + import LocalCallContext - predicate stageStats3( - int n, string stage, int nodes, int fields, int conscand, int states, int tuples, - int calledges, int tfnodes, int tftuples - ) { - stageStats2(n, stage, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) - or - stage = "3 Fwd" and - n = 30 and - Stage3::stats(true, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) - or - stage = "3 Rev" and - n = 35 and - Stage3::stats(false, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) - } + predicate localStep = + PrevStage::LocalFlowBigStep::localFlowBigStepTc/8; - predicate stageStats4( - int n, string stage, int nodes, int fields, int conscand, int states, int tuples, - int calledges, int tfnodes, int tftuples - ) { - stageStats3(n, stage, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) - or - stage = "4 Fwd" and - n = 40 and - Stage4::stats(true, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) - or - stage = "4 Rev" and - n = 45 and - Stage4::stats(false, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) + bindingset[node, state, t0, ap] + predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { + strengthenType(node, t0, t) and + exists(state) and + exists(ap) } - predicate stageStats5( - int n, string stage, int nodes, int fields, int conscand, int states, int tuples, - int calledges, int tfnodes, int tftuples - ) { - stageStats4(n, stage, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) - or - stage = "5 Fwd" and - n = 50 and - Stage5::stats(true, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) - or - stage = "5 Rev" and - n = 55 and - Stage5::stats(false, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) + pragma[nomagic] + private predicate clearExceptStore(NodeEx node, Ap ap) { + Stage4Param::clearContent(node, ap.getHead(), true) } - predicate stageStats( - int n, string stage, int nodes, int fields, int conscand, int states, int tuples, - int calledges, int tfnodes, int tftuples - ) { - stageStats5(n, stage, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) - or - stage = "6 Fwd" and - n = 60 and - Stage6::stats(true, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) - or - stage = "6 Rev" and - n = 65 and - Stage6::stats(false, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) + bindingset[node, ap, isStoreStep] + predicate stepFilter(NodeEx node, Ap ap, boolean isStoreStep) { + if clearExceptStore(node, ap) then isStoreStep = true else any() } - } - - private signature predicate flag(); - - private predicate flagEnable() { any() } - private predicate flagDisable() { none() } - - module FlowExplorationFwd { - private import FlowExploration as F - import F::Public - - predicate partialFlow = F::partialFlowFwd/3; + bindingset[t1, t2] + predicate typecheck(Typ t1, Typ t2) { compatibleTypesFilter(t1, t2) } } - module FlowExplorationRev { - private import FlowExploration as F - import F::Public + private module Stage5 = MkStage::Stage; - predicate partialFlow = F::partialFlowRev/3; + pragma[nomagic] + private predicate stage5ConsCand(Content c, AccessPathFront apf, int len) { + Stage5::consCand(c, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1)) } - private module FlowExploration< - explorationLimitSig/0 explorationLimit, flag/0 flagFwd, flag/0 flagRev> - { - class CallContext = CachedCallContextSensitivity::Cc; - - class CallContextCall = CachedCallContextSensitivity::CcCall; - - class CallContextNoCall = CachedCallContextSensitivity::CcNoCall; + /** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ + private int count1to2unfold(AccessPathApproxCons1 apa) { + exists(Content c, int len | + c = apa.getHead() and + len = apa.len() and + result = strictcount(AccessPathFront apf | stage5ConsCand(c, apf, len)) + ) + } - predicate callContextNone = CachedCallContextSensitivity::ccNone/0; + private int countNodesUsingAccessPath(AccessPathApprox apa) { + result = + strictcount(NodeEx n, FlowState state | + Stage5::revFlow(n, state, apa) or Stage5::nodeMayUseSummary(n, state, apa) + ) + } - predicate callContextSomeCall = CachedCallContextSensitivity::ccSomeCall/0; + /** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ + private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa) and + nodes = countNodesUsingAccessPath(apa) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) + } - private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2) { - exists(NodeEx node1, NodeEx node2 | - jumpStepEx(node1, node2) - or - additionalJumpStep(node1, node2, _) - or - additionalJumpStateStep(node1, _, node2, _, _) - or - // flow into callable - viableParamArgEx(_, node2, node1) - or - // flow out of a callable - viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2) - | - c1 = node1.getEnclosingCallable() and - c2 = node2.getEnclosingCallable() and - c1 != c2 - ) - } + private predicate hasTail(AccessPathApprox apa, AccessPathApprox tail) { + exists(Content head | + apa.isCons(head, tail) and + Stage5::consCand(head, tail) + ) + } - private predicate interestingCallableSrc(DataFlowCallable c) { - exists(Node n | isRelevantSource(n, _) and c = getNodeEnclosingCallable(n)) - or - exists(DataFlowCallable mid | interestingCallableSrc(mid) and callableStep(mid, c)) - } + private predicate forceUnfold(AccessPathApprox apa) { + forceHighPrecision(apa.getHead()) + or + exists(Content c2 | + apa = TConsCons(_, c2, _) and + forceHighPrecision(c2) + ) + } - private predicate interestingCallableSink(DataFlowCallable c) { - exists(Node n | c = getNodeEnclosingCallable(n) | - isRelevantSink(n, _) or - isRelevantSink(n) + /** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ + private predicate evalUnfold(AccessPathApprox apa, boolean unfold) { + if forceUnfold(apa) + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa) and + nodes = countNodesUsingAccessPath(apa) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true ) - or - exists(DataFlowCallable mid | interestingCallableSink(mid) and callableStep(c, mid)) - } - - private newtype TCallableExt = - TCallable(DataFlowCallable c) { - interestingCallableSrc(c) or - interestingCallableSink(c) - } or - TCallableSrc() or - TCallableSink() + } - private predicate callableExtSrc(TCallableSrc src) { any() } + /** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ + private int countAps(AccessPathApprox apa) { + evalUnfold(apa, false) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa)) + or + evalUnfold(apa, false) and + result = count1to2unfold(apa) and + not expensiveLen1to2unfolding(apa) + or + evalUnfold(apa, true) and + result = countPotentialAps(apa) + } - private predicate callableExtSink(TCallableSink sink) { any() } + /** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ + language[monotonicAggregates] + private int countPotentialAps(AccessPathApprox apa) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | hasTail(apa, tail) | countAps(tail)) + } - private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) { - exists(DataFlowCallable c1, DataFlowCallable c2 | - callableStep(c1, c2) and - ce1 = TCallable(c1) and - ce2 = TCallable(c2) + private newtype TAccessPath = + TAccessPathNil() or + TAccessPathCons(Content head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false) and + head = apa.getHead() and + hasTail(apa, tail.getApprox()) ) - or - exists(Node n | - ce1 = TCallableSrc() and - isRelevantSource(n, _) and - ce2 = TCallable(getNodeEnclosingCallable(n)) + } or + TAccessPathCons2(Content head1, Content head2, int len) { + exists(AccessPathApproxCons apa, AccessPathApprox tail | + evalUnfold(apa, false) and + not expensiveLen1to2unfolding(apa) and + apa.len() = len and + hasTail(apa, tail) and + head1 = apa.getHead() and + head2 = tail.getHead() ) - or - exists(Node n | - ce2 = TCallableSink() and - ce1 = TCallable(getNodeEnclosingCallable(n)) - | - isRelevantSink(n, _) or - isRelevantSink(n) + } or + TAccessPathCons1(Content head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false) and + expensiveLen1to2unfolding(apa) and + apa.len() = len and + head = apa.getHead() ) } - private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) { - callableExtStepFwd(ce2, ce1) - } - - private int distSrcExt(TCallableExt c) = - shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result) + private module Stage6Param implements MkStage::StageParam { + private module PrevStage = Stage5; - private int distSinkExt(TCallableExt c) = - shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result) + class Typ = DataFlowType; - private int distSrc(DataFlowCallable c) { result = distSrcExt(TCallable(c)) - 1 } + class Ap = AccessPath; - private int distSink(DataFlowCallable c) { result = distSinkExt(TCallable(c)) - 1 } + class ApNil = AccessPathNil; - private newtype TPartialAccessPath = - TPartialNil() or - TPartialCons(Content c, int len) { len in [1 .. Config::accessPathLimit()] } + pragma[nomagic] + PrevStage::Ap getApprox(Ap ap) { result = ap.getApprox() } - /** - * Conceptually a list of `Content`s, but only the first - * element of the list and its length are tracked. - */ - private class PartialAccessPath extends TPartialAccessPath { - abstract string toString(); + Typ getTyp(DataFlowType t) { result = t } - Content getHead() { this = TPartialCons(result, _) } + bindingset[c, tail] + pragma[inline_late] + Ap apCons(Content c, Ap tail) { result.isCons(c, tail) } - int len() { - this = TPartialNil() and result = 0 - or - this = TPartialCons(_, result) - } - } + class ApHeadContent = Content; - private class PartialAccessPathNil extends PartialAccessPath, TPartialNil { - override string toString() { result = "" } - } + pragma[noinline] + ApHeadContent getHeadContent(Ap ap) { result = ap.getHead() } - private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { - override string toString() { - exists(Content c, int len | this = TPartialCons(c, len) | - if len = 1 - then result = "[" + c.toString() + "]" - else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" - ) - } - } + ApHeadContent projectToHeadContent(Content c) { result = c } - private predicate relevantState(FlowState state) { - sourceNode(_, state) or - sinkNodeWithState(_, state) or - additionalLocalStateStep(_, state, _, _, _) or - additionalLocalStateStep(_, _, _, state, _) or - additionalJumpStateStep(_, state, _, _, _) or - additionalJumpStateStep(_, _, _, state, _) - } + private module ApOption = Option; - private predicate revSinkNode(NodeEx node, FlowState state) { - sinkNodeWithState(node, state) - or - isRelevantSink(node.asNodeOrImplicitRead()) and - relevantState(state) and - not fullBarrier(node) and - not stateBarrier(node, state) - } + class ApOption = ApOption::Option; - private newtype TSummaryCtx1 = - TSummaryCtx1None() or - TSummaryCtx1Param(ParamNodeEx p) + ApOption apNone() { result.isNone() } - private newtype TSummaryCtx2 = - TSummaryCtx2None() or - TSummaryCtx2Some(FlowState s) { relevantState(s) } + ApOption apSome(Ap ap) { result = ApOption::some(ap) } - private newtype TSummaryCtx3 = - TSummaryCtx3None() or - TSummaryCtx3Some(DataFlowType t) + private module CallContextSensitivityInput implements CallContextSensitivityInputSig { + predicate relevantCallEdgeIn = PrevStage::relevantCallEdgeIn/2; - private newtype TSummaryCtx4 = - TSummaryCtx4None() or - TSummaryCtx4Some(PartialAccessPath ap) + predicate relevantCallEdgeOut = PrevStage::relevantCallEdgeOut/2; - private newtype TRevSummaryCtx1 = - TRevSummaryCtx1None() or - TRevSummaryCtx1Some(ReturnPosition pos) + predicate reducedViableImplInCallContextCand = + Stage5Param::reducedViableImplInCallContext/3; - private newtype TRevSummaryCtx2 = - TRevSummaryCtx2None() or - TRevSummaryCtx2Some(FlowState s) { relevantState(s) } + predicate reducedViableImplInReturnCand = Stage5Param::reducedViableImplInReturn/2; + } - private newtype TRevSummaryCtx3 = - TRevSummaryCtx3None() or - TRevSummaryCtx3Some(PartialAccessPath ap) + import CallContextSensitivity + import LocalCallContext - private newtype TPartialPathNode = - TPartialPathNodeFwd( - NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, - TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - flagFwd() and - sourceNode(node, state) and - cc = callContextNone() and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - t = node.getDataFlowType() and - ap = TPartialNil() and - exists(explorationLimit()) - or - partialPathStep(_, node, state, cc, sc1, sc2, sc3, sc4, t, ap) and - distSrc(node.getEnclosingCallable()) <= explorationLimit() - } or - TPartialPathNodeRev( - NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, - TRevSummaryCtx3 sc3, PartialAccessPath ap - ) { - flagRev() and - revSinkNode(node, state) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = TPartialNil() and - exists(explorationLimit()) - or - revPartialPathStep(_, node, state, sc1, sc2, sc3, ap) and - distSink(node.getEnclosingCallable()) <= explorationLimit() - } + predicate localStep = + PrevStage::LocalFlowBigStep::localFlowBigStepTc/8; - // inline to reduce fan-out via `getAReadContent` - bindingset[c] - private predicate clearsContentEx(NodeEx n, Content c) { - exists(ContentSet cs | - clearsContentSet(n, cs) and - pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() - ) + bindingset[node, state, t0, ap] + predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { + strengthenType(node, t0, t) and + exists(state) and + exists(ap) } pragma[nomagic] - private predicate partialPathStep( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - partialPathStep1(mid, node, state, cc, sc1, sc2, sc3, sc4, _, t, ap) + private predicate clearExceptStore(NodeEx node, Ap ap) { + Stage4Param::clearContent(node, ap.getHead(), true) } - pragma[nomagic] - private predicate partialPathStep1( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t0, DataFlowType t, - PartialAccessPath ap - ) { - exists(boolean isStoreStep | - partialPathStep0(mid, node, state, cc, sc1, sc2, sc3, sc4, t0, ap, isStoreStep) and - not fullBarrier(node) and - not stateBarrier(node, state) and - not inBarrier(node, state) and - ( - notExpectsContent(node) or - expectsContentEx(node, ap.getHead()) - ) and - strengthenType(node, t0, t) - | - isStoreStep = true or - not clearsContentEx(node, ap.getHead()) - ) + bindingset[node, ap, isStoreStep] + predicate stepFilter(NodeEx node, Ap ap, boolean isStoreStep) { + if clearExceptStore(node, ap) then isStoreStep = true else any() } + bindingset[t1, t2] + predicate typecheck(Typ t1, Typ t2) { compatibleTypesFilter(t1, t2) } + } + + module Stage6 = MkStage::Stage; + + /** + * A list of `Content`s. + * + * If data flows from a source to a given node with a given `AccessPath`, + * this indicates the sequence of dereference operations needed to get from + * the value in the node to the tracked object. + */ + private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract Content getHead(); + + /** Holds if this is a representation of `head` followed by `tail`. */ pragma[nomagic] - private predicate partialPathTypeStrengthen( - DataFlowType t0, PartialAccessPath ap, DataFlowType t - ) { - partialPathStep1(_, _, _, _, _, _, _, _, t0, t, ap) and t0 != t - } + abstract predicate isCons(Content head, AccessPath tail); - module Public { - /** - * A `Node` augmented with a call context, an access path, and a configuration. - */ - class PartialPathNode extends TPartialPathNode { - /** Gets a textual representation of this element. */ - string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); - /** - * Gets a textual representation of this element, including a textual - * representation of the call context. - */ - string toStringWithContext() { - result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() - } + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); - /** Gets the location of this node. */ - Location getLocation() { result = this.getNodeEx().getLocation() } + /** Gets the length of this access path. */ + abstract int length(); - /** - * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). - */ - pragma[inline] - deprecated predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } + /** Gets a textual representation of this access path. */ + abstract string toString(); + } - /** Gets the underlying `Node`. */ - final Node getNode() { this.getNodeEx().projectToNode() = result } + private class AccessPathNil extends AccessPath, TAccessPathNil { + override Content getHead() { none() } - FlowState getState() { none() } + override predicate isCons(Content head, AccessPath tail) { none() } - private NodeEx getNodeEx() { - result = this.(PartialPathNodeFwd).getNodeEx() or - result = this.(PartialPathNodeRev).getNodeEx() - } + override AccessPathFrontNil getFront() { result = TFrontNil() } - /** Gets a successor of this node, if any. */ - PartialPathNode getASuccessor() { none() } + override AccessPathApproxNil getApprox() { result = TNil() } - /** - * Gets the approximate distance to the nearest source measured in number - * of interprocedural steps. - */ - int getSourceDistance() { result = distSrc(this.getNodeEx().getEnclosingCallable()) } + override int length() { result = 0 } - /** - * Gets the approximate distance to the nearest sink measured in number - * of interprocedural steps. - */ - int getSinkDistance() { result = distSink(this.getNodeEx().getEnclosingCallable()) } + override string toString() { result = "" } + } - private string ppType() { - this instanceof PartialPathNodeRev and result = "" - or - exists(string t | t = this.(PartialPathNodeFwd).getType().toString() | - if t = "" then result = "" else result = " : " + t - ) - } + private class AccessPathCons extends AccessPath, TAccessPathCons { + private Content head_; + private AccessPath tail_; - private string ppAp() { - exists(string s | - s = this.(PartialPathNodeFwd).getAp().toString() or - s = this.(PartialPathNodeRev).getAp().toString() - | - if s = "" then result = "" else result = " " + s - ) - } + AccessPathCons() { this = TAccessPathCons(head_, tail_) } - private string ppCtx() { - result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">" - } + override Content getHead() { result = head_ } - /** Holds if this is a source in a forward-flow path. */ - predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() } + override predicate isCons(Content head, AccessPath tail) { head = head_ and tail = tail_ } - /** Holds if this is a sink in a reverse-flow path. */ - predicate isRevSink() { this.(PartialPathNodeRev).isSink() } - } + override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - /** - * Provides the query predicates needed to include a graph in a path-problem query. - */ - module PartialPathGraph { - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b } - } + override AccessPathApproxCons getApprox() { + result = TConsNil(head_) and tail_ = TAccessPathNil() + or + result = TConsCons(head_, tail_.getHead(), this.length()) + or + result = TCons1(head_, this.length()) } - import Public - - private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd { - NodeEx node; - FlowState state; - CallContext cc; - TSummaryCtx1 sc1; - TSummaryCtx2 sc2; - TSummaryCtx3 sc3; - TSummaryCtx4 sc4; - DataFlowType t; - PartialAccessPath ap; - - PartialPathNodeFwd() { - this = TPartialPathNodeFwd(node, state, cc, sc1, sc2, sc3, sc4, t, ap) - } + override int length() { result = 1 + tail_.length() } + + private string toStringImpl(boolean needsSuffix) { + tail_ = TAccessPathNil() and + needsSuffix = false and + result = head_.toString() + "]" + or + result = head_ + ", " + tail_.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(Content c2, Content c3, int len | tail_ = TAccessPathCons2(c2, c3, len) | + result = head_ + ", " + c2 + ", " + c3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head_ + ", " + c2 + ", " + c3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(Content c2, int len | tail_ = TAccessPathCons1(c2, len) | + result = head_ + ", " + c2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head_ + ", " + c2 + "]" and len = 1 and needsSuffix = false + ) + } + + override string toString() { + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } + } + + private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private Content head1; + private Content head2; + private int len; - NodeEx getNodeEx() { result = node } + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } - override FlowState getState() { result = state } + override Content getHead() { result = head1 } - CallContext getCallContext() { result = cc } + override predicate isCons(Content head, AccessPath tail) { + head = head1 and + Stage5::consCand(head1, tail.getApprox()) and + tail.getHead() = head2 and + tail.length() = len - 1 + } - TSummaryCtx1 getSummaryCtx1() { result = sc1 } + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } - TSummaryCtx2 getSummaryCtx2() { result = sc2 } + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } - TSummaryCtx3 getSummaryCtx3() { result = sc3 } + override int length() { result = len } - TSummaryCtx4 getSummaryCtx4() { result = sc4 } + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = + "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } + } - DataFlowType getType() { result = t } + private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private Content head_; + private int len; - PartialAccessPath getAp() { result = ap } + AccessPathCons1() { this = TAccessPathCons1(head_, len) } - override PartialPathNodeFwd getASuccessor() { - not outBarrier(node, state) and - partialPathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), - result.getSummaryCtx1(), result.getSummaryCtx2(), result.getSummaryCtx3(), - result.getSummaryCtx4(), result.getType(), result.getAp()) - } + override Content getHead() { result = head_ } - predicate isSource() { - sourceNode(node, state) and - cc = callContextNone() and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - ap instanceof TPartialNil - } + override predicate isCons(Content head, AccessPath tail) { + head = head_ and + Stage5::consCand(head_, tail.getApprox()) and + tail.length() = len - 1 } - private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev { - NodeEx node; - FlowState state; - TRevSummaryCtx1 sc1; - TRevSummaryCtx2 sc2; - TRevSummaryCtx3 sc3; - PartialAccessPath ap; - - PartialPathNodeRev() { this = TPartialPathNodeRev(node, state, sc1, sc2, sc3, ap) } - - NodeEx getNodeEx() { result = node } + override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - override FlowState getState() { result = state } + override AccessPathApproxCons getApprox() { result = TCons1(head_, len) } - TRevSummaryCtx1 getSummaryCtx1() { result = sc1 } + override int length() { result = len } - TRevSummaryCtx2 getSummaryCtx2() { result = sc2 } + override string toString() { + if len = 1 + then result = "[" + head_.toString() + "]" + else result = "[" + head_.toString() + ", ... (" + len.toString() + ")]" + } + } - TRevSummaryCtx3 getSummaryCtx3() { result = sc3 } + private module S6Graph = Stage6::Graph; - PartialAccessPath getAp() { result = ap } + private module S6 = S6Graph::Public; - override PartialPathNodeRev getASuccessor() { - not inBarrier(node, state) and - revPartialPathStep(result, this.getNodeEx(), this.getState(), this.getSummaryCtx1(), - this.getSummaryCtx2(), this.getSummaryCtx3(), this.getAp()) - } + import S6 - predicate isSink() { - revSinkNode(node, state) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = TPartialNil() - } - } + /** + * Holds if data can flow from `source` to `sink`. + */ + predicate flow(Node source, Node sink) { + exists(PathNode source0, PathNode sink0 | + flowPath(source0, sink0) and source0.getNode() = source and sink0.getNode() = sink + ) + } - pragma[nomagic] - private predicate partialPathStep0( - PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, - TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap, - boolean isStoreStep - ) { - not isUnreachableInCall1(node, - CachedCallContextSensitivity::LocalCallContext::getLocalCc(cc)) and - ( - localFlowStepEx(mid.getNodeEx(), node, _) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - t = mid.getType() and - ap = mid.getAp() - or - additionalLocalFlowStep(mid.getNodeEx(), node, _) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - or - additionalLocalStateStep(mid.getNodeEx(), mid.getState(), node, state, _) and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() - ) and - isStoreStep = false - or - jumpStepEx(mid.getNodeEx(), node) and - state = mid.getState() and - cc = callContextNone() and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - t = mid.getType() and - ap = mid.getAp() and - isStoreStep = false - or - additionalJumpStep(mid.getNodeEx(), node, _) and - state = mid.getState() and - cc = callContextNone() and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() and - isStoreStep = false - or - additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state, _) and - cc = callContextNone() and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - mid.getAp() instanceof PartialAccessPathNil and - t = node.getDataFlowType() and - ap = TPartialNil() and - isStoreStep = false - or - partialPathStoreStep(mid, _, _, _, node, t, ap) and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - isStoreStep = true - or - exists(DataFlowType t0, PartialAccessPath ap0, Content c | - partialPathReadStep(mid, t0, ap0, c, node, cc) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - apConsFwd(t, ap, c, t0, ap0) - ) and - isStoreStep = false - or - partialPathIntoCallable(mid, node, state, _, cc, sc1, sc2, sc3, sc4, _, t, ap) and - isStoreStep = false - or - partialPathOutOfCallable(mid, node, state, cc, t, ap) and - sc1 = TSummaryCtx1None() and - sc2 = TSummaryCtx2None() and - sc3 = TSummaryCtx3None() and - sc4 = TSummaryCtx4None() and - isStoreStep = false - or - partialPathThroughCallable(mid, node, state, cc, t, ap) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - isStoreStep = false - } + /** + * Holds if data can flow from some source to `sink`. + */ + predicate flowTo(Node sink) { exists(PathNode n | n.isSink() and n.getNode() = sink) } - bindingset[result, i] - private int unbindInt(int i) { pragma[only_bind_out](i) = pragma[only_bind_out](result) } + /** + * Holds if data can flow from some source to `sink`. + */ + predicate flowToExpr(DataFlowExpr sink) { flowTo(exprNode(sink)) } - pragma[inline] - private predicate partialPathStoreStep( - PartialPathNodeFwd mid, DataFlowType t1, PartialAccessPath ap1, Content c, NodeEx node, - DataFlowType t2, PartialAccessPath ap2 - ) { - exists(NodeEx midNode, DataFlowType contentType | - midNode = mid.getNodeEx() and - t1 = mid.getType() and - ap1 = mid.getAp() and - storeUnrestricted(midNode, c, node, contentType, t2) and - ap2.getHead() = c and - ap2.len() = unbindInt(ap1.len() + 1) and - compatibleTypesFilter(t1, contentType) - ) - } + /** + * INTERNAL: Only for debugging. + * + * Calculates per-stage metrics for data flow. + */ + predicate stageStats = Debug::stageStats/10; - pragma[nomagic] - private predicate apConsFwd( - DataFlowType t1, PartialAccessPath ap1, Content c, DataFlowType t2, PartialAccessPath ap2 - ) { - partialPathStoreStep(_, t1, ap1, c, _, t2, ap2) - or - exists(DataFlowType t0 | - partialPathTypeStrengthen(t0, ap2, t2) and - apConsFwd(t1, ap1, c, t0, ap2) - ) - } + private module Stage1alias = Stage1; - pragma[nomagic] - private predicate partialPathReadStep( - PartialPathNodeFwd mid, DataFlowType t, PartialAccessPath ap, Content c, NodeEx node, - CallContext cc - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - t = mid.getType() and - ap = mid.getAp() and - read(midNode, c, node) and - ap.getHead() = c and - cc = mid.getCallContext() - ) - } + private module Stage2alias = Stage2; - private predicate partialPathOutOfCallable0( - PartialPathNodeFwd mid, ReturnPosition pos, FlowState state, CallContext innercc, - DataFlowType t, PartialAccessPath ap - ) { - pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and - state = mid.getState() and - innercc = mid.getCallContext() and - innercc instanceof CallContextNoCall and - t = mid.getType() and - ap = mid.getAp() - } + private module Stage3alias = Stage3; - pragma[nomagic] - private predicate partialPathOutOfCallable1( - PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, FlowState state, - CallContext cc, DataFlowType t, PartialAccessPath ap - ) { - exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - partialPathOutOfCallable0(mid, pos, state, innercc, t, ap) and - c = pos.getCallable() and - kind = pos.getKind() and - CachedCallContextSensitivity::resolveReturn(innercc, c, call) and - cc = CachedCallContextSensitivity::getCallContextReturn(c, call) - ) - } + private module Stage4alias = Stage4; - private predicate partialPathOutOfCallable( - PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, - PartialAccessPath ap - ) { - exists(ReturnKindExt kind, DataFlowCall call | - partialPathOutOfCallable1(mid, call, kind, state, cc, t, ap) and - out = kind.getAnOutNodeEx(call) - ) - } + private module Stage5alias = Stage5; - pragma[noinline] - private predicate partialPathIntoArg( - PartialPathNodeFwd mid, ParameterPosition ppos, FlowState state, CallContext cc, - DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - exists(ArgNode arg, ArgumentPosition apos | - arg = mid.getNodeEx().asNode() and - state = mid.getState() and - cc = mid.getCallContext() and - arg.argumentOf(call, apos) and - t = mid.getType() and - ap = mid.getAp() and - parameterMatch(ppos, apos) - ) - } + /** + * INTERNAL: Subject to change without notice. + * + * Contains references to individual pruning stages. + */ + module Stages { + module Stage1 = Stage1alias; - pragma[nomagic] - private predicate partialPathIntoCallable0( - PartialPathNodeFwd mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, - CallContext outercc, DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - partialPathIntoArg(mid, pos, state, outercc, call, t, ap) and - callable = CachedCallContextSensitivity::resolveCall(call, outercc) - } + module Stage2 = Stage2alias; - private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParamNodeEx p, FlowState state, CallContext outercc, - CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, - TSummaryCtx4 sc4, DataFlowCall call, DataFlowType t, PartialAccessPath ap - ) { - exists(ParameterPosition pos, DataFlowCallable callable | - partialPathIntoCallable0(mid, callable, pos, state, outercc, call, t, ap) and - p.isParameterOf(callable, pos) and - sc1 = TSummaryCtx1Param(p) and - sc2 = TSummaryCtx2Some(state) and - sc3 = TSummaryCtx3Some(t) and - sc4 = TSummaryCtx4Some(ap) and - innercc = - CachedCallContextSensitivity::LocalCallContext::getCallContextCall(call, callable) - ) - } + module Stage3 = Stage3alias; - pragma[nomagic] - private predicate paramFlowsThroughInPartialPath( - ReturnKindExt kind, FlowState state, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, - TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap - ) { - exists(PartialPathNodeFwd mid, RetNodeEx ret | - mid.getNodeEx() = ret and - kind = ret.getKind() and - state = mid.getState() and - cc = mid.getCallContext() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc4 = mid.getSummaryCtx4() and - t = mid.getType() and - ap = mid.getAp() - ) - } + module Stage4 = Stage4alias; - pragma[noinline] - private predicate partialPathThroughCallable0( - DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, FlowState state, - CallContext cc, DataFlowType t, PartialAccessPath ap - ) { - exists( - CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, - TSummaryCtx4 sc4 - | - partialPathIntoCallable(mid, _, _, cc, innercc, sc1, sc2, sc3, sc4, call, _, _) and - paramFlowsThroughInPartialPath(kind, state, innercc, sc1, sc2, sc3, sc4, t, ap) - ) - } + module Stage5 = Stage5alias; + } - private predicate partialPathThroughCallable( - PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, - PartialAccessPath ap - ) { - exists(DataFlowCall call, ReturnKindExt kind | - partialPathThroughCallable0(call, mid, kind, state, cc, t, ap) and - out = kind.getAnOutNodeEx(call) - ) - } + /** + * INTERNAL: Only for debugging. + * + * Contains references to individual pruning stages and stage statistics. + */ + module Debug { + import Stages - pragma[nomagic] - private predicate revPartialPathStep( - PartialPathNodeRev mid, NodeEx node, FlowState state, TRevSummaryCtx1 sc1, - TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, PartialAccessPath ap + predicate stageStats1( + int n, string stage, int nodes, int fields, int conscand, int states, int tuples, + int calledges, int tfnodes, int tftuples ) { - exists(boolean isStoreStep | - revPartialPathStep0(mid, node, state, sc1, sc2, sc3, ap, isStoreStep) and - ( - notExpectsContent(node) or - expectsContentEx(node, ap.getHead()) - ) and - not fullBarrier(node) and - not stateBarrier(node, state) and - not outBarrier(node, state) and - // if a node is not the target of a store, we can check `clearsContent` immediately - ( - storeUnrestricted(_, _, node, _, _) - or - not clearsContentEx(node, ap.getHead()) - ) - | - // if a node is the target of a store, we can only check `clearsContent` - // when we know whether we took the store step - isStoreStep = true - or - exists(NodeEx midNode, PartialAccessPath midAp | - midNode = mid.getNodeEx() and - midAp = mid.getAp() and - not clearsContentEx(midNode, midAp.getHead()) - ) - ) + stage = "1 Fwd" and + n = 10 and + Stage1::stats(true, nodes, fields, conscand, states, tuples, calledges) and + tfnodes = -1 and + tftuples = -1 + or + stage = "1 Rev" and + n = 15 and + Stage1::stats(false, nodes, fields, conscand, states, tuples, calledges) and + tfnodes = -1 and + tftuples = -1 } - pragma[nomagic] - private predicate revPartialPathStep0( - PartialPathNodeRev mid, NodeEx node, FlowState state, TRevSummaryCtx1 sc1, - TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, PartialAccessPath ap, boolean isStoreStep + predicate stageStats2( + int n, string stage, int nodes, int fields, int conscand, int states, int tuples, + int calledges, int tfnodes, int tftuples ) { - localFlowStepEx(node, mid.getNodeEx(), _) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - ap = mid.getAp() and - isStoreStep = false - or - additionalLocalFlowStep(node, mid.getNodeEx(), _) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() and - isStoreStep = false - or - additionalLocalStateStep(node, state, mid.getNodeEx(), mid.getState(), _) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() and - isStoreStep = false - or - jumpStepEx(node, mid.getNodeEx()) and - state = mid.getState() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = mid.getAp() and - isStoreStep = false - or - additionalJumpStep(node, mid.getNodeEx(), _) and - state = mid.getState() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() and - isStoreStep = false - or - additionalJumpStateStep(node, state, mid.getNodeEx(), mid.getState(), _) and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil() and - isStoreStep = false - or - revPartialPathReadStep(mid, _, _, node, ap) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - isStoreStep = false - or - exists(PartialAccessPath ap0, Content c | - revPartialPathStoreStep(mid, ap0, c, node) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - apConsRev(ap, c, ap0) and - isStoreStep = true - ) - or - exists(ParamNodeEx p | - mid.getNodeEx() = p and - viableParamArgEx(_, p, node) and - state = mid.getState() and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - sc1 = TRevSummaryCtx1None() and - sc2 = TRevSummaryCtx2None() and - sc3 = TRevSummaryCtx3None() and - ap = mid.getAp() and - isStoreStep = false - ) + stageStats1(n, stage, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) or - exists(ReturnPosition pos | - revPartialPathIntoReturn(mid, pos, state, sc1, sc2, sc3, _, ap) and - pos = node.(RetNodeEx).getReturnPosition() and - isStoreStep = false - ) + stage = "2 Fwd" and + n = 20 and + Stage2::stats(true, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) or - revPartialPathThroughCallable(mid, node, state, ap) and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - isStoreStep = false - } - - pragma[inline] - private predicate revPartialPathReadStep( - PartialPathNodeRev mid, PartialAccessPath ap1, Content c, NodeEx node, PartialAccessPath ap2 - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - ap1 = mid.getAp() and - read(node, c, midNode) and - ap2.getHead() = c and - ap2.len() = unbindInt(ap1.len() + 1) - ) - } - - pragma[nomagic] - private predicate apConsRev(PartialAccessPath ap1, Content c, PartialAccessPath ap2) { - revPartialPathReadStep(_, ap1, c, _, ap2) - } - - pragma[nomagic] - private predicate revPartialPathStoreStep( - PartialPathNodeRev mid, PartialAccessPath ap, Content c, NodeEx node - ) { - exists(NodeEx midNode | - midNode = mid.getNodeEx() and - ap = mid.getAp() and - storeUnrestricted(node, c, midNode, _, _) and - ap.getHead() = c - ) + stage = "2 Rev" and + n = 25 and + Stage2::stats(false, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) } - pragma[nomagic] - private predicate revPartialPathIntoReturn( - PartialPathNodeRev mid, ReturnPosition pos, FlowState state, TRevSummaryCtx1Some sc1, - TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3, DataFlowCall call, PartialAccessPath ap + predicate stageStats3( + int n, string stage, int nodes, int fields, int conscand, int states, int tuples, + int calledges, int tfnodes, int tftuples ) { - exists(NodeEx out | - mid.getNodeEx() = out and - mid.getState() = state and - viableReturnPosOutEx(call, pos, out) and - sc1 = TRevSummaryCtx1Some(pos) and - sc2 = TRevSummaryCtx2Some(state) and - sc3 = TRevSummaryCtx3Some(ap) and - ap = mid.getAp() - ) + stageStats2(n, stage, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) + or + stage = "3 Fwd" and + n = 30 and + Stage3::stats(true, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) + or + stage = "3 Rev" and + n = 35 and + Stage3::stats(false, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) } - pragma[nomagic] - private predicate revPartialPathFlowsThrough( - ArgumentPosition apos, FlowState state, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, - TRevSummaryCtx3Some sc3, PartialAccessPath ap + predicate stageStats4( + int n, string stage, int nodes, int fields, int conscand, int states, int tuples, + int calledges, int tfnodes, int tftuples ) { - exists(PartialPathNodeRev mid, ParamNodeEx p, ParameterPosition ppos | - mid.getNodeEx() = p and - mid.getState() = state and - p.getPosition() = ppos and - sc1 = mid.getSummaryCtx1() and - sc2 = mid.getSummaryCtx2() and - sc3 = mid.getSummaryCtx3() and - ap = mid.getAp() and - parameterMatch(ppos, apos) - ) + stageStats3(n, stage, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) + or + stage = "4 Fwd" and + n = 40 and + Stage4::stats(true, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) + or + stage = "4 Rev" and + n = 45 and + Stage4::stats(false, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) } - pragma[nomagic] - private predicate revPartialPathThroughCallable0( - DataFlowCall call, PartialPathNodeRev mid, ArgumentPosition pos, FlowState state, - PartialAccessPath ap + predicate stageStats5( + int n, string stage, int nodes, int fields, int conscand, int states, int tuples, + int calledges, int tfnodes, int tftuples ) { - exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3 | - revPartialPathIntoReturn(mid, _, _, sc1, sc2, sc3, call, _) and - revPartialPathFlowsThrough(pos, state, sc1, sc2, sc3, ap) - ) + stageStats4(n, stage, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) + or + stage = "5 Fwd" and + n = 50 and + Stage5::stats(true, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) + or + stage = "5 Rev" and + n = 55 and + Stage5::stats(false, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) } - pragma[nomagic] - private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgNodeEx node, FlowState state, PartialAccessPath ap + predicate stageStats( + int n, string stage, int nodes, int fields, int conscand, int states, int tuples, + int calledges, int tfnodes, int tftuples ) { - exists(DataFlowCall call, ArgumentPosition pos | - revPartialPathThroughCallable0(call, mid, pos, state, ap) and - node.argumentOf(call, pos) - ) - } - - private predicate fwdPartialFlow(PartialPathNode source, PartialPathNode node) { - source.isFwdSource() and - node = source.getASuccessor+() - } - - private predicate revPartialFlow(PartialPathNode node, PartialPathNode sink) { - sink.isRevSink() and - node.getASuccessor+() = sink - } - - /** - * Holds if there is a partial data flow path from `source` to `node`. The - * approximate distance between `node` and the closest source is `dist` and - * is restricted to be less than or equal to `explorationLimit()`. This - * predicate completely disregards sink definitions. - * - * This predicate is intended for data-flow exploration and debugging and may - * perform poorly if the number of sources is too big and/or the exploration - * limit is set too high without using barriers. - * - * To use this in a `path-problem` query, import the module `PartialPathGraph`. - */ - predicate partialFlowFwd(PartialPathNode source, PartialPathNode node, int dist) { - fwdPartialFlow(source, node) and - dist = node.getSourceDistance() - } - - /** - * Holds if there is a partial data flow path from `node` to `sink`. The - * approximate distance between `node` and the closest sink is `dist` and - * is restricted to be less than or equal to `explorationLimit()`. This - * predicate completely disregards source definitions. - * - * This predicate is intended for data-flow exploration and debugging and may - * perform poorly if the number of sinks is too big and/or the exploration - * limit is set too high without using barriers. - * - * To use this in a `path-problem` query, import the module `PartialPathGraph`. - * - * Note that reverse flow has slightly lower precision than the corresponding - * forward flow, as reverse flow disregards type pruning among other features. - */ - predicate partialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) { - revPartialFlow(node, sink) and - dist = node.getSinkDistance() + stageStats5(n, stage, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) + or + stage = "6 Fwd" and + n = 60 and + Stage6::stats(true, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) + or + stage = "6 Rev" and + n = 65 and + Stage6::stats(false, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) } } } diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll new file mode 100644 index 000000000000..86c8c2b25e19 --- /dev/null +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll @@ -0,0 +1,2280 @@ +/** + * INTERNAL: Do not use. + * + * Provides an implementation of a fast initial pruning of global + * (interprocedural) data flow reachability (Stage 1). + */ + +private import codeql.util.Unit +private import codeql.util.Location +private import codeql.dataflow.DataFlow +private import DataFlowImpl + +module MakeImplStage1 Lang> { + private import Lang + private import DataFlowMake + private import MakeImpl as Impl + private import DataFlowImplCommon::MakeImplCommon + private import DataFlowImplCommonPublic + + bindingset[this] + signature class FlowStateSig; + + signature module Stage1Output { + bindingset[source, sink] + predicate isRelevantSourceSinkPair(Node source, Node sink); + + predicate isRelevantSink(Node sink, FlowState state); + + predicate isRelevantSink(Node sink); + + predicate inBarrier(NodeEx node, FlowState state); + + predicate outBarrier(NodeEx node, FlowState state); + + predicate stateBarrier(NodeEx node, FlowState state); + + predicate sourceNode(NodeEx node, FlowState state); + + predicate sinkNode(NodeEx node, FlowState state); + + predicate hasSourceCallCtx(); + + predicate hasSinkCallCtx(); + + predicate jumpStepEx(NodeEx node1, NodeEx node2); + + predicate additionalJumpStep(NodeEx node1, NodeEx node2, string model); + + predicate additionalJumpStateStep( + NodeEx node1, FlowState s1, NodeEx node2, FlowState s2, string model + ); + + predicate localStepNodeCand1( + NodeEx node1, NodeEx node2, boolean preservesValue, DataFlowType t, LocalCallContext lcc, + string label + ); + + predicate localStateStepNodeCand1( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, DataFlowType t, + LocalCallContext lcc, string label + ); + + bindingset[c] + predicate expectsContentEx(NodeEx n, Content c); + + predicate notExpectsContent(NodeEx n); + + bindingset[p, kind] + predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind); + + // begin StageSig + class Ap; + + class ApNil extends Ap; + + predicate revFlow(NodeEx node); + + bindingset[node, state] + predicate revFlow(NodeEx node, FlowState state, Ap ap); + + predicate callMayFlowThroughRev(DataFlowCall call); + + predicate parameterMayFlowThrough(ParamNodeEx p, boolean emptyAp); + + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind); + + predicate storeStepCand( + NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, DataFlowType containerType + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2); + + predicate callEdgeArgParam( + DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, boolean emptyAp + ); + + predicate callEdgeReturn( + DataFlowCall call, DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow + ); + + predicate relevantCallEdgeIn(DataFlowCall call, DataFlowCallable c); + + predicate relevantCallEdgeOut(DataFlowCall call, DataFlowCallable c); + + // end StageSig + predicate revFlowIsReadAndStored(Content c); + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, int calledges + ); + + predicate revFlowState(FlowState state); + } + + module ImplStage1 { + private class FlowState = Config::FlowState; + + private module SourceSinkFiltering { + private import codeql.util.AlertFiltering + + private module AlertFiltering = AlertFilteringImpl; + + pragma[nomagic] + private predicate isFilteredSource(Node source) { + Config::isSource(source, _) and + if Config::observeDiffInformedIncrementalMode() + then AlertFiltering::filterByLocation(Config::getASelectedSourceLocation(source)) + else any() + } + + pragma[nomagic] + private predicate isFilteredSink(Node sink) { + ( + Config::isSink(sink, _) or + Config::isSink(sink) + ) and + if Config::observeDiffInformedIncrementalMode() + then AlertFiltering::filterByLocation(Config::getASelectedSinkLocation(sink)) + else any() + } + + private predicate hasFilteredSource() { isFilteredSource(_) } + + private predicate hasFilteredSink() { isFilteredSink(_) } + + predicate isRelevantSource(Node source, FlowState state) { + // If there are filtered sinks, we need to pass through all sources to preserve all alerts + // with filtered sinks. Otherwise the only alerts of interest are those with filtered + // sources, so we can perform the source filtering right here. + Config::isSource(source, state) and + ( + isFilteredSource(source) or + hasFilteredSink() + ) + } + + predicate isRelevantSink(Node sink, FlowState state) { + // If there are filtered sources, we need to pass through all sinks to preserve all alerts + // with filtered sources. Otherwise the only alerts of interest are those with filtered + // sinks, so we can perform the sink filtering right here. + Config::isSink(sink, state) and + ( + isFilteredSink(sink) or + hasFilteredSource() + ) + } + + predicate isRelevantSink(Node sink) { + // If there are filtered sources, we need to pass through all sinks to preserve all alerts + // with filtered sources. Otherwise the only alerts of interest are those with filtered + // sinks, so we can perform the sink filtering right here. + Config::isSink(sink) and + ( + isFilteredSink(sink) or + hasFilteredSource() + ) + } + + bindingset[source, sink] + pragma[inline_late] + predicate isRelevantSourceSinkPair(Node source, Node sink) { + isFilteredSource(source) or + isFilteredSink(sink) + } + } + + private import SourceSinkFiltering + + private predicate inBarrier(NodeEx node) { + exists(Node n | + node.asNode() = n and + Config::isBarrierIn(n) and + isRelevantSource(n, _) + ) + } + + pragma[nomagic] + private predicate inBarrier(NodeEx node, FlowState state) { + exists(Node n | + node.asNode() = n and + Config::isBarrierIn(n, state) and + isRelevantSource(n, state) + ) + } + + private predicate outBarrier(NodeEx node) { + exists(Node n | + node.asNodeOrImplicitRead() = n and + Config::isBarrierOut(n) + | + isRelevantSink(n, _) + or + isRelevantSink(n) + ) + } + + pragma[nomagic] + private predicate outBarrier(NodeEx node, FlowState state) { + exists(Node n | + node.asNodeOrImplicitRead() = n and + Config::isBarrierOut(n, state) + | + isRelevantSink(n, state) + or + isRelevantSink(n) + ) + } + + pragma[nomagic] + private predicate fullBarrier(NodeEx node) { + exists(Node n | node.asNode() = n | + Config::isBarrier(n) + or + Config::isBarrierIn(n) and + not isRelevantSource(n, _) + or + Config::isBarrierOut(n) and + not isRelevantSink(n, _) and + not isRelevantSink(n) + ) + } + + pragma[nomagic] + private predicate stateBarrier(NodeEx node, FlowState state) { + exists(Node n | node.asNode() = n | + Config::isBarrier(n, state) + or + Config::isBarrierIn(n, state) and + not isRelevantSource(n, state) + or + Config::isBarrierOut(n, state) and + not isRelevantSink(n, state) and + not isRelevantSink(n) + ) + } + + pragma[nomagic] + private predicate sourceNode(NodeEx node, FlowState state) { + isRelevantSource(node.asNode(), state) and + not fullBarrier(node) and + not stateBarrier(node, state) + } + + pragma[nomagic] + private predicate sinkNodeWithState(NodeEx node, FlowState state) { + isRelevantSink(node.asNodeOrImplicitRead(), state) and + not fullBarrier(node) and + not stateBarrier(node, state) + } + + /** Provides the relevant barriers for a step from `node1` to `node2`. */ + bindingset[node1, node2] + private predicate stepFilter(NodeEx node1, NodeEx node2) { + not outBarrier(node1) and + not inBarrier(node2) and + not fullBarrier(node1) and + not fullBarrier(node2) + } + + /** Provides the relevant barriers for a step from `node1,state1` to `node2,state2`, including stateless barriers for `node1` to `node2`. */ + bindingset[node1, state1, node2, state2] + private predicate stateStepFilter(NodeEx node1, FlowState state1, NodeEx node2, FlowState state2) { + stepFilter(node1, node2) and + not outBarrier(node1, state1) and + not inBarrier(node2, state2) and + not stateBarrier(node1, state1) and + not stateBarrier(node2, state2) + } + + bindingset[n, cc] + pragma[inline_late] + private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) { + cc.unreachable(n) + } + + /** + * Holds if data can flow in one local step from `node1` to `node2`. + */ + private predicate localFlowStepEx(NodeEx node1, NodeEx node2, string model) { + localFlowStepExImpl(node1, node2, model) and + stepFilter(node1, node2) + } + + /** + * Holds if the additional step from `node1` to `node2` does not jump between callables. + */ + private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, string model) { + exists(Node n1, Node n2 | + node1.asNodeOrImplicitRead() = n1 and + node2.asNode() = n2 and + Config::isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2), model) and + getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and + stepFilter(node1, node2) + ) + } + + private predicate additionalLocalStateStep( + NodeEx node1, FlowState s1, NodeEx node2, FlowState s2, string model + ) { + exists(Node n1, Node n2 | + node1.asNodeOrImplicitRead() = n1 and + node2.asNode() = n2 and + Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2, + model) and + getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and + stateStepFilter(node1, s1, node2, s2) + ) + } + + /** + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. + */ + private predicate jumpStepEx(NodeEx node1, NodeEx node2) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and + stepFilter(node1, node2) and + not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) + } + + /** + * Holds if the additional step from `node1` to `node2` jumps between callables. + */ + private predicate additionalJumpStep(NodeEx node1, NodeEx node2, string model) { + exists(Node n1, Node n2 | + node1.asNodeOrImplicitRead() = n1 and + node2.asNode() = n2 and + Config::isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2), model) and + getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and + stepFilter(node1, node2) and + not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) + } + + private predicate additionalJumpStateStep( + NodeEx node1, FlowState s1, NodeEx node2, FlowState s2, string model + ) { + exists(Node n1, Node n2 | + node1.asNodeOrImplicitRead() = n1 and + node2.asNode() = n2 and + Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2, + model) and + getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and + stateStepFilter(node1, s1, node2, s2) and + not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) + } + + pragma[nomagic] + private predicate readSetEx(NodeEx node1, ContentSet c, NodeEx node2) { + readEx(node1, c, node2) and + stepFilter(node1, node2) + or + exists(Node n | + node2.isImplicitReadNode(n) and + Config::allowImplicitRead(n, c) + | + node1.asNode() = n and + not fullBarrier(node1) + or + node1.isImplicitReadNode(n) + ) + } + + // inline to reduce fan-out via `getAReadContent` + bindingset[c] + private predicate read(NodeEx node1, Content c, NodeEx node2) { + exists(ContentSet cs | + readSetEx(node1, cs, node2) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) + } + + pragma[nomagic] + private predicate storeUnrestricted( + NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, DataFlowType containerType + ) { + storeEx(node1, c, node2, contentType, containerType) and + stepFilter(node1, node2) + } + + pragma[nomagic] + private predicate hasReadStep(Content c) { read(_, c, _) } + + pragma[nomagic] + private predicate store( + NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, DataFlowType containerType + ) { + storeUnrestricted(node1, c, node2, contentType, containerType) and + hasReadStep(c) + } + + /** + * Holds if field flow should be used for the given configuration. + */ + private predicate useFieldFlow() { + Config::fieldFlowBranchLimit() >= 1 and Config::accessPathLimit() > 0 + } + + /** + * Holds if flow from `p` to a return node of kind `kind` is allowed. + * + * We don't expect a parameter to return stored in itself, unless + * explicitly allowed + */ + bindingset[p, kind] + private predicate parameterFlowThroughAllowedEx(ParamNodeEx p, ReturnKindExt kind) { + exists(ParameterPosition pos | p.isParameterOf(_, pos) | + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + allowParameterReturnInSelfEx(p) + ) + } + + private module Stage1 { + private import Stage1Common + + class Ap = Unit; + + class ApNil = Ap; + + private class Cc = boolean; + + /* Begin: Stage 1 logic. */ + /** + * Holds if `node` is reachable from a source. + * + * The Boolean `cc` records whether the node is reached through an + * argument in a call. + */ + private predicate fwdFlow(NodeEx node, Cc cc) { + sourceNode(node, _) and + if hasSourceCallCtx() then cc = true else cc = false + or + exists(NodeEx mid | fwdFlow(mid, cc) | + localFlowStepEx(mid, node, _) or + additionalLocalFlowStep(mid, node, _) or + additionalLocalStateStep(mid, _, node, _, _) + ) + or + exists(NodeEx mid | fwdFlow(mid, _) and cc = false | + jumpStepEx(mid, node) or + additionalJumpStep(mid, node, _) or + additionalJumpStateStep(mid, _, node, _, _) + ) + or + // store + exists(NodeEx mid | + useFieldFlow() and + fwdFlow(mid, cc) and + store(mid, _, node, _, _) + ) + or + // read + exists(ContentSet c | + fwdFlowReadSet(c, node, cc) and + fwdFlowConsCandSet(c, _) + ) + or + // flow into a callable + fwdFlowIn(_, _, _, node) and + cc = true + or + // flow out of a callable + fwdFlowOut(_, _, node, false) and + cc = false + or + // flow through a callable + exists(DataFlowCall call, ReturnKindExtOption kind, ReturnKindExtOption disallowReturnKind | + fwdFlowOutFromArg(call, kind, node) and + fwdFlowIsEntered(call, disallowReturnKind, cc) and + kind != disallowReturnKind + ) + } + + // inline to reduce the number of iterations + pragma[inline] + private predicate fwdFlowIn(DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p) { + // call context cannot help reduce virtual dispatch + fwdFlow(arg, cc) and + viableParamArgEx(call, p, arg) and + not fullBarrier(p) and + ( + cc = false + or + cc = true and + not CachedCallContextSensitivity::reducedViableImplInCallContext(call, _, _) + ) + or + // call context may help reduce virtual dispatch + exists(DataFlowCallable target | + fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target) and + target = viableImplInSomeFwdFlowCallContextExt(call) and + cc = true + ) + } + + pragma[nomagic] + private ReturnKindExtOption getDisallowedReturnKind0(ParamNodeEx p) { + if allowParameterReturnInSelfEx(p) + then result.isNone() + else p.isParameterOf(_, result.asSome().(ParamUpdateReturnKind).getPosition()) + } + + bindingset[p] + pragma[inline_late] + private ReturnKindExtOption getDisallowedReturnKind(ParamNodeEx p) { + result = getDisallowedReturnKind0(p) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, ReturnKindExtOption disallowReturnKind, Cc cc + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, _, cc, p) and + disallowReturnKind = getDisallowedReturnKind(p) + ) + } + + pragma[nomagic] + private predicate fwdFlowInReducedViableImplInSomeCallContext( + DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target + ) { + fwdFlow(arg, true) and + viableParamArgEx(call, p, arg) and + CachedCallContextSensitivity::reducedViableImplInCallContext(call, _, _) and + target = p.getEnclosingCallable() and + not fullBarrier(p) + } + + /** + * Gets a viable dispatch target of `call` in the context `ctx`. This is + * restricted to those `call`s for which a context might make a difference, + * and to `ctx`s that are reachable in `fwdFlow`. + */ + pragma[nomagic] + private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(DataFlowCall call) { + exists(DataFlowCall ctx | + fwdFlowIsEntered(ctx, _, _) and + result = viableImplInCallContextExt(call, ctx) + ) + } + + private predicate fwdFlow(NodeEx node) { fwdFlow(node, _) } + + pragma[nomagic] + private predicate fwdFlowReadSet(ContentSet c, NodeEx node, Cc cc) { + exists(NodeEx mid | + fwdFlow(mid, cc) and + readSetEx(mid, c, node) + ) + } + + /** + * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Content c) { + exists(NodeEx mid, NodeEx node | + not fullBarrier(node) and + useFieldFlow() and + fwdFlow(mid, _) and + store(mid, c, node, _, _) + ) + } + + /** + * Holds if `cs` may be interpreted in a read as the target of some store + * into `c`, in the flow covered by `fwdFlow`. + */ + pragma[nomagic] + private predicate fwdFlowConsCandSet(ContentSet cs, Content c) { + fwdFlowConsCand(c) and + c = cs.getAReadContent() + } + + pragma[nomagic] + private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc) { + exists(RetNodeEx ret | + fwdFlow(ret, cc) and + ret.getReturnPosition() = pos + ) + } + + // inline to reduce the number of iterations + pragma[inline] + private predicate fwdFlowOut(DataFlowCall call, ReturnKindExt kind, NodeEx out, Cc cc) { + exists(ReturnPosition pos | + fwdFlowReturnPosition(pos, cc) and + viableReturnPosOutEx(call, pos, out) and + not fullBarrier(out) and + kind = pos.getKind() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg(DataFlowCall call, ReturnKindExtOption kind, NodeEx out) { + fwdFlowOut(call, kind.asSome(), out, true) + } + + private predicate stateStepFwd(FlowState state1, FlowState state2) { + exists(NodeEx node1 | + additionalLocalStateStep(node1, state1, _, state2, _) or + additionalJumpStateStep(node1, state1, _, state2, _) + | + fwdFlow(node1) + ) + } + + private predicate fwdFlowState(FlowState state) { + sourceNode(_, state) + or + exists(FlowState state0 | + fwdFlowState(state0) and + stateStepFwd(state0, state) + ) + } + + predicate sinkNode(NodeEx node, FlowState state) { + fwdFlow(pragma[only_bind_into](node)) and + fwdFlowState(state) and + isRelevantSink(node.asNodeOrImplicitRead()) + or + fwdFlow(node) and + fwdFlowState(state) and + sinkNodeWithState(node, state) + } + + /** + * Holds if `node` is part of a path from a source to a sink. + * + * The Boolean `toReturn` records whether the node must be returned from + * the enclosing callable in order to reach a sink. + */ + pragma[nomagic] + predicate revFlow(NodeEx node, boolean toReturn) { + revFlow0(node, toReturn) and + fwdFlow(node) + } + + pragma[nomagic] + private predicate revFlow0(NodeEx node, boolean toReturn) { + sinkNode(node, _) and + if hasSinkCallCtx() then toReturn = true else toReturn = false + or + exists(NodeEx mid | revFlow(mid, toReturn) | + localFlowStepEx(node, mid, _) or + additionalLocalFlowStep(node, mid, _) or + additionalLocalStateStep(node, _, mid, _, _) + ) + or + exists(NodeEx mid | revFlow(mid, _) and toReturn = false | + jumpStepEx(node, mid) or + additionalJumpStep(node, mid, _) or + additionalJumpStateStep(node, _, mid, _, _) + ) + or + // store + exists(Content c | + revFlowStore(c, node, toReturn) and + revFlowConsCand(c) + ) + or + // read + exists(NodeEx mid, ContentSet c | + readSetEx(node, c, mid) and + fwdFlowConsCandSet(c, _) and + revFlow(mid, toReturn) + ) + or + // flow into a callable + revFlowIn(_, _, node, false) and + toReturn = false + or + // flow out of a callable + exists(ReturnPosition pos | + revFlowOut(pos) and + node.(RetNodeEx).getReturnPosition() = pos and + toReturn = true + ) + or + // flow through a callable + exists(DataFlowCall call, ReturnKindExtOption kind, ReturnKindExtOption disallowReturnKind | + revFlowIsReturned(call, kind, toReturn) and + revFlowInToReturn(call, disallowReturnKind, node) and + kind != disallowReturnKind + ) + } + + /** + * Holds if `c` is the target of a read in the flow covered by `revFlow`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Content c) { + exists(NodeEx mid, NodeEx node, ContentSet cs | + fwdFlow(node) and + readSetEx(node, cs, mid) and + fwdFlowConsCandSet(cs, c) and + revFlow(pragma[only_bind_into](mid), _) + ) + } + + pragma[nomagic] + private predicate revFlowStore(Content c, NodeEx node, boolean toReturn) { + exists(NodeEx mid | + revFlow(mid, toReturn) and + fwdFlowConsCand(c) and + store(node, c, mid, _, _) + ) + } + + /** + * Holds if `c` is the target of both a read and a store in the flow covered + * by `revFlow`. + */ + pragma[nomagic] + predicate revFlowIsReadAndStored(Content c) { + revFlowConsCand(c) and + revFlowStore(c, _, _) + } + + pragma[nomagic] + predicate viableReturnPosOutNodeCandFwd1(DataFlowCall call, ReturnPosition pos, NodeEx out) { + fwdFlowReturnPosition(pos, _) and + viableReturnPosOutEx(call, pos, out) + } + + pragma[nomagic] + private predicate revFlowOut(ReturnPosition pos) { + exists(NodeEx out | + revFlow(out, _) and + viableReturnPosOutNodeCandFwd1(_, pos, out) + ) + } + + pragma[nomagic] + predicate viableParamArgNodeCandFwd1(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { + fwdFlowIn(call, arg, _, p) + } + + // inline to reduce the number of iterations + pragma[inline] + private predicate revFlowIn(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, boolean toReturn) { + revFlow(p, toReturn) and + viableParamArgNodeCandFwd1(call, p, arg) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ReturnKindExtOption disallowReturnKind, ArgNodeEx arg + ) { + exists(ParamNodeEx p | + revFlowIn(call, p, arg, true) and + disallowReturnKind = getDisallowedReturnKind(p) + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, ReturnKindExtOption kind, boolean toReturn + ) { + exists(NodeEx out | + revFlow(out, toReturn) and + fwdFlowOutFromArg(call, kind, out) + ) + } + + private predicate stateStepRev(FlowState state1, FlowState state2) { + exists(NodeEx node1, NodeEx node2 | + additionalLocalStateStep(node1, state1, node2, state2, _) or + additionalJumpStateStep(node1, state1, node2, state2, _) + | + revFlow(node1, _) and + revFlow(node2, _) and + fwdFlowState(state1) and + fwdFlowState(state2) + ) + } + + pragma[nomagic] + predicate revFlowState(FlowState state) { + exists(NodeEx node | + sinkNode(node, state) and + revFlow(node, _) and + fwdFlowState(state) + ) + or + exists(FlowState state0 | + revFlowState(state0) and + stateStepRev(state, state0) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, DataFlowType containerType + ) { + revFlowIsReadAndStored(c) and + revFlow(node2) and + store(node1, c, node2, contentType, containerType) + } + + pragma[nomagic] + predicate readStepCand(NodeEx n1, Content c, NodeEx n2) { + revFlowIsReadAndStored(c) and + read(n1, c, n2) and + revFlow(n2) + } + + pragma[nomagic] + predicate revFlow(NodeEx node) { revFlow(node, _) } + + bindingset[node, state] + predicate revFlow(NodeEx node, FlowState state, Ap ap) { + revFlow(node, _) and + exists(state) and + exists(ap) + } + + private predicate throughFlowNodeCand(NodeEx node) { + revFlow(node, true) and + fwdFlow(node, true) and + not inBarrier(node) and + not outBarrier(node) + } + + /** Holds if flow may return from `callable`. */ + pragma[nomagic] + private predicate returnFlowCallableNodeCand(DataFlowCallable callable, ReturnKindExt kind) { + exists(RetNodeEx ret | + throughFlowNodeCand(ret) and + callable = ret.getEnclosingCallable() and + kind = ret.getKind() + ) + } + + /** + * Holds if flow may enter through `p` and reach a return node making `p` a + * candidate for the origin of a summary. + */ + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, boolean emptyAp) { + exists(DataFlowCallable c, ReturnKindExt kind | + throughFlowNodeCand(p) and + returnFlowCallableNodeCand(c, kind) and + p.getEnclosingCallable() = c and + emptyAp = [true, false] and + parameterFlowThroughAllowedEx(p, kind) + ) + } + + pragma[nomagic] + predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind) { + throughFlowNodeCand(ret) and + kind = ret.getKind() + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call) { + exists( + ArgNodeEx arg, ReturnKindExtOption kind, ReturnKindExtOption disallowReturnKind, + boolean toReturn + | + revFlow(arg, pragma[only_bind_into](toReturn)) and + revFlowIsReturned(call, kind, pragma[only_bind_into](toReturn)) and + revFlowInToReturn(call, disallowReturnKind, arg) and + kind != disallowReturnKind + ) + } + + predicate callEdgeArgParam( + DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, boolean emptyAp + ) { + exists(boolean allowsFieldFlow | + flowIntoCallNodeCand1(call, arg, p, allowsFieldFlow) and + c = p.getEnclosingCallable() and + ( + emptyAp = true + or + allowsFieldFlow = true and emptyAp = false + ) + ) + } + + predicate callEdgeReturn( + DataFlowCall call, DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + boolean allowsFieldFlow + ) { + flowOutOfCallNodeCand1(call, ret, kind, out, allowsFieldFlow) and + c = ret.getEnclosingCallable() + } + + predicate relevantCallEdgeIn(DataFlowCall call, DataFlowCallable c) { + callEdgeArgParam(call, c, _, _, _) + } + + predicate relevantCallEdgeOut(DataFlowCall call, DataFlowCallable c) { + callEdgeReturn(call, c, _, _, _, _) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, int calledges + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node)) and + fields = count(Content f0 | fwdFlowConsCand(f0)) and + conscand = -1 and + states = count(FlowState state | fwdFlowState(state)) and + tuples = count(NodeEx n, boolean b | fwdFlow(n, b)) and + calledges = -1 + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _)) and + fields = count(Content f0 | revFlowConsCand(f0)) and + conscand = -1 and + states = count(FlowState state | revFlowState(state)) and + tuples = count(NodeEx n, boolean b | revFlow(n, b)) and + calledges = + count(DataFlowCall call, DataFlowCallable c | + callEdgeArgParam(call, c, _, _, _) or + callEdgeReturn(call, c, _, _, _, _) + ) + } + /* End: Stage 1 logic. */ + } + + private module Stage1Common { + predicate isRelevantSourceSinkPair = SourceSinkFiltering::isRelevantSourceSinkPair/2; + + predicate hasSourceCallCtx() { + exists(FlowFeature feature | feature = Config::getAFeature() | + feature instanceof FeatureHasSourceCallContext or + feature instanceof FeatureEqualSourceSinkCallContext + ) + } + + predicate hasSinkCallCtx() { + exists(FlowFeature feature | feature = Config::getAFeature() | + feature instanceof FeatureHasSinkCallContext or + feature instanceof FeatureEqualSourceSinkCallContext + ) + } + } + + pragma[nomagic] + private predicate localStepNodeCand1( + NodeEx node1, NodeEx node2, boolean preservesValue, DataFlowType t, LocalCallContext lcc, + string label + ) { + Stage1::revFlow(node1) and + Stage1::revFlow(node2) and + ( + preservesValue = true and + localFlowStepEx(node1, node2, label) and + t = node1.getDataFlowType() + or + preservesValue = false and + additionalLocalFlowStep(node1, node2, label) and + t = node2.getDataFlowType() + ) and + lcc.relevantFor(node1.getEnclosingCallable()) and + not isUnreachableInCall1(node1, lcc) and + not isUnreachableInCall1(node2, lcc) + } + + pragma[nomagic] + private predicate localStateStepNodeCand1( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, DataFlowType t, + LocalCallContext lcc, string label + ) { + Stage1::revFlow(node1) and + Stage1::revFlow(node2) and + additionalLocalStateStep(node1, state1, node2, state2, label) and + t = node2.getDataFlowType() and + lcc.relevantFor(node1.getEnclosingCallable()) and + not isUnreachableInCall1(node1, lcc) and + not isUnreachableInCall1(node2, lcc) + } + + pragma[nomagic] + private predicate viableReturnPosOutNodeCand1(DataFlowCall call, ReturnPosition pos, NodeEx out) { + Stage1::revFlow(out) and + Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out) + } + + /** + * Holds if data can flow out of `call` from `ret` to `out`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. + */ + pragma[nomagic] + private predicate flowOutOfCallNodeCand1( + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out + ) { + exists(ReturnPosition pos | + viableReturnPosOutNodeCand1(call, pos, out) and + pos = ret.getReturnPosition() and + kind = pos.getKind() and + Stage1::revFlow(ret) and + not outBarrier(ret) and + not inBarrier(out) + ) + } + + pragma[nomagic] + private predicate viableParamArgNodeCand1(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { + Stage1::viableParamArgNodeCandFwd1(call, p, arg) and + Stage1::revFlow(arg) + } + + /** + * Holds if data can flow into `call` and that this step is part of a + * path from a source to a sink. + */ + pragma[nomagic] + private predicate flowIntoCallNodeCand1(DataFlowCall call, ArgNodeEx arg, ParamNodeEx p) { + viableParamArgNodeCand1(call, p, arg) and + Stage1::revFlow(p) and + not outBarrier(arg) and + not inBarrier(p) + } + + /** + * Gets an additional term that is added to `branch` and `join` when deciding whether + * the amount of forward or backward branching is within the limit specified by the + * configuration. + */ + pragma[nomagic] + private int getLanguageSpecificFlowIntoCallNodeCand1(ArgNodeEx arg, ParamNodeEx p) { + flowIntoCallNodeCand1(_, arg, p) and + result = getAdditionalFlowIntoCallNodeTerm(arg.projectToNode(), p.projectToNode()) + } + + pragma[nomagic] + private predicate returnCallEdge1( + DataFlowCallable c, SndLevelScopeOption scope, DataFlowCall call, NodeEx out + ) { + exists(RetNodeEx ret | + flowOutOfCallNodeCand1(call, ret, _, out) and + c = ret.getEnclosingCallable() + | + scope = getSecondLevelScopeEx(ret) + or + ret = TParamReturnNode(_, scope) + ) + } + + private int simpleDispatchFanoutOnReturn(DataFlowCall call, NodeEx out) { + result = + strictcount(DataFlowCallable c, SndLevelScopeOption scope | + returnCallEdge1(c, scope, call, out) + ) + } + + pragma[nomagic] + private predicate returnCallEdgeInCtx1( + DataFlowCallable c, SndLevelScopeOption scope, DataFlowCall call, NodeEx out, DataFlowCall ctx + ) { + returnCallEdge1(c, scope, call, out) and + c = viableImplInCallContextExt(call, ctx) + } + + private int ctxDispatchFanoutOnReturn(NodeEx out, DataFlowCall ctx) { + exists(DataFlowCall call, DataFlowCallable c | + simpleDispatchFanoutOnReturn(call, out) > 1 and + not Stage1::revFlow(out, false) and + call.getEnclosingCallable() = c and + returnCallEdge1(c, _, ctx, _) and + mayBenefitFromCallContextExt(call, _) and + result = + count(DataFlowCallable tgt, SndLevelScopeOption scope | + returnCallEdgeInCtx1(tgt, scope, call, out, ctx) + ) + ) + } + + private int ctxDispatchFanoutOnReturn(NodeEx out) { + result = max(DataFlowCall ctx | | ctxDispatchFanoutOnReturn(out, ctx)) + } + + private int dispatchFanoutOnReturn(NodeEx out) { + result = ctxDispatchFanoutOnReturn(out) + or + not exists(ctxDispatchFanoutOnReturn(out)) and + result = simpleDispatchFanoutOnReturn(_, out) + } + + /** + * Gets the amount of forward branching on the origin of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ + pragma[nomagic] + private int branch(ArgNodeEx n1) { + result = + strictcount(DataFlowCallable c | + exists(NodeEx n | + flowIntoCallNodeCand1(_, n1, n) and + c = n.getEnclosingCallable() + ) + ) + sum(ParamNodeEx p1 | | getLanguageSpecificFlowIntoCallNodeCand1(n1, p1)) + } + + /** + * Gets the amount of backward branching on the target of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ + pragma[nomagic] + private int join(ParamNodeEx n2) { + result = + strictcount(DataFlowCallable c | + exists(NodeEx n | + flowIntoCallNodeCand1(_, n, n2) and + c = n.getEnclosingCallable() + ) + ) + sum(ArgNodeEx arg2 | | getLanguageSpecificFlowIntoCallNodeCand1(arg2, n2)) + } + + /** + * Holds if data can flow out of `call` from `ret` to `out`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. The + * `allowsFieldFlow` flag indicates whether the branching is within the limit + * specified by the configuration. + */ + pragma[nomagic] + private predicate flowOutOfCallNodeCand1( + DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow + ) { + flowOutOfCallNodeCand1(call, ret, kind, out) and + exists(int j | + j = dispatchFanoutOnReturn(out) and + j > 0 and + if + j <= Config::fieldFlowBranchLimit() or + ignoreFieldFlowBranchLimit(ret.getEnclosingCallable()) + then allowsFieldFlow = true + else allowsFieldFlow = false + ) + } + + /** + * Holds if data can flow into `call` and that this step is part of a + * path from a source to a sink. The `allowsFieldFlow` flag indicates whether + * the branching is within the limit specified by the configuration. + */ + pragma[nomagic] + private predicate flowIntoCallNodeCand1( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow + ) { + flowIntoCallNodeCand1(call, arg, p) and + exists(int b, int j | + b = branch(arg) and + j = join(p) and + if + b.minimum(j) <= Config::fieldFlowBranchLimit() or + ignoreFieldFlowBranchLimit(p.getEnclosingCallable()) + then allowsFieldFlow = true + else allowsFieldFlow = false + ) + } + + private predicate inBarrierAlias = inBarrier/2; + + private predicate outBarrierAlias = outBarrier/2; + + private predicate stateBarrierAlias = stateBarrier/2; + + private predicate sourceNodeAlias = sourceNode/2; + + private predicate jumpStepExAlias = jumpStepEx/2; + + private predicate additionalJumpStepAlias = additionalJumpStep/3; + + private predicate additionalJumpStateStepAlias = additionalJumpStateStep/5; + + private predicate localStepNodeCand1Alias = localStepNodeCand1/6; + + private predicate localStateStepNodeCand1Alias = localStateStepNodeCand1/7; + + module Stage1NoState implements Stage1Output { + predicate isRelevantSink(Node sink, FlowState state) { + SourceSinkFiltering::isRelevantSink(sink, state) + } + + predicate isRelevantSink(Node sink) { SourceSinkFiltering::isRelevantSink(sink) } + + predicate inBarrier = inBarrierAlias/2; + + predicate outBarrier = outBarrierAlias/2; + + predicate stateBarrier = stateBarrierAlias/2; + + // inline to reduce fan-out via `getAReadContent` + bindingset[c] + predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentSet(n, cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) + } + + pragma[nomagic] + predicate notExpectsContent(NodeEx n) { not expectsContentSet(n, _) } + + predicate parameterFlowThroughAllowed = parameterFlowThroughAllowedEx/2; + + import Stage1 + import Stage1Common + + predicate sourceNode = sourceNodeAlias/2; + + predicate jumpStepEx = jumpStepExAlias/2; + + predicate additionalJumpStep = additionalJumpStepAlias/3; + + predicate additionalJumpStateStep = additionalJumpStateStepAlias/5; + + predicate localStepNodeCand1 = localStepNodeCand1Alias/6; + + predicate localStateStepNodeCand1 = localStateStepNodeCand1Alias/7; + } + + private signature predicate flag(); + + private predicate flagEnable() { any() } + + private predicate flagDisable() { none() } + + module PartialFlow { + module FlowExplorationFwd { + private import FlowExploration as F + import F::Public + + predicate partialFlow = F::partialFlowFwd/3; + } + + module FlowExplorationRev { + private import FlowExploration as F + import F::Public + + predicate partialFlow = F::partialFlowRev/3; + } + } + + private module FlowExploration< + explorationLimitSig/0 explorationLimit, flag/0 flagFwd, flag/0 flagRev> + { + class CallContext = CachedCallContextSensitivity::Cc; + + class CallContextCall = CachedCallContextSensitivity::CcCall; + + class CallContextNoCall = CachedCallContextSensitivity::CcNoCall; + + predicate callContextNone = CachedCallContextSensitivity::ccNone/0; + + predicate callContextSomeCall = CachedCallContextSensitivity::ccSomeCall/0; + + private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2) { + exists(NodeEx node1, NodeEx node2 | + jumpStepEx(node1, node2) + or + additionalJumpStep(node1, node2, _) + or + additionalJumpStateStep(node1, _, node2, _, _) + or + // flow into callable + viableParamArgEx(_, node2, node1) + or + // flow out of a callable + viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2) + | + c1 = node1.getEnclosingCallable() and + c2 = node2.getEnclosingCallable() and + c1 != c2 + ) + } + + private predicate interestingCallableSrc(DataFlowCallable c) { + exists(Node n | isRelevantSource(n, _) and c = getNodeEnclosingCallable(n)) + or + exists(DataFlowCallable mid | interestingCallableSrc(mid) and callableStep(mid, c)) + } + + private predicate interestingCallableSink(DataFlowCallable c) { + exists(Node n | c = getNodeEnclosingCallable(n) | + isRelevantSink(n, _) or + isRelevantSink(n) + ) + or + exists(DataFlowCallable mid | interestingCallableSink(mid) and callableStep(c, mid)) + } + + private newtype TCallableExt = + TCallable(DataFlowCallable c) { + interestingCallableSrc(c) or + interestingCallableSink(c) + } or + TCallableSrc() or + TCallableSink() + + private predicate callableExtSrc(TCallableSrc src) { any() } + + private predicate callableExtSink(TCallableSink sink) { any() } + + private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) { + exists(DataFlowCallable c1, DataFlowCallable c2 | + callableStep(c1, c2) and + ce1 = TCallable(c1) and + ce2 = TCallable(c2) + ) + or + exists(Node n | + ce1 = TCallableSrc() and + isRelevantSource(n, _) and + ce2 = TCallable(getNodeEnclosingCallable(n)) + ) + or + exists(Node n | + ce2 = TCallableSink() and + ce1 = TCallable(getNodeEnclosingCallable(n)) + | + isRelevantSink(n, _) or + isRelevantSink(n) + ) + } + + private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) { + callableExtStepFwd(ce2, ce1) + } + + private int distSrcExt(TCallableExt c) = + shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result) + + private int distSinkExt(TCallableExt c) = + shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result) + + private int distSrc(DataFlowCallable c) { result = distSrcExt(TCallable(c)) - 1 } + + private int distSink(DataFlowCallable c) { result = distSinkExt(TCallable(c)) - 1 } + + private newtype TPartialAccessPath = + TPartialNil() or + TPartialCons(Content c, int len) { len in [1 .. Config::accessPathLimit()] } + + /** + * Conceptually a list of `Content`s, but only the first + * element of the list and its length are tracked. + */ + private class PartialAccessPath extends TPartialAccessPath { + abstract string toString(); + + Content getHead() { this = TPartialCons(result, _) } + + int len() { + this = TPartialNil() and result = 0 + or + this = TPartialCons(_, result) + } + } + + private class PartialAccessPathNil extends PartialAccessPath, TPartialNil { + override string toString() { result = "" } + } + + private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { + override string toString() { + exists(Content c, int len | this = TPartialCons(c, len) | + if len = 1 + then result = "[" + c.toString() + "]" + else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" + ) + } + } + + private predicate relevantState(FlowState state) { + sourceNode(_, state) or + sinkNodeWithState(_, state) or + additionalLocalStateStep(_, state, _, _, _) or + additionalLocalStateStep(_, _, _, state, _) or + additionalJumpStateStep(_, state, _, _, _) or + additionalJumpStateStep(_, _, _, state, _) + } + + private predicate revSinkNode(NodeEx node, FlowState state) { + sinkNodeWithState(node, state) + or + isRelevantSink(node.asNodeOrImplicitRead()) and + relevantState(state) and + not fullBarrier(node) and + not stateBarrier(node, state) + } + + private newtype TSummaryCtx1 = + TSummaryCtx1None() or + TSummaryCtx1Param(ParamNodeEx p) + + private newtype TSummaryCtx2 = + TSummaryCtx2None() or + TSummaryCtx2Some(FlowState s) { relevantState(s) } + + private newtype TSummaryCtx3 = + TSummaryCtx3None() or + TSummaryCtx3Some(DataFlowType t) + + private newtype TSummaryCtx4 = + TSummaryCtx4None() or + TSummaryCtx4Some(PartialAccessPath ap) + + private newtype TRevSummaryCtx1 = + TRevSummaryCtx1None() or + TRevSummaryCtx1Some(ReturnPosition pos) + + private newtype TRevSummaryCtx2 = + TRevSummaryCtx2None() or + TRevSummaryCtx2Some(FlowState s) { relevantState(s) } + + private newtype TRevSummaryCtx3 = + TRevSummaryCtx3None() or + TRevSummaryCtx3Some(PartialAccessPath ap) + + private newtype TPartialPathNode = + TPartialPathNodeFwd( + NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, + TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap + ) { + flagFwd() and + sourceNode(node, state) and + cc = callContextNone() and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + sc4 = TSummaryCtx4None() and + t = node.getDataFlowType() and + ap = TPartialNil() and + exists(explorationLimit()) + or + partialPathStep(_, node, state, cc, sc1, sc2, sc3, sc4, t, ap) and + distSrc(node.getEnclosingCallable()) <= explorationLimit() + } or + TPartialPathNodeRev( + NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, + TRevSummaryCtx3 sc3, PartialAccessPath ap + ) { + flagRev() and + revSinkNode(node, state) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = TPartialNil() and + exists(explorationLimit()) + or + revPartialPathStep(_, node, state, sc1, sc2, sc3, ap) and + distSink(node.getEnclosingCallable()) <= explorationLimit() + } + + // inline to reduce fan-out via `getAReadContent` + bindingset[c] + private predicate clearsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + clearsContentSet(n, cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) + } + + pragma[nomagic] + private predicate partialPathStep( + PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, + TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap + ) { + partialPathStep1(mid, node, state, cc, sc1, sc2, sc3, sc4, _, t, ap) + } + + bindingset[node, t0] + private predicate strengthenType(NodeEx node, DataFlowType t0, DataFlowType t) { + if node instanceof CastingNodeEx + then + exists(DataFlowType nt | nt = node.getDataFlowType() | + if typeStrongerThanFilter(nt, t0) + then t = nt + else ( + compatibleTypesFilter(nt, t0) and t = t0 + ) + ) + else t = t0 + } + + pragma[nomagic] + private predicate partialPathStep1( + PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, + TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t0, DataFlowType t, + PartialAccessPath ap + ) { + exists(boolean isStoreStep | + partialPathStep0(mid, node, state, cc, sc1, sc2, sc3, sc4, t0, ap, isStoreStep) and + not fullBarrier(node) and + not stateBarrier(node, state) and + not inBarrier(node, state) and + ( + Stage1NoState::notExpectsContent(node) or + Stage1NoState::expectsContentEx(node, ap.getHead()) + ) and + strengthenType(node, t0, t) + | + isStoreStep = true or + not clearsContentEx(node, ap.getHead()) + ) + } + + pragma[nomagic] + private predicate partialPathTypeStrengthen( + DataFlowType t0, PartialAccessPath ap, DataFlowType t + ) { + partialPathStep1(_, _, _, _, _, _, _, _, t0, t, ap) and t0 != t + } + + module Public { + /** + * A `Node` augmented with a call context, an access path, and a configuration. + */ + class PartialPathNode extends TPartialPathNode { + /** Gets a textual representation of this element. */ + string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } + + /** + * Gets a textual representation of this element, including a textual + * representation of the call context. + */ + string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + } + + /** Gets the location of this node. */ + Location getLocation() { result = this.getNodeEx().getLocation() } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + pragma[inline] + deprecated predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + + /** Gets the underlying `Node`. */ + final Node getNode() { this.getNodeEx().projectToNode() = result } + + FlowState getState() { none() } + + private NodeEx getNodeEx() { + result = this.(PartialPathNodeFwd).getNodeEx() or + result = this.(PartialPathNodeRev).getNodeEx() + } + + /** Gets a successor of this node, if any. */ + PartialPathNode getASuccessor() { none() } + + /** + * Gets the approximate distance to the nearest source measured in number + * of interprocedural steps. + */ + int getSourceDistance() { result = distSrc(this.getNodeEx().getEnclosingCallable()) } + + /** + * Gets the approximate distance to the nearest sink measured in number + * of interprocedural steps. + */ + int getSinkDistance() { result = distSink(this.getNodeEx().getEnclosingCallable()) } + + private string ppType() { + this instanceof PartialPathNodeRev and result = "" + or + exists(string t | t = this.(PartialPathNodeFwd).getType().toString() | + if t = "" then result = "" else result = " : " + t + ) + } + + private string ppAp() { + exists(string s | + s = this.(PartialPathNodeFwd).getAp().toString() or + s = this.(PartialPathNodeRev).getAp().toString() + | + if s = "" then result = "" else result = " " + s + ) + } + + private string ppCtx() { + result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">" + } + + /** Holds if this is a source in a forward-flow path. */ + predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() } + + /** Holds if this is a sink in a reverse-flow path. */ + predicate isRevSink() { this.(PartialPathNodeRev).isSink() } + } + + /** + * Provides the query predicates needed to include a graph in a path-problem query. + */ + module PartialPathGraph { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b } + } + } + + import Public + + private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd { + NodeEx node; + FlowState state; + CallContext cc; + TSummaryCtx1 sc1; + TSummaryCtx2 sc2; + TSummaryCtx3 sc3; + TSummaryCtx4 sc4; + DataFlowType t; + PartialAccessPath ap; + + PartialPathNodeFwd() { + this = TPartialPathNodeFwd(node, state, cc, sc1, sc2, sc3, sc4, t, ap) + } + + NodeEx getNodeEx() { result = node } + + override FlowState getState() { result = state } + + CallContext getCallContext() { result = cc } + + TSummaryCtx1 getSummaryCtx1() { result = sc1 } + + TSummaryCtx2 getSummaryCtx2() { result = sc2 } + + TSummaryCtx3 getSummaryCtx3() { result = sc3 } + + TSummaryCtx4 getSummaryCtx4() { result = sc4 } + + DataFlowType getType() { result = t } + + PartialAccessPath getAp() { result = ap } + + override PartialPathNodeFwd getASuccessor() { + not outBarrier(node, state) and + partialPathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), + result.getSummaryCtx1(), result.getSummaryCtx2(), result.getSummaryCtx3(), + result.getSummaryCtx4(), result.getType(), result.getAp()) + } + + predicate isSource() { + sourceNode(node, state) and + cc = callContextNone() and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + sc4 = TSummaryCtx4None() and + ap instanceof TPartialNil + } + } + + private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev { + NodeEx node; + FlowState state; + TRevSummaryCtx1 sc1; + TRevSummaryCtx2 sc2; + TRevSummaryCtx3 sc3; + PartialAccessPath ap; + + PartialPathNodeRev() { this = TPartialPathNodeRev(node, state, sc1, sc2, sc3, ap) } + + NodeEx getNodeEx() { result = node } + + override FlowState getState() { result = state } + + TRevSummaryCtx1 getSummaryCtx1() { result = sc1 } + + TRevSummaryCtx2 getSummaryCtx2() { result = sc2 } + + TRevSummaryCtx3 getSummaryCtx3() { result = sc3 } + + PartialAccessPath getAp() { result = ap } + + override PartialPathNodeRev getASuccessor() { + not inBarrier(node, state) and + revPartialPathStep(result, this.getNodeEx(), this.getState(), this.getSummaryCtx1(), + this.getSummaryCtx2(), this.getSummaryCtx3(), this.getAp()) + } + + predicate isSink() { + revSinkNode(node, state) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = TPartialNil() + } + } + + pragma[nomagic] + private predicate partialPathStep0( + PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, + TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap, + boolean isStoreStep + ) { + not isUnreachableInCall1(node, + CachedCallContextSensitivity::LocalCallContext::getLocalCc(cc)) and + ( + localFlowStepEx(mid.getNodeEx(), node, _) and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + sc4 = mid.getSummaryCtx4() and + t = mid.getType() and + ap = mid.getAp() + or + additionalLocalFlowStep(mid.getNodeEx(), node, _) and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + sc4 = mid.getSummaryCtx4() and + mid.getAp() instanceof PartialAccessPathNil and + t = node.getDataFlowType() and + ap = TPartialNil() + or + additionalLocalStateStep(mid.getNodeEx(), mid.getState(), node, state, _) and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + sc4 = mid.getSummaryCtx4() and + mid.getAp() instanceof PartialAccessPathNil and + t = node.getDataFlowType() and + ap = TPartialNil() + ) and + isStoreStep = false + or + jumpStepEx(mid.getNodeEx(), node) and + state = mid.getState() and + cc = callContextNone() and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + sc4 = TSummaryCtx4None() and + t = mid.getType() and + ap = mid.getAp() and + isStoreStep = false + or + additionalJumpStep(mid.getNodeEx(), node, _) and + state = mid.getState() and + cc = callContextNone() and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + sc4 = TSummaryCtx4None() and + mid.getAp() instanceof PartialAccessPathNil and + t = node.getDataFlowType() and + ap = TPartialNil() and + isStoreStep = false + or + additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state, _) and + cc = callContextNone() and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + sc4 = TSummaryCtx4None() and + mid.getAp() instanceof PartialAccessPathNil and + t = node.getDataFlowType() and + ap = TPartialNil() and + isStoreStep = false + or + partialPathStoreStep(mid, _, _, _, node, t, ap) and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + sc4 = mid.getSummaryCtx4() and + isStoreStep = true + or + exists(DataFlowType t0, PartialAccessPath ap0, Content c | + partialPathReadStep(mid, t0, ap0, c, node, cc) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + sc4 = mid.getSummaryCtx4() and + apConsFwd(t, ap, c, t0, ap0) + ) and + isStoreStep = false + or + partialPathIntoCallable(mid, node, state, _, cc, sc1, sc2, sc3, sc4, _, t, ap) and + isStoreStep = false + or + partialPathOutOfCallable(mid, node, state, cc, t, ap) and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + sc4 = TSummaryCtx4None() and + isStoreStep = false + or + partialPathThroughCallable(mid, node, state, cc, t, ap) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + sc4 = mid.getSummaryCtx4() and + isStoreStep = false + } + + bindingset[result, i] + private int unbindInt(int i) { pragma[only_bind_out](i) = pragma[only_bind_out](result) } + + pragma[inline] + private predicate partialPathStoreStep( + PartialPathNodeFwd mid, DataFlowType t1, PartialAccessPath ap1, Content c, NodeEx node, + DataFlowType t2, PartialAccessPath ap2 + ) { + exists(NodeEx midNode, DataFlowType contentType | + midNode = mid.getNodeEx() and + t1 = mid.getType() and + ap1 = mid.getAp() and + storeUnrestricted(midNode, c, node, contentType, t2) and + ap2.getHead() = c and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypesFilter(t1, contentType) + ) + } + + pragma[nomagic] + private predicate apConsFwd( + DataFlowType t1, PartialAccessPath ap1, Content c, DataFlowType t2, PartialAccessPath ap2 + ) { + partialPathStoreStep(_, t1, ap1, c, _, t2, ap2) + or + exists(DataFlowType t0 | + partialPathTypeStrengthen(t0, ap2, t2) and + apConsFwd(t1, ap1, c, t0, ap2) + ) + } + + pragma[nomagic] + private predicate partialPathReadStep( + PartialPathNodeFwd mid, DataFlowType t, PartialAccessPath ap, Content c, NodeEx node, + CallContext cc + ) { + exists(NodeEx midNode | + midNode = mid.getNodeEx() and + t = mid.getType() and + ap = mid.getAp() and + read(midNode, c, node) and + ap.getHead() = c and + cc = mid.getCallContext() + ) + } + + private predicate partialPathOutOfCallable0( + PartialPathNodeFwd mid, ReturnPosition pos, FlowState state, CallContext innercc, + DataFlowType t, PartialAccessPath ap + ) { + pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and + state = mid.getState() and + innercc = mid.getCallContext() and + innercc instanceof CallContextNoCall and + t = mid.getType() and + ap = mid.getAp() + } + + pragma[nomagic] + private predicate partialPathOutOfCallable1( + PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, FlowState state, + CallContext cc, DataFlowType t, PartialAccessPath ap + ) { + exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | + partialPathOutOfCallable0(mid, pos, state, innercc, t, ap) and + c = pos.getCallable() and + kind = pos.getKind() and + CachedCallContextSensitivity::resolveReturn(innercc, c, call) and + cc = CachedCallContextSensitivity::getCallContextReturn(c, call) + ) + } + + private predicate partialPathOutOfCallable( + PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, + PartialAccessPath ap + ) { + exists(ReturnKindExt kind, DataFlowCall call | + partialPathOutOfCallable1(mid, call, kind, state, cc, t, ap) and + out = kind.getAnOutNodeEx(call) + ) + } + + pragma[noinline] + private predicate partialPathIntoArg( + PartialPathNodeFwd mid, ParameterPosition ppos, FlowState state, CallContext cc, + DataFlowCall call, DataFlowType t, PartialAccessPath ap + ) { + exists(ArgNode arg, ArgumentPosition apos | + arg = mid.getNodeEx().asNode() and + state = mid.getState() and + cc = mid.getCallContext() and + arg.argumentOf(call, apos) and + t = mid.getType() and + ap = mid.getAp() and + parameterMatch(ppos, apos) + ) + } + + pragma[nomagic] + private predicate partialPathIntoCallable0( + PartialPathNodeFwd mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, + CallContext outercc, DataFlowCall call, DataFlowType t, PartialAccessPath ap + ) { + partialPathIntoArg(mid, pos, state, outercc, call, t, ap) and + callable = CachedCallContextSensitivity::resolveCall(call, outercc) + } + + private predicate partialPathIntoCallable( + PartialPathNodeFwd mid, ParamNodeEx p, FlowState state, CallContext outercc, + CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, + TSummaryCtx4 sc4, DataFlowCall call, DataFlowType t, PartialAccessPath ap + ) { + exists(ParameterPosition pos, DataFlowCallable callable | + partialPathIntoCallable0(mid, callable, pos, state, outercc, call, t, ap) and + p.isParameterOf(callable, pos) and + sc1 = TSummaryCtx1Param(p) and + sc2 = TSummaryCtx2Some(state) and + sc3 = TSummaryCtx3Some(t) and + sc4 = TSummaryCtx4Some(ap) and + innercc = + CachedCallContextSensitivity::LocalCallContext::getCallContextCall(call, callable) + ) + } + + pragma[nomagic] + private predicate paramFlowsThroughInPartialPath( + ReturnKindExt kind, FlowState state, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, + TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap + ) { + exists(PartialPathNodeFwd mid, RetNodeEx ret | + mid.getNodeEx() = ret and + kind = ret.getKind() and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + sc4 = mid.getSummaryCtx4() and + t = mid.getType() and + ap = mid.getAp() + ) + } + + pragma[noinline] + private predicate partialPathThroughCallable0( + DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, FlowState state, + CallContext cc, DataFlowType t, PartialAccessPath ap + ) { + exists( + CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, + TSummaryCtx4 sc4 + | + partialPathIntoCallable(mid, _, _, cc, innercc, sc1, sc2, sc3, sc4, call, _, _) and + paramFlowsThroughInPartialPath(kind, state, innercc, sc1, sc2, sc3, sc4, t, ap) + ) + } + + private predicate partialPathThroughCallable( + PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, DataFlowType t, + PartialAccessPath ap + ) { + exists(DataFlowCall call, ReturnKindExt kind | + partialPathThroughCallable0(call, mid, kind, state, cc, t, ap) and + out = kind.getAnOutNodeEx(call) + ) + } + + pragma[nomagic] + private predicate revPartialPathStep( + PartialPathNodeRev mid, NodeEx node, FlowState state, TRevSummaryCtx1 sc1, + TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, PartialAccessPath ap + ) { + exists(boolean isStoreStep | + revPartialPathStep0(mid, node, state, sc1, sc2, sc3, ap, isStoreStep) and + ( + Stage1NoState::notExpectsContent(node) or + Stage1NoState::expectsContentEx(node, ap.getHead()) + ) and + not fullBarrier(node) and + not stateBarrier(node, state) and + not outBarrier(node, state) and + // if a node is not the target of a store, we can check `clearsContent` immediately + ( + storeUnrestricted(_, _, node, _, _) + or + not clearsContentEx(node, ap.getHead()) + ) + | + // if a node is the target of a store, we can only check `clearsContent` + // when we know whether we took the store step + isStoreStep = true + or + exists(NodeEx midNode, PartialAccessPath midAp | + midNode = mid.getNodeEx() and + midAp = mid.getAp() and + not clearsContentEx(midNode, midAp.getHead()) + ) + ) + } + + pragma[nomagic] + private predicate revPartialPathStep0( + PartialPathNodeRev mid, NodeEx node, FlowState state, TRevSummaryCtx1 sc1, + TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, PartialAccessPath ap, boolean isStoreStep + ) { + localFlowStepEx(node, mid.getNodeEx(), _) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + ap = mid.getAp() and + isStoreStep = false + or + additionalLocalFlowStep(node, mid.getNodeEx(), _) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil() and + isStoreStep = false + or + additionalLocalStateStep(node, state, mid.getNodeEx(), mid.getState(), _) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil() and + isStoreStep = false + or + jumpStepEx(node, mid.getNodeEx()) and + state = mid.getState() and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = mid.getAp() and + isStoreStep = false + or + additionalJumpStep(node, mid.getNodeEx(), _) and + state = mid.getState() and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil() and + isStoreStep = false + or + additionalJumpStateStep(node, state, mid.getNodeEx(), mid.getState(), _) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil() and + isStoreStep = false + or + revPartialPathReadStep(mid, _, _, node, ap) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + isStoreStep = false + or + exists(PartialAccessPath ap0, Content c | + revPartialPathStoreStep(mid, ap0, c, node) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + apConsRev(ap, c, ap0) and + isStoreStep = true + ) + or + exists(ParamNodeEx p | + mid.getNodeEx() = p and + viableParamArgEx(_, p, node) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = mid.getAp() and + isStoreStep = false + ) + or + exists(ReturnPosition pos | + revPartialPathIntoReturn(mid, pos, state, sc1, sc2, sc3, _, ap) and + pos = node.(RetNodeEx).getReturnPosition() and + isStoreStep = false + ) + or + revPartialPathThroughCallable(mid, node, state, ap) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + isStoreStep = false + } + + pragma[inline] + private predicate revPartialPathReadStep( + PartialPathNodeRev mid, PartialAccessPath ap1, Content c, NodeEx node, PartialAccessPath ap2 + ) { + exists(NodeEx midNode | + midNode = mid.getNodeEx() and + ap1 = mid.getAp() and + read(node, c, midNode) and + ap2.getHead() = c and + ap2.len() = unbindInt(ap1.len() + 1) + ) + } + + pragma[nomagic] + private predicate apConsRev(PartialAccessPath ap1, Content c, PartialAccessPath ap2) { + revPartialPathReadStep(_, ap1, c, _, ap2) + } + + pragma[nomagic] + private predicate revPartialPathStoreStep( + PartialPathNodeRev mid, PartialAccessPath ap, Content c, NodeEx node + ) { + exists(NodeEx midNode | + midNode = mid.getNodeEx() and + ap = mid.getAp() and + storeUnrestricted(node, c, midNode, _, _) and + ap.getHead() = c + ) + } + + pragma[nomagic] + private predicate revPartialPathIntoReturn( + PartialPathNodeRev mid, ReturnPosition pos, FlowState state, TRevSummaryCtx1Some sc1, + TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3, DataFlowCall call, PartialAccessPath ap + ) { + exists(NodeEx out | + mid.getNodeEx() = out and + mid.getState() = state and + viableReturnPosOutEx(call, pos, out) and + sc1 = TRevSummaryCtx1Some(pos) and + sc2 = TRevSummaryCtx2Some(state) and + sc3 = TRevSummaryCtx3Some(ap) and + ap = mid.getAp() + ) + } + + pragma[nomagic] + private predicate revPartialPathFlowsThrough( + ArgumentPosition apos, FlowState state, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, + TRevSummaryCtx3Some sc3, PartialAccessPath ap + ) { + exists(PartialPathNodeRev mid, ParamNodeEx p, ParameterPosition ppos | + mid.getNodeEx() = p and + mid.getState() = state and + p.getPosition() = ppos and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + ap = mid.getAp() and + parameterMatch(ppos, apos) + ) + } + + pragma[nomagic] + private predicate revPartialPathThroughCallable0( + DataFlowCall call, PartialPathNodeRev mid, ArgumentPosition pos, FlowState state, + PartialAccessPath ap + ) { + exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3 | + revPartialPathIntoReturn(mid, _, _, sc1, sc2, sc3, call, _) and + revPartialPathFlowsThrough(pos, state, sc1, sc2, sc3, ap) + ) + } + + pragma[nomagic] + private predicate revPartialPathThroughCallable( + PartialPathNodeRev mid, ArgNodeEx node, FlowState state, PartialAccessPath ap + ) { + exists(DataFlowCall call, ArgumentPosition pos | + revPartialPathThroughCallable0(call, mid, pos, state, ap) and + node.argumentOf(call, pos) + ) + } + + private predicate fwdPartialFlow(PartialPathNode source, PartialPathNode node) { + source.isFwdSource() and + node = source.getASuccessor+() + } + + private predicate revPartialFlow(PartialPathNode node, PartialPathNode sink) { + sink.isRevSink() and + node.getASuccessor+() = sink + } + + /** + * Holds if there is a partial data flow path from `source` to `node`. The + * approximate distance between `node` and the closest source is `dist` and + * is restricted to be less than or equal to `explorationLimit()`. This + * predicate completely disregards sink definitions. + * + * This predicate is intended for data-flow exploration and debugging and may + * perform poorly if the number of sources is too big and/or the exploration + * limit is set too high without using barriers. + * + * To use this in a `path-problem` query, import the module `PartialPathGraph`. + */ + predicate partialFlowFwd(PartialPathNode source, PartialPathNode node, int dist) { + fwdPartialFlow(source, node) and + dist = node.getSourceDistance() + } + + /** + * Holds if there is a partial data flow path from `node` to `sink`. The + * approximate distance between `node` and the closest sink is `dist` and + * is restricted to be less than or equal to `explorationLimit()`. This + * predicate completely disregards source definitions. + * + * This predicate is intended for data-flow exploration and debugging and may + * perform poorly if the number of sinks is too big and/or the exploration + * limit is set too high without using barriers. + * + * To use this in a `path-problem` query, import the module `PartialPathGraph`. + * + * Note that reverse flow has slightly lower precision than the corresponding + * forward flow, as reverse flow disregards type pruning among other features. + */ + predicate partialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) { + revPartialFlow(node, sink) and + dist = node.getSinkDistance() + } + } + } +} From 3cbf8e517fbf5123357e5c2beeaafa1bf14ff43d Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 29 Jan 2025 14:56:34 +0100 Subject: [PATCH 03/14] Dataflow: Remove superfluous constraint. --- shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll index 9cfc73ce3e56..6a2f7f2a0e0a 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll @@ -1623,10 +1623,7 @@ module MakeImpl Lang> { or node instanceof FlowCheckNode or - exists(FlowState s | - additionalLocalStateStep(_, s, node, state, _, _, _) and - s != state - ) + additionalLocalStateStep(_, _, node, state, _, _, _) ) } @@ -1661,8 +1658,7 @@ module MakeImpl Lang> { | additionalJumpStateStep(node, state, next, s, _) or - additionalLocalStateStep(node, state, next, s, _, _, _) and - s != state + additionalLocalStateStep(node, state, next, s, _, _, _) ) or node instanceof FlowCheckNode From d5759a7f33a26e4cef76ca2f77ea4703ae107c0d Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 29 Jan 2025 15:08:01 +0100 Subject: [PATCH 04/14] Dataflow: Move definition of toNormalSinkNode. --- .../codeql/dataflow/internal/DataFlowImpl.qll | 14 ++++------- .../dataflow/internal/DataFlowImplStage1.qll | 25 +++++++++++-------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll index 6a2f7f2a0e0a..3d76de1014e2 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll @@ -180,6 +180,8 @@ module MakeImpl Lang> { private predicate stateBarrier = Stage1::stateBarrier/2; + private predicate toNormalSinkNode = Stage1::toNormalSinkNode/1; + private predicate sourceNode = Stage1::sourceNode/2; private predicate sinkNode = Stage1::sinkNode/2; @@ -1770,7 +1772,7 @@ module MakeImpl Lang> { TPathNodeSink(NodeEx node, FlowState state) { exists(PathNodeMid sink | sink.isAtSink() and - node = sink.toNormalSinkNodeEx() and + node = sink.toNormalSinkNode() and state = sink.getState() ) } or @@ -1930,13 +1932,7 @@ module MakeImpl Lang> { /** If this node corresponds to a sink, gets the normal node for that sink. */ pragma[nomagic] - NodeEx toNormalSinkNodeEx() { - exists(Node n | - pragma[only_bind_out](node.asNodeOrImplicitRead()) = n and - (Stage1::isRelevantSink(n) or Stage1::isRelevantSink(n, _)) and - result.asNode() = n - ) - } + NodeEx toNormalSinkNode() { result = toNormalSinkNode(node) } override PathNodeImpl getASuccessorImpl(string label) { // an intermediate step to another intermediate node @@ -2031,7 +2027,7 @@ module MakeImpl Lang> { exists(string model | this.isAtSink() and sinkModel(node, model) and - result.getNodeEx() = this.toNormalSinkNodeEx() and + result.getNodeEx() = this.toNormalSinkNode() and result.getState() = state and if model != "" then label = "Sink:" + model else label = "" ) diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll index 86c8c2b25e19..b2142cfbe83b 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll @@ -24,16 +24,15 @@ module MakeImplStage1 Lang> { bindingset[source, sink] predicate isRelevantSourceSinkPair(Node source, Node sink); - predicate isRelevantSink(Node sink, FlowState state); - - predicate isRelevantSink(Node sink); - predicate inBarrier(NodeEx node, FlowState state); predicate outBarrier(NodeEx node, FlowState state); predicate stateBarrier(NodeEx node, FlowState state); + /** If `node` corresponds to a sink, gets the normal node for that sink. */ + NodeEx toNormalSinkNode(NodeEx node); + predicate sourceNode(NodeEx node, FlowState state); predicate sinkNode(NodeEx node, FlowState state); @@ -269,6 +268,16 @@ module MakeImplStage1 Lang> { not stateBarrier(node, state) } + /** If `node` corresponds to a sink, gets the normal node for that sink. */ + pragma[nomagic] + NodeEx toNormalSinkNodeEx(NodeEx node) { + exists(Node n | + pragma[only_bind_out](node.asNodeOrImplicitRead()) = n and + (isRelevantSink(n) or isRelevantSink(n, _)) and + result.asNode() = n + ) + } + /** Provides the relevant barriers for a step from `node1` to `node2`. */ bindingset[node1, node2] private predicate stepFilter(NodeEx node1, NodeEx node2) { @@ -1212,12 +1221,6 @@ module MakeImplStage1 Lang> { private predicate localStateStepNodeCand1Alias = localStateStepNodeCand1/7; module Stage1NoState implements Stage1Output { - predicate isRelevantSink(Node sink, FlowState state) { - SourceSinkFiltering::isRelevantSink(sink, state) - } - - predicate isRelevantSink(Node sink) { SourceSinkFiltering::isRelevantSink(sink) } - predicate inBarrier = inBarrierAlias/2; predicate outBarrier = outBarrierAlias/2; @@ -1241,6 +1244,8 @@ module MakeImplStage1 Lang> { import Stage1 import Stage1Common + predicate toNormalSinkNode = toNormalSinkNodeEx/1; + predicate sourceNode = sourceNodeAlias/2; predicate jumpStepEx = jumpStepExAlias/2; From 1799bf9d1424e63f82a7a72cf7aef22c54cadd4f Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 29 Jan 2025 15:39:55 +0100 Subject: [PATCH 05/14] Dataflow: Parameterise stages 2-6 over the node type. --- .../codeql/dataflow/internal/DataFlowImpl.qll | 459 +++++++++--------- .../dataflow/internal/DataFlowImplCommon.qll | 2 + .../dataflow/internal/DataFlowImplStage1.qll | 87 +++- 3 files changed, 292 insertions(+), 256 deletions(-) diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll index 3d76de1014e2..6bf5cb894f6f 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll @@ -174,6 +174,18 @@ module MakeImpl Lang> { module Impl Stage1> { private class FlowState = Config::FlowState; + private class Nd = Stage1::Nd; + + private class ArgNd = Stage1::ArgNd; + + private class ParamNd = Stage1::ParamNd; + + private class RetNd = Stage1::RetNd; + + private class OutNd = Stage1::OutNd; + + private class CastingNd = Stage1::CastingNd; + private predicate inBarrier = Stage1::inBarrier/2; private predicate outBarrier = Stage1::outBarrier/2; @@ -200,18 +212,16 @@ module MakeImpl Lang> { private predicate localStateStepNodeCand1 = Stage1::localStateStepNodeCand1/7; - private predicate sourceModel(NodeEx node, string model) { - sourceNode(node, _) and - ( + private predicate sourceModel(Nd n, string model) { + exists(NodeEx node | sourceNode(n, _) and node = n.getNodeEx() | model = getSourceModel(node) or not exists(getSourceModel(node)) and model = "" ) } - private predicate sinkModel(NodeEx node, string model) { - sinkNode(node, _) and - ( + private predicate sinkModel(Nd n, string model) { + exists(NodeEx node | sinkNode(n, _) and node = n.getNodeEx() | model = getSinkModel(node) or not exists(getSinkModel(node)) and model = "" @@ -242,29 +252,29 @@ module MakeImpl Lang> { class ApNil extends Ap; - predicate revFlow(NodeEx node); + predicate revFlow(Nd node); bindingset[node, state] - predicate revFlow(NodeEx node, FlowState state, Ap ap); + predicate revFlow(Nd node, FlowState state, Ap ap); predicate callMayFlowThroughRev(DataFlowCall call); - predicate parameterMayFlowThrough(ParamNodeEx p, boolean emptyAp); + predicate parameterMayFlowThrough(ParamNd p, boolean emptyAp); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind); + predicate returnMayFlowThrough(RetNd ret, ReturnKindExt kind); predicate storeStepCand( - NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, DataFlowType containerType + Nd node1, Content c, Nd node2, DataFlowType contentType, DataFlowType containerType ); - predicate readStepCand(NodeEx n1, Content c, NodeEx n2); + predicate readStepCand(Nd n1, Content c, Nd n2); predicate callEdgeArgParam( - DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, boolean emptyAp + DataFlowCall call, DataFlowCallable c, ArgNd arg, ParamNd p, boolean emptyAp ); predicate callEdgeReturn( - DataFlowCall call, DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + DataFlowCall call, DataFlowCallable c, RetNd ret, ReturnKindExt kind, Nd out, boolean allowsFieldFlow ); @@ -362,15 +372,15 @@ module MakeImpl Lang> { bindingset[node1, state1] bindingset[node2, state2] predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - Typ t, LocalCc lcc, string label + Nd node1, FlowState state1, Nd node2, FlowState state2, boolean preservesValue, Typ t, + LocalCc lcc, string label ); bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t); + predicate filter(Nd node, FlowState state, Typ t0, Ap ap, Typ t); bindingset[node, ap, isStoreStep] - predicate stepFilter(NodeEx node, Ap ap, boolean isStoreStep); + predicate stepFilter(Nd node, Ap ap, boolean isStoreStep); bindingset[t1, t2] predicate typecheck(Typ t1, Typ t2); @@ -398,13 +408,13 @@ module MakeImpl Lang> { /* Begin: Stage logic. */ pragma[nomagic] - private Typ getNodeTyp(NodeEx node) { + private Typ getNodeTyp(Nd node) { PrevStage::revFlow(node) and result = getTyp(node.getDataFlowType()) } pragma[nomagic] private predicate flowThroughOutOfCall( - DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow + DataFlowCall call, RetNd ret, Nd out, boolean allowsFieldFlow ) { exists(ReturnKindExt kind | PrevStage::callEdgeReturn(call, _, ret, kind, out, allowsFieldFlow) and @@ -447,13 +457,13 @@ module MakeImpl Lang> { */ pragma[nomagic] additional predicate fwdFlow( - NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored + Nd node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored ) { fwdFlow1(node, state, cc, summaryCtx, _, t, ap, stored) } private predicate fwdFlow1( - NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Typ t, Ap ap, + Nd node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Typ t, Ap ap, TypOption stored ) { exists(ApApprox apa | @@ -461,7 +471,7 @@ module MakeImpl Lang> { PrevStage::revFlow(node, state, apa) and filter(node, state, t0, ap, t) and ( - if node instanceof CastingNodeEx + if node instanceof CastingNd then ap instanceof ApNil or compatibleContainer(getHeadContent(ap), node.getDataFlowType()) or @@ -473,7 +483,7 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, ApApprox apa, + Nd node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, ApApprox apa, TypOption stored ) { sourceNode(node, state) and @@ -484,7 +494,7 @@ module MakeImpl Lang> { apa = getApprox(ap) and stored.isNone() or - exists(NodeEx mid, FlowState state0, Typ t0, LocalCc localCc | + exists(Nd mid, FlowState state0, Typ t0, LocalCc localCc | fwdFlow(mid, state0, cc, summaryCtx, t0, ap, stored) and apa = getApprox(ap) and localCc = getLocalCc(cc) @@ -531,7 +541,7 @@ module MakeImpl Lang> { apa = getApprox(ap) or // flow through a callable - exists(DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow | + exists(DataFlowCall call, RetNd ret, boolean allowsFieldFlow | fwdFlowThrough(call, cc, state, summaryCtx, t, ap, stored, ret) and flowThroughOutOfCall(call, ret, node, allowsFieldFlow) and apa = getApprox(ap) and @@ -542,7 +552,7 @@ module MakeImpl Lang> { private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParamNodeEx p, FlowState state, Typ t, Ap ap, TypOption stored) { + TSummaryCtxSome(ParamNd p, FlowState state, Typ t, Ap ap, TypOption stored) { fwdFlowInFlowThrough(p, state, _, t, ap, stored) } @@ -567,7 +577,7 @@ module MakeImpl Lang> { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParamNodeEx p; + private ParamNd p; private FlowState state; private Typ t; private Ap ap; @@ -575,7 +585,7 @@ module MakeImpl Lang> { SummaryCtxSome() { this = TSummaryCtxSome(p, state, t, ap, stored) } - ParamNodeEx getParamNode() { result = p } + ParamNd getParamNode() { result = p } private string ppTyp() { result = t.toString() and result != "" } @@ -586,20 +596,20 @@ module MakeImpl Lang> { override Location getLocation() { result = p.getLocation() } } - private predicate fwdFlowJump(NodeEx node, FlowState state, Typ t, Ap ap, TypOption stored) { - exists(NodeEx mid | + private predicate fwdFlowJump(Nd node, FlowState state, Typ t, Ap ap, TypOption stored) { + exists(Nd mid | fwdFlow(mid, state, _, _, t, ap, stored) and jumpStepEx(mid, node) ) or - exists(NodeEx mid | + exists(Nd mid | fwdFlow(mid, state, _, _, _, ap, stored) and additionalJumpStep(mid, node, _) and t = getNodeTyp(node) and ap instanceof ApNil ) or - exists(NodeEx mid, FlowState state0 | + exists(Nd mid, FlowState state0 | fwdFlow(mid, state0, _, _, _, ap, stored) and additionalJumpStateStep(mid, state0, node, state, _) and t = getNodeTyp(node) and @@ -609,8 +619,8 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowStore( - NodeEx node1, Typ t1, Ap ap1, TypOption stored1, Content c, Typ t2, TypOption stored2, - NodeEx node2, FlowState state, Cc cc, SummaryCtx summaryCtx + Nd node1, Typ t1, Ap ap1, TypOption stored1, Content c, Typ t2, TypOption stored2, + Nd node2, FlowState state, Cc cc, SummaryCtx summaryCtx ) { exists(DataFlowType contentType, DataFlowType containerType | fwdFlow(node1, state, cc, summaryCtx, t1, ap1, stored1) and @@ -637,21 +647,21 @@ module MakeImpl Lang> { } pragma[nomagic] - private predicate readStepCand(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { + private predicate readStepCand(Nd node1, ApHeadContent apc, Content c, Nd node2) { PrevStage::readStepCand(node1, c, node2) and apc = projectToHeadContent(c) } bindingset[node1, apc] pragma[inline_late] - private predicate readStepCand0(NodeEx node1, ApHeadContent apc, Content c, NodeEx node2) { + private predicate readStepCand0(Nd node1, ApHeadContent apc, Content c, Nd node2) { readStepCand(node1, apc, c, node2) } pragma[nomagic] private predicate fwdFlowRead0( - Typ t, Ap ap, TypOption stored, Content c, NodeEx node1, NodeEx node2, FlowState state, - Cc cc, SummaryCtx summaryCtx + Typ t, Ap ap, TypOption stored, Content c, Nd node1, Nd node2, FlowState state, Cc cc, + SummaryCtx summaryCtx ) { exists(ApHeadContent apc | fwdFlow(node1, state, cc, summaryCtx, t, ap, stored) and @@ -664,7 +674,7 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowRead( - NodeEx node1, Typ t1, Ap ap1, TypOption stored1, Content c, NodeEx node2, Typ t2, Ap ap2, + Nd node1, Typ t1, Ap ap1, TypOption stored1, Content c, Nd node2, Typ t2, Ap ap2, TypOption stored2, FlowState state, Cc cc, SummaryCtx summaryCtx ) { exists(Typ ct1, Typ ct2 | @@ -682,7 +692,7 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowIntoArg( - ArgNodeEx arg, FlowState state, Cc outercc, SummaryCtx summaryCtx, Typ t, Ap ap, + ArgNd arg, FlowState state, Cc outercc, SummaryCtx summaryCtx, Typ t, Ap ap, boolean emptyAp, TypOption stored, boolean cc ) { fwdFlow(arg, state, outercc, summaryCtx, t, ap, stored) and @@ -706,7 +716,7 @@ module MakeImpl Lang> { private module FwdFlowIn { pragma[nomagic] private predicate callEdgeArgParamRestricted( - DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, boolean emptyAp + DataFlowCall call, DataFlowCallable c, ArgNd arg, ParamNd p, boolean emptyAp ) { PrevStage::callEdgeArgParam(call, c, arg, p, emptyAp) and if @@ -740,7 +750,7 @@ module MakeImpl Lang> { bindingset[arg, ctx] pragma[inline_late] private DataFlowCallable viableImplCallContextReducedInlineLate( - DataFlowCall call, ArgNodeEx arg, CcCall ctx + DataFlowCall call, ArgNd arg, CcCall ctx ) { callEdgeArgParamRestricted(call, _, arg, _, _) and instanceofCcCall(ctx) and @@ -750,7 +760,7 @@ module MakeImpl Lang> { bindingset[call] pragma[inline_late] private predicate callEdgeArgParamRestrictedInlineLate( - DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, boolean emptyAp + DataFlowCall call, DataFlowCallable c, ArgNd arg, ParamNd p, boolean emptyAp ) { callEdgeArgParamRestricted(call, c, arg, p, emptyAp) } @@ -765,7 +775,7 @@ module MakeImpl Lang> { bindingset[arg, outercc] pragma[inline_late] private predicate viableImplArgNotCallContextReduced( - DataFlowCall call, ArgNodeEx arg, Cc outercc + DataFlowCall call, ArgNd arg, Cc outercc ) { callEdgeArgParamRestricted(call, _, arg, _, _) and instanceofCc(outercc) and @@ -774,8 +784,8 @@ module MakeImpl Lang> { pragma[inline] private predicate fwdFlowInCand( - DataFlowCall call, ArgNodeEx arg, FlowState state, Cc outercc, DataFlowCallable inner, - ParamNodeEx p, SummaryCtx summaryCtx, Typ t, Ap ap, boolean emptyAp, TypOption stored, + DataFlowCall call, ArgNd arg, FlowState state, Cc outercc, DataFlowCallable inner, + ParamNd p, SummaryCtx summaryCtx, Typ t, Ap ap, boolean emptyAp, TypOption stored, boolean cc ) { fwdFlowIntoArg(arg, state, outercc, summaryCtx, t, ap, emptyAp, stored, cc) and @@ -791,8 +801,8 @@ module MakeImpl Lang> { pragma[inline] private predicate fwdFlowInCandTypeFlowDisabled( - DataFlowCall call, ArgNodeEx arg, FlowState state, Cc outercc, DataFlowCallable inner, - ParamNodeEx p, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, boolean cc + DataFlowCall call, ArgNd arg, FlowState state, Cc outercc, DataFlowCallable inner, + ParamNd p, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, boolean cc ) { not enableTypeFlow() and fwdFlowInCand(call, arg, state, outercc, inner, p, summaryCtx, t, ap, _, stored, cc) @@ -800,7 +810,7 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowInCandTypeFlowEnabled( - DataFlowCall call, ArgNodeEx arg, Cc outercc, DataFlowCallable inner, ParamNodeEx p, + DataFlowCall call, ArgNd arg, Cc outercc, DataFlowCallable inner, ParamNd p, boolean emptyAp, boolean cc ) { enableTypeFlow() and @@ -818,7 +828,7 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowInValidEdgeTypeFlowEnabled( - DataFlowCall call, ArgNodeEx arg, Cc outercc, DataFlowCallable inner, ParamNodeEx p, + DataFlowCall call, ArgNd arg, Cc outercc, DataFlowCallable inner, ParamNd p, CcCall innercc, boolean emptyAp, boolean cc ) { fwdFlowInCandTypeFlowEnabled(call, arg, outercc, inner, p, emptyAp, cc) and @@ -828,9 +838,9 @@ module MakeImpl Lang> { pragma[inline] predicate fwdFlowIn( - DataFlowCall call, ArgNodeEx arg, DataFlowCallable inner, ParamNodeEx p, - FlowState state, Cc outercc, CcCall innercc, SummaryCtx summaryCtx, Typ t, Ap ap, - TypOption stored, boolean cc + DataFlowCall call, ArgNd arg, DataFlowCallable inner, ParamNd p, FlowState state, + Cc outercc, CcCall innercc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, + boolean cc ) { // type flow disabled: linear recursion fwdFlowInCandTypeFlowDisabled(call, arg, state, outercc, inner, p, summaryCtx, t, ap, @@ -851,7 +861,7 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowInNoFlowThrough( - ParamNodeEx p, FlowState state, CcCall innercc, Typ t, Ap ap, TypOption stored + ParamNd p, FlowState state, CcCall innercc, Typ t, Ap ap, TypOption stored ) { FwdFlowInNoThrough::fwdFlowIn(_, _, _, p, state, _, innercc, _, t, ap, stored, _) } @@ -862,7 +872,7 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowInFlowThrough( - ParamNodeEx p, FlowState state, CcCall innercc, Typ t, Ap ap, TypOption stored + ParamNd p, FlowState state, CcCall innercc, Typ t, Ap ap, TypOption stored ) { FwdFlowInThrough::fwdFlowIn(_, _, _, p, state, _, innercc, _, t, ap, stored, _) } @@ -886,7 +896,7 @@ module MakeImpl Lang> { bindingset[call] pragma[inline_late] private predicate flowOutOfCallInlineLate( - DataFlowCall call, DataFlowCallable c, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow + DataFlowCall call, DataFlowCallable c, RetNd ret, Nd out, boolean allowsFieldFlow ) { PrevStage::callEdgeReturn(call, c, ret, _, out, allowsFieldFlow) } @@ -895,7 +905,7 @@ module MakeImpl Lang> { pragma[inline_late] pragma[noopt] private predicate flowOutOfCallNotCallContextReduced( - DataFlowCall call, DataFlowCallable c, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + DataFlowCall call, DataFlowCallable c, RetNd ret, Nd out, boolean allowsFieldFlow, CcNoCall innercc ) { viableImplNotCallContextReducedReverse(innercc) and @@ -904,7 +914,7 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowIntoRet( - RetNodeEx ret, FlowState state, CcNoCall cc, SummaryCtx summaryCtx, Typ t, Ap ap, + RetNd ret, FlowState state, CcNoCall cc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored ) { instanceofCcNoCall(cc) and @@ -914,7 +924,7 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowOutCand( - DataFlowCall call, RetNodeEx ret, CcNoCall innercc, DataFlowCallable inner, NodeEx out, + DataFlowCall call, RetNd ret, CcNoCall innercc, DataFlowCallable inner, Nd out, boolean allowsFieldFlow ) { fwdFlowIntoRet(ret, _, innercc, _, _, _, _) and @@ -929,7 +939,7 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowOutValidEdge( - DataFlowCall call, RetNodeEx ret, CcNoCall innercc, DataFlowCallable inner, NodeEx out, + DataFlowCall call, RetNd ret, CcNoCall innercc, DataFlowCallable inner, Nd out, CcNoCall outercc, boolean allowsFieldFlow ) { fwdFlowOutCand(call, ret, innercc, inner, out, allowsFieldFlow) and @@ -939,10 +949,10 @@ module MakeImpl Lang> { pragma[inline] private predicate fwdFlowOut( - DataFlowCall call, DataFlowCallable inner, NodeEx out, FlowState state, CcNoCall outercc, + DataFlowCall call, DataFlowCallable inner, Nd out, FlowState state, CcNoCall outercc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored ) { - exists(RetNodeEx ret, CcNoCall innercc, boolean allowsFieldFlow | + exists(RetNd ret, CcNoCall innercc, boolean allowsFieldFlow | fwdFlowIntoRet(ret, state, innercc, summaryCtx, t, ap, stored) and fwdFlowOutValidEdge(call, ret, innercc, inner, out, outercc, allowsFieldFlow) and not inBarrier(out, state) and @@ -959,7 +969,7 @@ module MakeImpl Lang> { pragma[nomagic] private predicate dataFlowTakenCallEdgeIn0( - DataFlowCall call, DataFlowCallable c, ParamNodeEx p, FlowState state, CcCall innercc, + DataFlowCall call, DataFlowCallable c, ParamNd p, FlowState state, CcCall innercc, Typ t, Ap ap, TypOption stored, boolean cc ) { FwdFlowInNoThrough::fwdFlowIn(call, _, c, p, state, _, innercc, _, t, ap, stored, cc) @@ -969,7 +979,7 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlow1Param( - ParamNodeEx p, FlowState state, CcCall cc, Typ t0, Ap ap, TypOption stored + ParamNd p, FlowState state, CcCall cc, Typ t0, Ap ap, TypOption stored ) { instanceofCcCall(cc) and fwdFlow1(p, state, cc, _, t0, _, ap, stored) @@ -977,7 +987,7 @@ module MakeImpl Lang> { pragma[nomagic] predicate dataFlowTakenCallEdgeIn(DataFlowCall call, DataFlowCallable c, boolean cc) { - exists(ParamNodeEx p, FlowState state, CcCall innercc, Typ t, Ap ap, TypOption stored | + exists(ParamNd p, FlowState state, CcCall innercc, Typ t, Ap ap, TypOption stored | dataFlowTakenCallEdgeIn0(call, c, p, state, innercc, t, ap, stored, cc) and fwdFlow1Param(p, state, innercc, t, ap, stored) ) @@ -985,15 +995,15 @@ module MakeImpl Lang> { pragma[nomagic] private predicate dataFlowTakenCallEdgeOut0( - DataFlowCall call, DataFlowCallable c, NodeEx node, FlowState state, Cc cc, Typ t, - Ap ap, TypOption stored + DataFlowCall call, DataFlowCallable c, Nd node, FlowState state, Cc cc, Typ t, Ap ap, + TypOption stored ) { fwdFlowOut(call, c, node, state, cc, _, t, ap, stored) } pragma[nomagic] private predicate fwdFlow1Out( - NodeEx node, FlowState state, Cc cc, Typ t0, Ap ap, TypOption stored + Nd node, FlowState state, Cc cc, Typ t0, Ap ap, TypOption stored ) { fwdFlow1(node, state, cc, _, t0, _, ap, stored) and PrevStage::callEdgeReturn(_, _, _, _, node, _) @@ -1001,21 +1011,21 @@ module MakeImpl Lang> { pragma[nomagic] predicate dataFlowTakenCallEdgeOut(DataFlowCall call, DataFlowCallable c) { - exists(NodeEx node, FlowState state, Cc cc, Typ t, Ap ap, TypOption stored | + exists(Nd node, FlowState state, Cc cc, Typ t, Ap ap, TypOption stored | dataFlowTakenCallEdgeOut0(call, c, node, state, cc, t, ap, stored) and fwdFlow1Out(node, state, cc, t, ap, stored) ) } predicate dataFlowNonCallEntry(DataFlowCallable c, boolean cc) { - exists(NodeEx node, FlowState state | + exists(Nd node, FlowState state | sourceNode(node, state) and (if hasSourceCallCtx() then cc = true else cc = false) and PrevStage::revFlow(node, state, any(PrevStage::ApNil nil)) and c = node.getEnclosingCallable() ) or - exists(NodeEx node | + exists(Nd node | cc = false and fwdFlowJump(node, _, _, _, _) and c = node.getEnclosingCallable() @@ -1026,7 +1036,7 @@ module MakeImpl Lang> { private module FwdTypeFlow = TypeFlow; private predicate flowIntoCallTaken( - DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, boolean emptyAp + DataFlowCall call, DataFlowCallable c, ArgNd arg, ParamNd p, boolean emptyAp ) { PrevStage::callEdgeArgParam(call, c, arg, p, emptyAp) and FwdTypeFlowInput::dataFlowTakenCallEdgeIn(call, c, _) @@ -1034,10 +1044,10 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowRetFromArg( - RetNodeEx ret, FlowState state, CcCall ccc, SummaryCtxSome summaryCtx, Typ t, Ap ap, + RetNd ret, FlowState state, CcCall ccc, SummaryCtxSome summaryCtx, Typ t, Ap ap, TypOption stored ) { - exists(ReturnKindExt kind, ParamNodeEx p, Ap argAp | + exists(ReturnKindExt kind, ParamNd p, Ap argAp | instanceofCcCall(ccc) and fwdFlow(pragma[only_bind_into](ret), state, ccc, summaryCtx, t, ap, stored) and summaryCtx = @@ -1051,9 +1061,8 @@ module MakeImpl Lang> { pragma[inline] private predicate fwdFlowThrough0( - DataFlowCall call, ArgNodeEx arg, Cc cc, FlowState state, CcCall ccc, - SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, RetNodeEx ret, - SummaryCtxSome innerSummaryCtx + DataFlowCall call, ArgNd arg, Cc cc, FlowState state, CcCall ccc, SummaryCtx summaryCtx, + Typ t, Ap ap, TypOption stored, RetNd ret, SummaryCtxSome innerSummaryCtx ) { fwdFlowRetFromArg(ret, state, ccc, innerSummaryCtx, t, ap, stored) and fwdFlowIsEntered(call, arg, cc, ccc, summaryCtx, innerSummaryCtx) @@ -1062,15 +1071,15 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowThrough( DataFlowCall call, Cc cc, FlowState state, SummaryCtx summaryCtx, Typ t, Ap ap, - TypOption stored, RetNodeEx ret + TypOption stored, RetNd ret ) { fwdFlowThrough0(call, _, cc, state, _, summaryCtx, t, ap, stored, ret, _) } pragma[nomagic] private predicate fwdFlowIsEntered0( - DataFlowCall call, ArgNodeEx arg, Cc cc, CcCall innerCc, SummaryCtx summaryCtx, - ParamNodeEx p, FlowState state, Typ t, Ap ap, TypOption stored + DataFlowCall call, ArgNd arg, Cc cc, CcCall innerCc, SummaryCtx summaryCtx, ParamNd p, + FlowState state, Typ t, Ap ap, TypOption stored ) { FwdFlowInThrough::fwdFlowIn(call, arg, _, p, state, cc, innerCc, summaryCtx, t, ap, stored, _) @@ -1082,29 +1091,29 @@ module MakeImpl Lang> { */ pragma[nomagic] private predicate fwdFlowIsEntered( - DataFlowCall call, ArgNodeEx arg, Cc cc, CcCall innerCc, SummaryCtx summaryCtx, + DataFlowCall call, ArgNd arg, Cc cc, CcCall innerCc, SummaryCtx summaryCtx, SummaryCtxSome innerSummaryCtx ) { - exists(ParamNodeEx p, FlowState state, Typ t, Ap ap, TypOption stored | + exists(ParamNd p, FlowState state, Typ t, Ap ap, TypOption stored | fwdFlowIsEntered0(call, arg, cc, innerCc, summaryCtx, p, state, t, ap, stored) and innerSummaryCtx = TSummaryCtxSome(p, state, t, ap, stored) ) } pragma[nomagic] - private predicate storeStepFwd(NodeEx node1, Ap ap1, Content c, NodeEx node2, Ap ap2) { + private predicate storeStepFwd(Nd node1, Ap ap1, Content c, Nd node2, Ap ap2) { fwdFlowStore(node1, _, ap1, _, c, _, _, node2, _, _, _) and readStepFwd(_, ap2, c, _, ap1) } pragma[nomagic] - private predicate readStepFwd(NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2) { + private predicate readStepFwd(Nd n1, Ap ap1, Content c, Nd n2, Ap ap2) { fwdFlowRead(n1, _, ap1, _, c, n2, _, ap2, _, _, _, _) } pragma[nomagic] private predicate returnFlowsThrough0( - DataFlowCall call, FlowState state, CcCall ccc, Ap ap, RetNodeEx ret, + DataFlowCall call, FlowState state, CcCall ccc, Ap ap, RetNd ret, SummaryCtxSome innerSummaryCtx ) { fwdFlowThrough0(call, _, _, state, ccc, _, _, ap, _, ret, innerSummaryCtx) @@ -1112,8 +1121,8 @@ module MakeImpl Lang> { pragma[nomagic] private predicate returnFlowsThrough( - RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Typ argT, - Ap argAp, TypOption argStored, Ap ap + RetNd ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNd p, Typ argT, Ap argAp, + TypOption argStored, Ap ap ) { exists(DataFlowCall call, boolean allowsFieldFlow | returnFlowsThrough0(call, state, ccc, ap, ret, @@ -1125,9 +1134,7 @@ module MakeImpl Lang> { } pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, Ap argAp - ) { + private predicate flowThroughIntoCall(DataFlowCall call, ArgNd arg, ParamNd p, Ap argAp) { exists(Typ argT, TypOption argStored | returnFlowsThrough(_, _, _, _, pragma[only_bind_into](p), pragma[only_bind_into](argT), pragma[only_bind_into](argAp), pragma[only_bind_into](argStored), _) and @@ -1139,7 +1146,7 @@ module MakeImpl Lang> { pragma[nomagic] private predicate flowIntoCallAp( - DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, Ap ap + DataFlowCall call, DataFlowCallable c, ArgNd arg, ParamNd p, Ap ap ) { flowIntoCallTaken(call, c, arg, p, isNil(ap)) and fwdFlow(arg, _, _, _, _, ap, _) @@ -1147,8 +1154,8 @@ module MakeImpl Lang> { pragma[nomagic] private predicate flowOutOfCallAp( - DataFlowCall call, DataFlowCallable c, RetNodeEx ret, ReturnPosition pos, NodeEx out, - Ap ap, boolean allowsFieldFlow + DataFlowCall call, DataFlowCallable c, RetNd ret, ReturnPosition pos, Nd out, Ap ap, + boolean allowsFieldFlow ) { PrevStage::callEdgeReturn(call, c, ret, _, out, allowsFieldFlow) and fwdFlow(ret, _, _, _, _, ap, _) and @@ -1171,7 +1178,7 @@ module MakeImpl Lang> { */ pragma[nomagic] additional predicate revFlow( - NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap + Nd node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap ) { revFlow0(node, state, returnCtx, returnAp, ap) and fwdFlow(node, state, _, _, _, ap, _) @@ -1179,7 +1186,7 @@ module MakeImpl Lang> { pragma[nomagic] private predicate revFlow0( - NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap + Nd node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap ) { fwdFlow(node, state, _, _, _, ap, _) and sinkNode(node, state) and @@ -1191,12 +1198,12 @@ module MakeImpl Lang> { returnAp = apNone() and ap instanceof ApNil or - exists(NodeEx mid, FlowState state0 | + exists(Nd mid, FlowState state0 | localStep(node, state, mid, state0, true, _, _, _) and revFlow(mid, state0, returnCtx, returnAp, ap) ) or - exists(NodeEx mid, FlowState state0 | + exists(Nd mid, FlowState state0 | localStep(node, pragma[only_bind_into](state), mid, state0, false, _, _, _) and revFlow(mid, state0, returnCtx, returnAp, ap) and ap instanceof ApNil @@ -1213,7 +1220,7 @@ module MakeImpl Lang> { ) or // read - exists(NodeEx mid, Ap ap0 | + exists(Nd mid, Ap ap0 | revFlow(mid, state, returnCtx, returnAp, ap0) and readStepFwd(node, ap, _, mid, ap0) ) @@ -1224,7 +1231,7 @@ module MakeImpl Lang> { returnAp = apNone() or // flow through a callable - exists(DataFlowCall call, ParamNodeEx p | + exists(DataFlowCall call, ParamNd p | revFlowThrough(call, returnCtx, p, state, returnAp, ap) and flowThroughIntoCall(call, node, p, ap) ) @@ -1242,19 +1249,19 @@ module MakeImpl Lang> { ) } - private predicate revFlowJump(NodeEx node, FlowState state, Ap ap) { - exists(NodeEx mid | + private predicate revFlowJump(Nd node, FlowState state, Ap ap) { + exists(Nd mid | jumpStepEx(node, mid) and revFlow(mid, state, _, _, ap) ) or - exists(NodeEx mid | + exists(Nd mid | additionalJumpStep(node, mid, _) and revFlow(pragma[only_bind_into](mid), state, _, _, ap) and ap instanceof ApNil ) or - exists(NodeEx mid, FlowState state0 | + exists(Nd mid, FlowState state0 | additionalJumpStateStep(node, state, mid, state0, _) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, ap) and ap instanceof ApNil @@ -1263,7 +1270,7 @@ module MakeImpl Lang> { pragma[nomagic] private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, NodeEx mid, ReturnCtx returnCtx, + Ap ap0, Content c, Ap ap, Nd node, FlowState state, Nd mid, ReturnCtx returnCtx, ApOption returnAp ) { revFlow(mid, state, returnCtx, returnAp, ap0) and @@ -1276,7 +1283,7 @@ module MakeImpl Lang> { */ pragma[nomagic] private predicate revFlowConsCand(Ap cons, Content c, Ap tail) { - exists(NodeEx mid, Ap tail0 | + exists(Nd mid, Ap tail0 | revFlow(mid, _, _, _, tail) and tail = pragma[only_bind_into](tail0) and readStepFwd(_, cons, c, mid, tail0) @@ -1296,7 +1303,7 @@ module MakeImpl Lang> { pragma[nomagic] predicate dataFlowTakenCallEdgeIn(DataFlowCall call, DataFlowCallable c, boolean cc) { - exists(RetNodeEx ret | + exists(RetNd ret | revFlowOut(call, ret, _, _, _, cc, _, _) and c = ret.getEnclosingCallable() ) @@ -1308,14 +1315,14 @@ module MakeImpl Lang> { } predicate dataFlowNonCallEntry(DataFlowCallable c, boolean cc) { - exists(NodeEx node, FlowState state, ApNil nil | + exists(Nd node, FlowState state, ApNil nil | fwdFlow(node, state, _, _, _, nil, _) and sinkNode(node, state) and (if hasSinkCallCtx() then cc = true else cc = false) and c = node.getEnclosingCallable() ) or - exists(NodeEx node | + exists(Nd node | cc = false and revFlowJump(node, _, _) and c = node.getEnclosingCallable() @@ -1327,7 +1334,7 @@ module MakeImpl Lang> { pragma[nomagic] private predicate flowIntoCallApValid( - DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, Ap ap + DataFlowCall call, DataFlowCallable c, ArgNd arg, ParamNd p, Ap ap ) { flowIntoCallAp(call, c, arg, p, ap) and RevTypeFlow::typeFlowValidEdgeOut(call, c) @@ -1335,7 +1342,7 @@ module MakeImpl Lang> { pragma[nomagic] private predicate flowOutOfCallApValid( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, Ap ap, boolean cc + DataFlowCall call, RetNd ret, ReturnPosition pos, Nd out, Ap ap, boolean cc ) { exists(DataFlowCallable c | flowOutOfCallAp(call, c, ret, pos, out, ap, _) and @@ -1344,9 +1351,9 @@ module MakeImpl Lang> { } private predicate revFlowIn( - DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, FlowState state, Ap ap + DataFlowCall call, DataFlowCallable c, ArgNd arg, FlowState state, Ap ap ) { - exists(ParamNodeEx p | + exists(ParamNd p | revFlow(p, state, TReturnCtxNone(), _, ap) and flowIntoCallApValid(call, c, arg, p, ap) ) @@ -1354,10 +1361,10 @@ module MakeImpl Lang> { pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, - ReturnCtx returnCtx, boolean cc, ApOption returnAp, Ap ap + DataFlowCall call, RetNd ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, + boolean cc, ApOption returnAp, Ap ap ) { - exists(NodeEx out | + exists(Nd out | revFlow(out, state, returnCtx, returnAp, ap) and flowOutOfCallApValid(call, ret, pos, out, ap, cc) and if returnCtx instanceof TReturnCtxNone then cc = false else cc = true @@ -1366,7 +1373,7 @@ module MakeImpl Lang> { pragma[nomagic] private predicate revFlowParamToReturn( - ParamNodeEx p, FlowState state, ReturnPosition pos, Ap returnAp, Ap ap + ParamNd p, FlowState state, ReturnPosition pos, Ap returnAp, Ap ap ) { revFlow(pragma[only_bind_into](p), state, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), pragma[only_bind_into](ap)) and @@ -1376,7 +1383,7 @@ module MakeImpl Lang> { pragma[nomagic] private predicate revFlowThrough( - DataFlowCall call, ReturnCtx returnCtx, ParamNodeEx p, FlowState state, ApOption returnAp, + DataFlowCall call, ReturnCtx returnCtx, ParamNd p, FlowState state, ApOption returnAp, Ap ap ) { exists(ReturnPosition pos, Ap innerReturnAp | @@ -1394,7 +1401,7 @@ module MakeImpl Lang> { private predicate revFlowIsReturned( DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | + exists(RetNd ret, FlowState state, CcCall ccc | revFlowOut(call, ret, pos, state, returnCtx, _, returnAp, ap) and returnFlowsThrough(ret, pos, state, ccc, _, _, _, _, ap) and matchesCall(ccc, call) @@ -1403,8 +1410,7 @@ module MakeImpl Lang> { pragma[nomagic] predicate storeStepCand( - NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, - DataFlowType containerType + Nd node1, Content c, Nd node2, DataFlowType contentType, DataFlowType containerType ) { exists(Ap ap2, Ap ap1 | PrevStage::storeStepCand(node1, c, node2, contentType, containerType) and @@ -1413,7 +1419,7 @@ module MakeImpl Lang> { ) } - predicate readStepCand(NodeEx node1, Content c, NodeEx node2) { + predicate readStepCand(Nd node1, Content c, Nd node2) { exists(Ap ap1, Ap ap2 | revFlow(node2, _, _, _, pragma[only_bind_into](ap2)) and readStepFwd(node1, ap1, c, node2, ap2) and @@ -1421,10 +1427,10 @@ module MakeImpl Lang> { ) } - predicate revFlow(NodeEx node, FlowState state, Ap ap) { revFlow(node, state, _, _, ap) } + predicate revFlow(Nd node, FlowState state, Ap ap) { revFlow(node, state, _, _, ap) } pragma[nomagic] - predicate revFlow(NodeEx node) { revFlow(node, _, _, _, _) } + predicate revFlow(Nd node) { revFlow(node, _, _, _, _) } private predicate fwdConsCand(Content c, Ap ap) { storeStepFwd(_, ap, c, _, _) } @@ -1450,15 +1456,13 @@ module MakeImpl Lang> { } pragma[nomagic] - private predicate parameterFlowsThroughRev( - ParamNodeEx p, Ap ap, ReturnPosition pos, Ap returnAp - ) { + private predicate parameterFlowsThroughRev(ParamNd p, Ap ap, ReturnPosition pos, Ap returnAp) { revFlow(p, _, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap) and Stage1::parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] - private predicate parameterMayFlowThroughAp(ParamNodeEx p, Ap ap) { + private predicate parameterMayFlowThroughAp(ParamNd p, Ap ap) { exists(ReturnPosition pos | returnFlowsThrough(_, pos, _, _, p, _, ap, _, _) and parameterFlowsThroughRev(p, ap, pos, _) @@ -1466,7 +1470,7 @@ module MakeImpl Lang> { } pragma[nomagic] - predicate parameterMayFlowThrough(ParamNodeEx p, boolean emptyAp) { + predicate parameterMayFlowThrough(ParamNd p, boolean emptyAp) { exists(Ap ap | parameterMayFlowThroughAp(p, ap) and emptyAp = isNil(ap) @@ -1474,7 +1478,7 @@ module MakeImpl Lang> { } pragma[nomagic] - private predicate nodeMayUseSummary0(NodeEx n, ParamNodeEx p, FlowState state, Ap ap) { + private predicate nodeMayUseSummary0(Nd n, ParamNd p, FlowState state, Ap ap) { exists(Ap ap0 | parameterMayFlowThrough(p, _) and revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, ap0) and @@ -1487,16 +1491,16 @@ module MakeImpl Lang> { * and remains relevant for the following pruning stage. */ pragma[nomagic] - additional predicate nodeMayUseSummary(NodeEx n, FlowState state, Ap ap) { - exists(ParamNodeEx p | + additional predicate nodeMayUseSummary(Nd n, FlowState state, Ap ap) { + exists(ParamNd p | parameterMayFlowThroughAp(p, ap) and nodeMayUseSummary0(n, p, state, ap) ) } pragma[nomagic] - predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind) { - exists(ParamNodeEx p, ReturnPosition pos, Ap argAp, Ap ap | + predicate returnMayFlowThrough(RetNd ret, ReturnKindExt kind) { + exists(ParamNd p, ReturnPosition pos, Ap argAp, Ap ap | returnFlowsThrough(ret, pos, _, _, p, _, argAp, _, ap) and parameterFlowsThroughRev(p, argAp, pos, ap) and kind = pos.getKind() @@ -1505,10 +1509,10 @@ module MakeImpl Lang> { pragma[nomagic] private predicate revFlowThroughArg( - DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, + DataFlowCall call, ArgNd arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap ) { - exists(ParamNodeEx p | + exists(ParamNd p | revFlowThrough(call, returnCtx, p, state, returnAp, ap) and flowThroughIntoCall(call, arg, p, ap) ) @@ -1516,14 +1520,14 @@ module MakeImpl Lang> { pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call) { - exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | + exists(ArgNd arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | revFlow(arg, state, returnCtx, returnAp, ap) and revFlowThroughArg(call, arg, state, returnCtx, returnAp, ap) ) } predicate callEdgeArgParam( - DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, boolean emptyAp + DataFlowCall call, DataFlowCallable c, ArgNd arg, ParamNd p, boolean emptyAp ) { exists(FlowState state, Ap ap | flowIntoCallAp(call, c, arg, p, ap) and @@ -1538,7 +1542,7 @@ module MakeImpl Lang> { } predicate callEdgeReturn( - DataFlowCall call, DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + DataFlowCall call, DataFlowCallable c, RetNd ret, ReturnKindExt kind, Nd out, boolean allowsFieldFlow ) { exists(FlowState state, ReturnPosition pos, Ap ap | @@ -1562,7 +1566,7 @@ module MakeImpl Lang> { bindingset[node1, state1] bindingset[node2, state2] signature predicate localStepSig( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + Nd node1, FlowState state1, Nd node2, FlowState state2, boolean preservesValue, DataFlowType t, LocalCallContext lcc, string label ); @@ -1574,22 +1578,24 @@ module MakeImpl Lang> { * this stage. */ additional module LocalFlowBigStep { + final private class FinalNd = Nd; + /** * A node where some checking is required, and hence the big-step relation * is not allowed to step over. */ - private class FlowCheckNode extends NodeEx { + private class FlowCheckNode extends FinalNd { FlowCheckNode() { revFlow(this) and ( - flowCheckNode(this) or - Config::neverSkip(this.asNode()) + flowCheckNode(this.getNodeEx()) or + Config::neverSkip(this.getNodeEx().asNode()) ) } } private predicate additionalLocalStateStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, DataFlowType t, + Nd node1, FlowState state1, Nd node2, FlowState state2, DataFlowType t, LocalCallContext lcc, string label ) { exists(ApNil nil | @@ -1604,7 +1610,7 @@ module MakeImpl Lang> { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(NodeEx node, FlowState state, Ap ap) { + private predicate localFlowEntry(Nd node, FlowState state, Ap ap) { revFlow(node, state, ap) and ( sourceNode(node, state) @@ -1615,9 +1621,9 @@ module MakeImpl Lang> { or additionalJumpStateStep(_, _, node, state, _) or - node instanceof ParamNodeEx + node instanceof ParamNd or - node instanceof OutNodeEx + node instanceof OutNd or storeStepCand(_, _, node, _, _) or @@ -1633,10 +1639,10 @@ module MakeImpl Lang> { * Holds if `node` can be the last node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowExit(NodeEx node, FlowState state, Ap ap) { + private predicate localFlowExit(Nd node, FlowState state, Ap ap) { revFlow(node, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and ( - exists(NodeEx next, Ap apNext | revFlow(next, pragma[only_bind_into](state), apNext) | + exists(Nd next, Ap apNext | revFlow(next, pragma[only_bind_into](state), apNext) | jumpStepEx(node, next) and apNext = ap or @@ -1655,7 +1661,7 @@ module MakeImpl Lang> { readStepCand(node, _, next) ) or - exists(NodeEx next, FlowState s | + exists(Nd next, FlowState s | revFlow(next, s, pragma[only_bind_into](ap)) and ap instanceof ApNil | additionalJumpStateStep(node, state, next, s, _) @@ -1679,12 +1685,12 @@ module MakeImpl Lang> { */ pragma[nomagic] private predicate localFlowStepPlus( - NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, + Nd node1, FlowState state, Nd node2, boolean preservesValue, DataFlowType t, LocalCallContext cc, string label ) { not inBarrier(node2, state) and not outBarrier(node1, state) and - exists(NodeEx mid, boolean preservesValue2, DataFlowType t2, string label2, Ap ap | + exists(Nd mid, boolean preservesValue2, DataFlowType t2, string label2, Ap ap | localStepInput(mid, state, node2, state, preservesValue2, t2, cc, label2) and revFlow(node2, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and not outBarrier(mid, state) and @@ -1714,7 +1720,7 @@ module MakeImpl Lang> { */ pragma[nomagic] predicate localFlowBigStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + Nd node1, FlowState state1, Nd node2, FlowState state2, boolean preservesValue, DataFlowType t, LocalCallContext callContext, string label ) { exists(Ap ap | @@ -1740,7 +1746,7 @@ module MakeImpl Lang> { */ pragma[nomagic] predicate localFlowBigStepTc( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + Nd node1, FlowState state1, Nd node2, FlowState state2, boolean preservesValue, DataFlowType t, LocalCallContext callContext, string label ) { exists(Ap ap | @@ -1763,13 +1769,12 @@ module MakeImpl Lang> { additional module Graph { private newtype TPathNode = TPathNodeMid( - NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, - TypOption stored + Nd node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored ) { fwdFlow(node, state, cc, summaryCtx, t, ap, stored) and revFlow(node, state, _, _, ap) } or - TPathNodeSink(NodeEx node, FlowState state) { + TPathNodeSink(Nd node, FlowState state) { exists(PathNodeMid sink | sink.isAtSink() and node = sink.toNormalSinkNode() and @@ -1901,7 +1906,7 @@ module MakeImpl Lang> { * a `FlowState`, a call context, a summary context, a tracked type, and an access path. */ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { - NodeEx node; + Nd node; FlowState state; Cc cc; SummaryCtx summaryCtx; @@ -1911,7 +1916,7 @@ module MakeImpl Lang> { PathNodeMid() { this = TPathNodeMid(node, state, cc, summaryCtx, t, ap, stored) } - override NodeEx getNodeEx() { result = node } + override NodeEx getNodeEx() { result = node.getNodeEx() } override FlowState getState() { result = state } @@ -1932,7 +1937,7 @@ module MakeImpl Lang> { /** If this node corresponds to a sink, gets the normal node for that sink. */ pragma[nomagic] - NodeEx toNormalSinkNode() { result = toNormalSinkNode(node) } + Nd toNormalSinkNode() { result = toNormalSinkNode(node) } override PathNodeImpl getASuccessorImpl(string label) { // an intermediate step to another intermediate node @@ -2027,8 +2032,7 @@ module MakeImpl Lang> { exists(string model | this.isAtSink() and sinkModel(node, model) and - result.getNodeEx() = this.toNormalSinkNode() and - result.getState() = state and + result = TPathNodeSink(this.toNormalSinkNode(), state) and if model != "" then label = "Sink:" + model else label = "" ) } @@ -2040,12 +2044,12 @@ module MakeImpl Lang> { * excluding the call context. */ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { - NodeEx node; + Nd node; FlowState state; PathNodeSink() { this = TPathNodeSink(node, state) } - override NodeEx getNodeEx() { result = node } + override NodeEx getNodeEx() { result = node.getNodeEx() } override FlowState getState() { result = state } @@ -2061,14 +2065,14 @@ module MakeImpl Lang> { bindingset[p, state, t, ap, stored] pragma[inline_late] private SummaryCtxSome mkSummaryCtxSome( - ParamNodeEx p, FlowState state, Typ t, Ap ap, TypOption stored + ParamNd p, FlowState state, Typ t, Ap ap, TypOption stored ) { result = TSummaryCtxSome(p, state, t, ap, stored) } pragma[nomagic] private predicate fwdFlowInStep( - ArgNodeEx arg, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc, + ArgNd arg, ParamNd p, FlowState state, Cc outercc, CcCall innercc, SummaryCtx outerSummaryCtx, SummaryCtx innerSummaryCtx, Typ t, Ap ap, TypOption stored ) { FwdFlowInNoThrough::fwdFlowIn(_, arg, _, p, state, outercc, innercc, outerSummaryCtx, t, @@ -2082,9 +2086,8 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowThroughStep0( - DataFlowCall call, ArgNodeEx arg, Cc cc, FlowState state, CcCall ccc, - SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, RetNodeEx ret, - SummaryCtxSome innerSummaryCtx + DataFlowCall call, ArgNd arg, Cc cc, FlowState state, CcCall ccc, SummaryCtx summaryCtx, + Typ t, Ap ap, TypOption stored, RetNd ret, SummaryCtxSome innerSummaryCtx ) { fwdFlowThrough0(call, arg, cc, state, ccc, summaryCtx, t, ap, stored, ret, innerSummaryCtx) @@ -2093,15 +2096,13 @@ module MakeImpl Lang> { bindingset[node, state, cc, summaryCtx, t, ap, stored] pragma[inline_late] private PathNodeImpl mkPathNode( - NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, - TypOption stored + Nd node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored ) { result = TPathNodeMid(node, state, cc, summaryCtx, t, ap, stored) } private PathNodeImpl typeStrengthenToPathNode( - NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, - TypOption stored + Nd node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, TypOption stored ) { exists(Typ t | fwdFlow1(node, state, cc, summaryCtx, t0, t, ap, stored) and @@ -2112,11 +2113,11 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowThroughStep1( PathNodeImpl pn1, PathNodeImpl pn2, PathNodeImpl pn3, DataFlowCall call, Cc cc, - FlowState state, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, RetNodeEx ret + FlowState state, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, RetNd ret ) { exists( - FlowState state0, ArgNodeEx arg, SummaryCtxSome innerSummaryCtx, ParamNodeEx p, - Typ innerArgT, Ap innerArgAp, TypOption innerArgStored, CcCall ccc + FlowState state0, ArgNd arg, SummaryCtxSome innerSummaryCtx, ParamNd p, Typ innerArgT, + Ap innerArgAp, TypOption innerArgStored, CcCall ccc | fwdFlowThroughStep0(call, arg, cc, state, ccc, summaryCtx, t, ap, stored, ret, innerSummaryCtx) and @@ -2131,10 +2132,10 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowThroughStep2( - PathNodeImpl pn1, PathNodeImpl pn2, PathNodeImpl pn3, NodeEx node, Cc cc, - FlowState state, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored + PathNodeImpl pn1, PathNodeImpl pn2, PathNodeImpl pn3, Nd node, Cc cc, FlowState state, + SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored ) { - exists(DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow | + exists(DataFlowCall call, RetNd ret, boolean allowsFieldFlow | fwdFlowThroughStep1(pn1, pn2, pn3, call, cc, state, summaryCtx, t, ap, stored, ret) and flowThroughOutOfCall(call, ret, node, allowsFieldFlow) and not inBarrier(node, state) and @@ -2143,10 +2144,10 @@ module MakeImpl Lang> { } private predicate localStep( - PathNodeImpl pn1, NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, - Ap ap, TypOption stored, string label, boolean isStoreStep + PathNodeImpl pn1, Nd node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, + TypOption stored, string label, boolean isStoreStep ) { - exists(NodeEx mid, FlowState state0, Typ t0, LocalCc localCc | + exists(Nd mid, FlowState state0, Typ t0, LocalCc localCc | pn1 = TPathNodeMid(mid, state0, cc, summaryCtx, t0, ap, stored) and localCc = getLocalCc(cc) and isStoreStep = false @@ -2159,7 +2160,7 @@ module MakeImpl Lang> { ) or // store - exists(NodeEx mid, Content c, Typ t0, Ap ap0, TypOption stored0 | + exists(Nd mid, Content c, Typ t0, Ap ap0, TypOption stored0 | pn1 = TPathNodeMid(mid, state, cc, summaryCtx, t0, ap0, stored0) and fwdFlowStore(mid, t0, ap0, stored0, c, t, stored, node, state, cc, summaryCtx) and ap = apCons(c, ap0) and @@ -2168,7 +2169,7 @@ module MakeImpl Lang> { ) or // read - exists(NodeEx mid, Typ t0, Ap ap0, TypOption stored0 | + exists(Nd mid, Typ t0, Ap ap0, TypOption stored0 | pn1 = TPathNodeMid(mid, state, cc, summaryCtx, t0, ap0, stored0) and fwdFlowRead(mid, t0, ap0, stored0, _, node, t, ap, stored, state, cc, summaryCtx) and label = "" and @@ -2178,7 +2179,7 @@ module MakeImpl Lang> { private predicate localStep(PathNodeImpl pn1, PathNodeImpl pn2, string label) { exists( - NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, + Nd node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, TypOption stored, boolean isStoreStep | localStep(pn1, node, state, cc, summaryCtx, t0, ap, stored, label, isStoreStep) and @@ -2209,11 +2210,11 @@ module MakeImpl Lang> { } private predicate nonLocalStep( - PathNodeImpl pn1, NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, - Ap ap, TypOption stored, string label + PathNodeImpl pn1, Nd node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, + TypOption stored, string label ) { // jump - exists(NodeEx mid, FlowState state0, Typ t0 | + exists(Nd mid, FlowState state0, Typ t0 | pn1 = TPathNodeMid(mid, state0, _, _, t0, ap, stored) and cc = ccNone() and summaryCtx = TSummaryCtxNone() @@ -2238,7 +2239,7 @@ module MakeImpl Lang> { ) or // flow into a callable - exists(ArgNodeEx arg, Cc outercc, SummaryCtx outerSummaryCtx | + exists(ArgNd arg, Cc outercc, SummaryCtx outerSummaryCtx | pn1 = TPathNodeMid(arg, state, outercc, outerSummaryCtx, t, ap, stored) and fwdFlowInStep(arg, node, state, outercc, cc, outerSummaryCtx, summaryCtx, t, ap, stored) and @@ -2246,7 +2247,7 @@ module MakeImpl Lang> { ) or // flow out of a callable - exists(RetNodeEx ret, CcNoCall innercc, boolean allowsFieldFlow | + exists(RetNd ret, CcNoCall innercc, boolean allowsFieldFlow | pn1 = TPathNodeMid(ret, state, innercc, summaryCtx, t, ap, stored) and fwdFlowIntoRet(ret, state, innercc, summaryCtx, t, ap, stored) and fwdFlowOutValidEdge(_, ret, innercc, _, node, cc, allowsFieldFlow) and @@ -2258,7 +2259,7 @@ module MakeImpl Lang> { private predicate nonLocalStep(PathNodeImpl pn1, PathNodeImpl pn2, string label) { exists( - NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, + Nd node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, TypOption stored | nonLocalStep(pn1, node, state, cc, summaryCtx, t0, ap, stored, label) and @@ -2276,7 +2277,7 @@ module MakeImpl Lang> { PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out ) { exists( - NodeEx node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, + Nd node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, TypOption stored, PathNodeImpl out0 | fwdFlowThroughStep2(arg, par, ret, node, cc, state, summaryCtx, t0, ap, stored) and @@ -2552,12 +2553,12 @@ module MakeImpl Lang> { int tfnodes, int tftuples ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, _)) and + nodes = count(NodeEx node | fwdFlow(any(Nd n | n.getNodeEx() = node), _, _, _, _, _, _)) and fields = count(Content f0 | fwdConsCand(f0, _)) and conscand = count(Content f0, Ap ap | fwdConsCand(f0, ap)) and states = count(FlowState state | fwdFlow(_, state, _, _, _, _, _)) and tuples = - count(NodeEx n, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, + count(Nd n, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored | fwdFlow(n, state, cc, summaryCtx, t, ap, stored)) and calledges = count(DataFlowCall call, DataFlowCallable c | @@ -2567,12 +2568,12 @@ module MakeImpl Lang> { FwdTypeFlow::typeFlowStats(tfnodes, tftuples) or fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _)) and + nodes = count(NodeEx node | revFlow(any(Nd n | n.getNodeEx() = node), _, _, _, _)) and fields = count(Content f0 | consCand(f0, _)) and conscand = count(Content f0, Ap ap | consCand(f0, ap)) and states = count(FlowState state | revFlow(_, state, _, _, _)) and tuples = - count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | + count(Nd n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | revFlow(n, state, returnCtx, retAp, ap) ) and calledges = @@ -2677,8 +2678,8 @@ module MakeImpl Lang> { bindingset[node1, state1] bindingset[node2, state2] predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - Typ t, LocalCc lcc, string label + Nd node1, FlowState state1, Nd node2, FlowState state2, boolean preservesValue, Typ t, + LocalCc lcc, string label ) { ( localStepNodeCand1(node1, node2, preservesValue, _, _, label) and @@ -2692,7 +2693,7 @@ module MakeImpl Lang> { } pragma[nomagic] - private predicate expectsContentCand(NodeEx node) { + private predicate expectsContentCand(Nd node) { exists(Content c | PrevStage::revFlow(node) and PrevStage::revFlowIsReadAndStored(c) and @@ -2701,7 +2702,7 @@ module MakeImpl Lang> { } bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { + predicate filter(Nd node, FlowState state, Typ t0, Ap ap, Typ t) { PrevStage::revFlowState(state) and t0 = t and exists(ap) and @@ -2715,7 +2716,7 @@ module MakeImpl Lang> { } bindingset[node, ap, isStoreStep] - predicate stepFilter(NodeEx node, Ap ap, boolean isStoreStep) { any() } + predicate stepFilter(Nd node, Ap ap, boolean isStoreStep) { any() } bindingset[t1, t2] predicate typecheck(Typ t1, Typ t2) { any() } @@ -2772,7 +2773,7 @@ module MakeImpl Lang> { bindingset[node1, state1] bindingset[node2, state2] private predicate localStepInput( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + Nd node1, FlowState state1, Nd node2, FlowState state2, boolean preservesValue, DataFlowType t, LocalCallContext lcc, string label ) { localStepNodeCand1(node1, node2, preservesValue, t, lcc, label) and @@ -2783,7 +2784,7 @@ module MakeImpl Lang> { } additional predicate localFlowBigStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + Nd node1, FlowState state1, Nd node2, FlowState state2, boolean preservesValue, DataFlowType t, LocalCallContext lcc, string label ) { PrevStage::LocalFlowBigStep::localFlowBigStep(node1, state1, node2, @@ -2793,8 +2794,8 @@ module MakeImpl Lang> { bindingset[node1, state1] bindingset[node2, state2] predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - Typ t, LocalCc lcc, string label + Nd node1, FlowState state1, Nd node2, FlowState state2, boolean preservesValue, Typ t, + LocalCc lcc, string label ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, _, _, label) and exists(t) and @@ -2802,7 +2803,7 @@ module MakeImpl Lang> { } pragma[nomagic] - private predicate expectsContentCand(NodeEx node, Ap ap) { + private predicate expectsContentCand(Nd node, Ap ap) { exists(Content c | PrevStage::revFlow(node) and PrevStage::readStepCand(_, c, _) and @@ -2812,7 +2813,7 @@ module MakeImpl Lang> { } bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { + predicate filter(Nd node, FlowState state, Typ t0, Ap ap, Typ t) { exists(state) and t0 = t and ( @@ -2823,7 +2824,7 @@ module MakeImpl Lang> { } bindingset[node, ap, isStoreStep] - predicate stepFilter(NodeEx node, Ap ap, boolean isStoreStep) { any() } + predicate stepFilter(Nd node, Ap ap, boolean isStoreStep) { any() } bindingset[t1, t2] predicate typecheck(Typ t1, Typ t2) { any() } @@ -2832,8 +2833,8 @@ module MakeImpl Lang> { private module Stage3 = MkStage::Stage; bindingset[node, t0] - private predicate strengthenType(NodeEx node, DataFlowType t0, DataFlowType t) { - if node instanceof CastingNodeEx + private predicate strengthenType(Nd node, DataFlowType t0, DataFlowType t) { + if node instanceof CastingNd then exists(DataFlowType nt | nt = node.getDataFlowType() | if typeStrongerThanFilter(nt, t0) @@ -2878,8 +2879,8 @@ module MakeImpl Lang> { pragma[nomagic] predicate localStep( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, - Typ t, LocalCc lcc, string label + Nd node1, FlowState state1, Nd node2, FlowState state2, boolean preservesValue, Typ t, + LocalCc lcc, string label ) { Stage3Param::localFlowBigStep(node1, state1, node2, state2, preservesValue, _, _, label) and PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and @@ -2889,13 +2890,13 @@ module MakeImpl Lang> { } pragma[nomagic] - private predicate clearSet(NodeEx node, ContentSet c) { + private predicate clearSet(Nd node, ContentSet c) { PrevStage::revFlow(node) and - clearsContentSet(node, c) + clearsContentSet(node.getNodeEx(), c) } pragma[nomagic] - additional predicate clearContent(NodeEx node, Content c, boolean isStoreTarget) { + additional predicate clearContent(Nd node, Content c, boolean isStoreTarget) { exists(ContentSet cs | PrevStage::readStepCand(_, pragma[only_bind_into](c), _) and c = cs.getAReadContent() and @@ -2907,7 +2908,7 @@ module MakeImpl Lang> { } pragma[nomagic] - private predicate clear(NodeEx node, Ap ap) { + private predicate clear(Nd node, Ap ap) { // When `node` is the target of a store, we interpret `clearsContent` as // only pertaining to _earlier_ store steps. In this case, we need to postpone // checking `clearsContent` to the step creation. @@ -2915,12 +2916,10 @@ module MakeImpl Lang> { } pragma[nomagic] - private predicate clearExceptStore(NodeEx node, Ap ap) { - clearContent(node, ap.getHead(), true) - } + private predicate clearExceptStore(Nd node, Ap ap) { clearContent(node, ap.getHead(), true) } pragma[nomagic] - private predicate expectsContentCand(NodeEx node, Ap ap) { + private predicate expectsContentCand(Nd node, Ap ap) { exists(Content c | PrevStage::revFlow(node) and PrevStage::readStepCand(_, c, _) and @@ -2930,7 +2929,7 @@ module MakeImpl Lang> { } bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { + predicate filter(Nd node, FlowState state, Typ t0, Ap ap, Typ t) { exists(state) and not clear(node, ap) and t0 = t and @@ -2942,7 +2941,7 @@ module MakeImpl Lang> { } bindingset[node, ap, isStoreStep] - predicate stepFilter(NodeEx node, Ap ap, boolean isStoreStep) { + predicate stepFilter(Nd node, Ap ap, boolean isStoreStep) { if clearExceptStore(node, ap) then isStoreStep = true else any() } @@ -2960,7 +2959,7 @@ module MakeImpl Lang> { exists(int tails, int nodes, int apLimit, int tupleLimit | tails = strictcount(AccessPathFront apf | Stage4::consCand(c, apf)) and nodes = - strictcount(NodeEx n, FlowState state | + strictcount(Nd n, FlowState state | Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) or Stage4::nodeMayUseSummary(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) @@ -3167,19 +3166,19 @@ module MakeImpl Lang> { PrevStage::LocalFlowBigStep::localFlowBigStepTc/8; bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { + predicate filter(Nd node, FlowState state, Typ t0, Ap ap, Typ t) { strengthenType(node, t0, t) and exists(state) and exists(ap) } pragma[nomagic] - private predicate clearExceptStore(NodeEx node, Ap ap) { + private predicate clearExceptStore(Nd node, Ap ap) { Stage4Param::clearContent(node, ap.getHead(), true) } bindingset[node, ap, isStoreStep] - predicate stepFilter(NodeEx node, Ap ap, boolean isStoreStep) { + predicate stepFilter(Nd node, Ap ap, boolean isStoreStep) { if clearExceptStore(node, ap) then isStoreStep = true else any() } @@ -3207,7 +3206,7 @@ module MakeImpl Lang> { private int countNodesUsingAccessPath(AccessPathApprox apa) { result = - strictcount(NodeEx n, FlowState state | + strictcount(Nd n, FlowState state | Stage5::revFlow(n, state, apa) or Stage5::nodeMayUseSummary(n, state, apa) ) } @@ -3364,19 +3363,19 @@ module MakeImpl Lang> { PrevStage::LocalFlowBigStep::localFlowBigStepTc/8; bindingset[node, state, t0, ap] - predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) { + predicate filter(Nd node, FlowState state, Typ t0, Ap ap, Typ t) { strengthenType(node, t0, t) and exists(state) and exists(ap) } pragma[nomagic] - private predicate clearExceptStore(NodeEx node, Ap ap) { + private predicate clearExceptStore(Nd node, Ap ap) { Stage4Param::clearContent(node, ap.getHead(), true) } bindingset[node, ap, isStoreStep] - predicate stepFilter(NodeEx node, Ap ap, boolean isStoreStep) { + predicate stepFilter(Nd node, Ap ap, boolean isStoreStep) { if clearExceptStore(node, ap) then isStoreStep = true else any() } diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll index 6ebb914e3def..f54412d8fd85 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll @@ -853,6 +853,8 @@ module MakeImplCommon Lang> { class SndLevelScopeOption = SndLevelScopeOption::Option; final class NodeEx extends TNodeEx { + NodeEx getNodeEx() { result = this } + string toString() { result = this.asNode().toString() or diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll index b2142cfbe83b..7f1faf6d3c65 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll @@ -24,77 +24,100 @@ module MakeImplStage1 Lang> { bindingset[source, sink] predicate isRelevantSourceSinkPair(Node source, Node sink); - predicate inBarrier(NodeEx node, FlowState state); + class Nd { + NodeEx getNodeEx(); - predicate outBarrier(NodeEx node, FlowState state); + string toString(); - predicate stateBarrier(NodeEx node, FlowState state); + Location getLocation(); + + DataFlowType getDataFlowType(); + + DataFlowCallable getEnclosingCallable(); + } + + class ArgNd extends Nd; + + class ParamNd extends Nd; + + class RetNd extends Nd { + ReturnPosition getReturnPosition(); + + ReturnKindExt getKind(); + } + + class OutNd extends Nd; + + class CastingNd extends Nd; + + predicate inBarrier(Nd node, FlowState state); + + predicate outBarrier(Nd node, FlowState state); + + predicate stateBarrier(Nd node, FlowState state); /** If `node` corresponds to a sink, gets the normal node for that sink. */ - NodeEx toNormalSinkNode(NodeEx node); + Nd toNormalSinkNode(Nd node); - predicate sourceNode(NodeEx node, FlowState state); + predicate sourceNode(Nd node, FlowState state); - predicate sinkNode(NodeEx node, FlowState state); + predicate sinkNode(Nd node, FlowState state); predicate hasSourceCallCtx(); predicate hasSinkCallCtx(); - predicate jumpStepEx(NodeEx node1, NodeEx node2); + predicate jumpStepEx(Nd node1, Nd node2); - predicate additionalJumpStep(NodeEx node1, NodeEx node2, string model); + predicate additionalJumpStep(Nd node1, Nd node2, string model); - predicate additionalJumpStateStep( - NodeEx node1, FlowState s1, NodeEx node2, FlowState s2, string model - ); + predicate additionalJumpStateStep(Nd node1, FlowState s1, Nd node2, FlowState s2, string model); predicate localStepNodeCand1( - NodeEx node1, NodeEx node2, boolean preservesValue, DataFlowType t, LocalCallContext lcc, - string label + Nd node1, Nd node2, boolean preservesValue, DataFlowType t, LocalCallContext lcc, string label ); predicate localStateStepNodeCand1( - NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, DataFlowType t, - LocalCallContext lcc, string label + Nd node1, FlowState state1, Nd node2, FlowState state2, DataFlowType t, LocalCallContext lcc, + string label ); bindingset[c] - predicate expectsContentEx(NodeEx n, Content c); + predicate expectsContentEx(Nd n, Content c); - predicate notExpectsContent(NodeEx n); + predicate notExpectsContent(Nd n); bindingset[p, kind] - predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind); + predicate parameterFlowThroughAllowed(ParamNd p, ReturnKindExt kind); // begin StageSig class Ap; class ApNil extends Ap; - predicate revFlow(NodeEx node); + predicate revFlow(Nd node); bindingset[node, state] - predicate revFlow(NodeEx node, FlowState state, Ap ap); + predicate revFlow(Nd node, FlowState state, Ap ap); predicate callMayFlowThroughRev(DataFlowCall call); - predicate parameterMayFlowThrough(ParamNodeEx p, boolean emptyAp); + predicate parameterMayFlowThrough(ParamNd p, boolean emptyAp); - predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind); + predicate returnMayFlowThrough(RetNd ret, ReturnKindExt kind); predicate storeStepCand( - NodeEx node1, Content c, NodeEx node2, DataFlowType contentType, DataFlowType containerType + Nd node1, Content c, Nd node2, DataFlowType contentType, DataFlowType containerType ); - predicate readStepCand(NodeEx n1, Content c, NodeEx n2); + predicate readStepCand(Nd n1, Content c, Nd n2); predicate callEdgeArgParam( - DataFlowCall call, DataFlowCallable c, ArgNodeEx arg, ParamNodeEx p, boolean emptyAp + DataFlowCall call, DataFlowCallable c, ArgNd arg, ParamNd p, boolean emptyAp ); predicate callEdgeReturn( - DataFlowCall call, DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, NodeEx out, + DataFlowCall call, DataFlowCallable c, RetNd ret, ReturnKindExt kind, Nd out, boolean allowsFieldFlow ); @@ -1221,6 +1244,18 @@ module MakeImplStage1 Lang> { private predicate localStateStepNodeCand1Alias = localStateStepNodeCand1/7; module Stage1NoState implements Stage1Output { + class Nd = NodeEx; + + class ArgNd = ArgNodeEx; + + class ParamNd = ParamNodeEx; + + class RetNd = RetNodeEx; + + class OutNd = OutNodeEx; + + class CastingNd = CastingNodeEx; + predicate inBarrier = inBarrierAlias/2; predicate outBarrier = outBarrierAlias/2; From 1166aa6a43734d7c4192f160631dc87bc6fe6624 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 30 Jan 2025 10:56:17 +0100 Subject: [PATCH 06/14] Dataflow: Prepare a (node,state) pair type. --- .../dataflow/internal/DataFlowImplStage1.qll | 259 +++++++++++++++++- 1 file changed, 247 insertions(+), 12 deletions(-) diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll index 7f1faf6d3c65..0475c9b1000f 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll @@ -470,10 +470,6 @@ module MakeImplStage1 Lang> { private module Stage1 { private import Stage1Common - class Ap = Unit; - - class ApNil = Ap; - private class Cc = boolean; /* Begin: Stage 1 logic. */ @@ -954,14 +950,6 @@ module MakeImplStage1 Lang> { c = ret.getEnclosingCallable() } - predicate relevantCallEdgeIn(DataFlowCall call, DataFlowCallable c) { - callEdgeArgParam(call, c, _, _, _) - } - - predicate relevantCallEdgeOut(DataFlowCall call, DataFlowCallable c) { - callEdgeReturn(call, c, _, _, _, _) - } - predicate stats( boolean fwd, int nodes, int fields, int conscand, int states, int tuples, int calledges ) { @@ -991,6 +979,10 @@ module MakeImplStage1 Lang> { private module Stage1Common { predicate isRelevantSourceSinkPair = SourceSinkFiltering::isRelevantSourceSinkPair/2; + class Ap = Unit; + + class ApNil = Ap; + predicate hasSourceCallCtx() { exists(FlowFeature feature | feature = Config::getAFeature() | feature instanceof FeatureHasSourceCallContext or @@ -1004,6 +996,20 @@ module MakeImplStage1 Lang> { feature instanceof FeatureEqualSourceSinkCallContext ) } + + predicate revFlowIsReadAndStored = Stage1::revFlowIsReadAndStored/1; + + predicate callMayFlowThroughRev = Stage1::callMayFlowThroughRev/1; + + predicate relevantCallEdgeIn(DataFlowCall call, DataFlowCallable c) { + Stage1::callEdgeArgParam(call, c, _, _, _) + } + + predicate relevantCallEdgeOut(DataFlowCall call, DataFlowCallable c) { + Stage1::callEdgeReturn(call, c, _, _, _, _) + } + + predicate stats = Stage1::stats/7; } pragma[nomagic] @@ -1294,6 +1300,235 @@ module MakeImplStage1 Lang> { predicate localStateStepNodeCand1 = localStateStepNodeCand1Alias/7; } + // TODO: implements Stage1Output + module Stage1WithState { + private predicate flowState(NodeEx node, FlowState state) { + Stage1::revFlow(node) and + Stage1::revFlowState(state) and + not stateBarrier(node, state) and + ( + sourceNode(node, state) + or + exists(NodeEx mid, FlowState state0 | flowState(mid, state0) | + additionalLocalStateStep(mid, state0, node, state, _) or + additionalJumpStateStep(mid, state0, node, state, _) + ) + or + exists(NodeEx mid | flowState(mid, state) | + localFlowStepEx(mid, node, _) or + additionalLocalFlowStep(mid, node, _) or + jumpStepExAlias(mid, node) or + additionalJumpStepAlias(mid, node, _) or + store(mid, _, node, _, _) or + readSetEx(mid, _, node) or + flowIntoCallNodeCand1(_, mid, node) or + flowOutOfCallNodeCand1(_, mid, _, node) + ) + ) + } + + private newtype TNd = TNodeState(NodeEx node, FlowState state) { flowState(node, state) } + + class Nd extends TNd { + NodeEx node; + + Nd() { this = TNodeState(node, _) } + + NodeEx getNodeEx() { result = node } + + FlowState getState() { this = TNodeState(_, result) } + + string toString() { result = node.toString() } + + Location getLocation() { result = node.getLocation() } + + DataFlowType getDataFlowType() { result = node.getDataFlowType() } + + DataFlowCallable getEnclosingCallable() { result = node.getEnclosingCallable() } + } + + class ArgNd extends Nd { + ArgNd() { node instanceof ArgNodeEx } + } + + class ParamNd extends Nd { + ParamNd() { node instanceof ParamNodeEx } + } + + class RetNd extends Nd { + override RetNodeEx node; + + ReturnPosition getReturnPosition() { result = node.getReturnPosition() } + + ReturnKindExt getKind() { result = node.getKind() } + } + + class OutNd extends Nd { + OutNd() { node instanceof OutNodeEx } + } + + class CastingNd extends Nd { + CastingNd() { node instanceof CastingNodeEx } + } + + // inline to reduce fan-out via `getAReadContent` + bindingset[c] + predicate expectsContentEx(Nd n, Content c) { + Stage1NoState::expectsContentEx(n.getNodeEx(), c) + } + + pragma[nomagic] + predicate notExpectsContent(Nd n) { Stage1NoState::notExpectsContent(n.getNodeEx()) } + + bindingset[p, kind] + pragma[inline_late] + predicate parameterFlowThroughAllowed(ParamNd p, ReturnKindExt kind) { + parameterFlowThroughAllowedEx(p.getNodeEx(), kind) + } + + import Stage1Common + + predicate revFlow(Nd node) { Stage1::revFlow(node.getNodeEx()) } + + predicate revFlow(Nd node, Ap ap) { Stage1::revFlow(node.getNodeEx()) and exists(ap) } + + predicate parameterMayFlowThrough(ParamNd p, boolean emptyAp) { + Stage1::parameterMayFlowThrough(p.getNodeEx(), emptyAp) + } + + predicate returnMayFlowThrough(RetNd ret, ReturnKindExt kind) { + Stage1::returnMayFlowThrough(ret.getNodeEx(), kind) + } + + pragma[nomagic] + predicate storeStepCand( + Nd node1, Content c, Nd node2, DataFlowType contentType, DataFlowType containerType + ) { + exists(NodeEx n1, NodeEx n2, FlowState s | + Stage1::storeStepCand(n1, c, n2, contentType, containerType) and + node1 = TNodeState(n1, pragma[only_bind_into](s)) and + node2 = TNodeState(n2, pragma[only_bind_into](s)) and + not outBarrier(n1, s) and + not inBarrier(n2, s) + ) + } + + pragma[nomagic] + predicate readStepCand(Nd node1, Content c, Nd node2) { + exists(NodeEx n1, NodeEx n2, FlowState s | + Stage1::readStepCand(n1, c, n2) and + node1 = TNodeState(n1, pragma[only_bind_into](s)) and + node2 = TNodeState(n2, pragma[only_bind_into](s)) and + not outBarrier(n1, s) and + not inBarrier(n2, s) + ) + } + + predicate callEdgeArgParam( + DataFlowCall call, DataFlowCallable c, ArgNd arg, ParamNd p, boolean emptyAp + ) { + exists(ArgNodeEx arg0, ParamNodeEx p0, FlowState s | + Stage1::callEdgeArgParam(call, c, arg0, p0, emptyAp) and + arg = TNodeState(arg0, pragma[only_bind_into](s)) and + p = TNodeState(p0, pragma[only_bind_into](s)) and + not outBarrier(arg0, s) and + not inBarrier(p0, s) + ) + } + + predicate callEdgeReturn( + DataFlowCall call, DataFlowCallable c, RetNd ret, ReturnKindExt kind, Nd out, + boolean allowsFieldFlow + ) { + exists(RetNodeEx ret0, NodeEx out0, FlowState s | + Stage1::callEdgeReturn(call, c, ret0, kind, out0, allowsFieldFlow) and + ret = TNodeState(ret0, pragma[only_bind_into](s)) and + out = TNodeState(out0, pragma[only_bind_into](s)) and + not outBarrier(ret0, s) and + not inBarrier(out0, s) + ) + } + + /** If `node` corresponds to a sink, gets the normal node for that sink. */ + Nd toNormalSinkNode(Nd node) { + exists(NodeEx res, NodeEx n, FlowState s | + res = toNormalSinkNodeEx(n) and + node = TNodeState(n, pragma[only_bind_into](s)) and + result = TNodeState(res, pragma[only_bind_into](s)) + ) + } + + predicate sourceNode(Nd node) { + exists(NodeEx n, FlowState state | + sourceNode(n, state) and + node = TNodeState(n, state) + ) + } + + predicate sinkNode(Nd node) { + exists(NodeEx n, FlowState state | + Stage1::sinkNode(n, state) and + node = TNodeState(n, state) + ) + } + + predicate jumpStepEx(Nd node1, Nd node2) { + exists(NodeEx n1, NodeEx n2, FlowState s | + jumpStepExAlias(n1, n2) and + node1 = TNodeState(n1, pragma[only_bind_into](s)) and + node2 = TNodeState(n2, pragma[only_bind_into](s)) and + not outBarrier(n1, s) and + not inBarrier(n2, s) + ) + } + + predicate additionalJumpStep(Nd node1, Nd node2, string model) { + exists(NodeEx n1, NodeEx n2, FlowState s | + additionalJumpStepAlias(n1, n2, model) and + node1 = TNodeState(n1, pragma[only_bind_into](s)) and + node2 = TNodeState(n2, pragma[only_bind_into](s)) and + not outBarrier(n1, s) and + not inBarrier(n2, s) + ) + or + exists(NodeEx n1, FlowState s1, NodeEx n2, FlowState s2 | + additionalJumpStateStep(n1, s1, n2, s2, model) and + node1 = TNodeState(n1, s1) and + node2 = TNodeState(n2, s2) + ) + } + + pragma[nomagic] + predicate localStep1( + Nd node1, Nd node2, boolean preservesValue, DataFlowType t, LocalCallContext lcc, + string label + ) { + exists(NodeEx n1, NodeEx n2, FlowState s | + localStepNodeCand1(n1, n2, preservesValue, t, lcc, label) and + node1 = TNodeState(n1, pragma[only_bind_into](s)) and + node2 = TNodeState(n2, pragma[only_bind_into](s)) and + not outBarrier(n1, s) and + not inBarrier(n2, s) + ) + or + exists(NodeEx n1, NodeEx n2, FlowState s1, FlowState s2 | + localStateStepNodeCand1(n1, s1, n2, s2, t, lcc, label) and + preservesValue = false and + node1 = TNodeState(n1, s1) and + node2 = TNodeState(n2, s2) + ) + } + + predicate isStateStep(Nd node1, Nd node2) { + exists(NodeEx n1, NodeEx n2, FlowState s1, FlowState s2 | + localStateStepNodeCand1(n1, s1, n2, s2, _, _, _) and + s1 != s2 and + node1 = TNodeState(n1, s1) and + node2 = TNodeState(n2, s2) + ) + } + } + private signature predicate flag(); private predicate flagEnable() { any() } From b4197b08aa4f3372e2f3cf19f97824f16a9ef6d9 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 30 Jan 2025 11:48:23 +0100 Subject: [PATCH 07/14] Dataflow: Use (node,state) pair as node type in stage 2+. --- shared/dataflow/codeql/dataflow/DataFlow.qll | 2 +- .../codeql/dataflow/TaintTracking.qll | 6 +- .../codeql/dataflow/internal/DataFlowImpl.qll | 795 ++++++++---------- .../dataflow/internal/DataFlowImplCommon.qll | 2 + .../dataflow/internal/DataFlowImplStage1.qll | 70 +- 5 files changed, 363 insertions(+), 512 deletions(-) diff --git a/shared/dataflow/codeql/dataflow/DataFlow.qll b/shared/dataflow/codeql/dataflow/DataFlow.qll index b74ea9a0e082..0d78d13c8848 100644 --- a/shared/dataflow/codeql/dataflow/DataFlow.qll +++ b/shared/dataflow/codeql/dataflow/DataFlow.qll @@ -734,7 +734,7 @@ module DataFlowMake Lang> { import Stage1::PartialFlow - private module Flow = Impl; + private module Flow = Impl; import Flow } diff --git a/shared/dataflow/codeql/dataflow/TaintTracking.qll b/shared/dataflow/codeql/dataflow/TaintTracking.qll index 762583528d48..b08f1e4af469 100644 --- a/shared/dataflow/codeql/dataflow/TaintTracking.qll +++ b/shared/dataflow/codeql/dataflow/TaintTracking.qll @@ -134,7 +134,7 @@ module TaintFlowMake< import Stage1::PartialFlow - private module Flow = DataFlowInternal::Impl; + private module Flow = DataFlowInternal::Impl; import Flow } @@ -236,7 +236,7 @@ module TaintFlowMake< import Stage1::PartialFlow - private module Flow = DataFlowInternal::Impl; + private module Flow = DataFlowInternal::Impl; import Flow } @@ -274,7 +274,7 @@ module TaintFlowMake< import Stage1::PartialFlow - private module Flow = DataFlowInternal::Impl; + private module Flow = DataFlowInternal::Impl; import Flow } diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll index 6bf5cb894f6f..b271694e1609 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll @@ -186,17 +186,11 @@ module MakeImpl Lang> { private class CastingNd = Stage1::CastingNd; - private predicate inBarrier = Stage1::inBarrier/2; - - private predicate outBarrier = Stage1::outBarrier/2; - - private predicate stateBarrier = Stage1::stateBarrier/2; - private predicate toNormalSinkNode = Stage1::toNormalSinkNode/1; - private predicate sourceNode = Stage1::sourceNode/2; + private predicate sourceNode = Stage1::sourceNode/1; - private predicate sinkNode = Stage1::sinkNode/2; + private predicate sinkNode = Stage1::sinkNode/1; private predicate hasSourceCallCtx = Stage1::hasSourceCallCtx/0; @@ -206,14 +200,12 @@ module MakeImpl Lang> { private predicate additionalJumpStep = Stage1::additionalJumpStep/3; - private predicate additionalJumpStateStep = Stage1::additionalJumpStateStep/5; + private predicate localStep1 = Stage1::localStep1/6; - private predicate localStepNodeCand1 = Stage1::localStepNodeCand1/6; - - private predicate localStateStepNodeCand1 = Stage1::localStateStepNodeCand1/7; + private predicate isStateStep = Stage1::isStateStep/2; private predicate sourceModel(Nd n, string model) { - exists(NodeEx node | sourceNode(n, _) and node = n.getNodeEx() | + exists(NodeEx node | sourceNode(n) and node = n.getNodeEx() | model = getSourceModel(node) or not exists(getSourceModel(node)) and model = "" @@ -221,7 +213,7 @@ module MakeImpl Lang> { } private predicate sinkModel(Nd n, string model) { - exists(NodeEx node | sinkNode(n, _) and node = n.getNodeEx() | + exists(NodeEx node | sinkNode(n) and node = n.getNodeEx() | model = getSinkModel(node) or not exists(getSinkModel(node)) and model = "" @@ -254,8 +246,8 @@ module MakeImpl Lang> { predicate revFlow(Nd node); - bindingset[node, state] - predicate revFlow(Nd node, FlowState state, Ap ap); + bindingset[node] + predicate revFlow(Nd node, Ap ap); predicate callMayFlowThroughRev(DataFlowCall call); @@ -369,15 +361,14 @@ module MakeImpl Lang> { bindingset[cc] LocalCc getLocalCc(Cc cc); - bindingset[node1, state1] - bindingset[node2, state2] + bindingset[node1] + bindingset[node2] predicate localStep( - Nd node1, FlowState state1, Nd node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc, string label + Nd node1, Nd node2, boolean preservesValue, Typ t, LocalCc lcc, string label ); - bindingset[node, state, t0, ap] - predicate filter(Nd node, FlowState state, Typ t0, Ap ap, Typ t); + bindingset[node, t0, ap] + predicate filter(Nd node, Typ t0, Ap ap, Typ t); bindingset[node, ap, isStoreStep] predicate stepFilter(Nd node, Ap ap, boolean isStoreStep); @@ -457,19 +448,18 @@ module MakeImpl Lang> { */ pragma[nomagic] additional predicate fwdFlow( - Nd node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored + Nd node, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored ) { - fwdFlow1(node, state, cc, summaryCtx, _, t, ap, stored) + fwdFlow1(node, cc, summaryCtx, _, t, ap, stored) } private predicate fwdFlow1( - Nd node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Typ t, Ap ap, - TypOption stored + Nd node, Cc cc, SummaryCtx summaryCtx, Typ t0, Typ t, Ap ap, TypOption stored ) { exists(ApApprox apa | - fwdFlow0(node, state, cc, summaryCtx, t0, ap, apa, stored) and - PrevStage::revFlow(node, state, apa) and - filter(node, state, t0, ap, t) and + fwdFlow0(node, cc, summaryCtx, t0, ap, apa, stored) and + PrevStage::revFlow(node, apa) and + filter(node, t0, ap, t) and ( if node instanceof CastingNd then @@ -483,10 +473,9 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlow0( - Nd node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, ApApprox apa, - TypOption stored + Nd node, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, ApApprox apa, TypOption stored ) { - sourceNode(node, state) and + sourceNode(node) and (if hasSourceCallCtx() then cc = ccSomeCall() else cc = ccNone()) and summaryCtx = TSummaryCtxNone() and t = getNodeTyp(node) and @@ -494,36 +483,36 @@ module MakeImpl Lang> { apa = getApprox(ap) and stored.isNone() or - exists(Nd mid, FlowState state0, Typ t0, LocalCc localCc | - fwdFlow(mid, state0, cc, summaryCtx, t0, ap, stored) and + exists(Nd mid, Typ t0, LocalCc localCc | + fwdFlow(mid, cc, summaryCtx, t0, ap, stored) and apa = getApprox(ap) and localCc = getLocalCc(cc) | - localStep(mid, state0, node, state, true, _, localCc, _) and + localStep(mid, node, true, _, localCc, _) and t = t0 or - localStep(mid, state0, node, state, false, t, localCc, _) and + localStep(mid, node, false, t, localCc, _) and ap instanceof ApNil ) or - fwdFlowJump(node, state, t, ap, stored) and + fwdFlowJump(node, t, ap, stored) and apa = getApprox(ap) and cc = ccNone() and summaryCtx = TSummaryCtxNone() or // store exists(Content c, Ap ap0 | - fwdFlowStore(_, _, ap0, _, c, t, stored, node, state, cc, summaryCtx) and + fwdFlowStore(_, _, ap0, _, c, t, stored, node, cc, summaryCtx) and ap = apCons(c, ap0) and apa = getApprox(ap) ) or // read - fwdFlowRead(_, _, _, _, _, node, t, ap, stored, state, cc, summaryCtx) and + fwdFlowRead(_, _, _, _, _, node, t, ap, stored, cc, summaryCtx) and apa = getApprox(ap) or // flow into a callable without summary context - fwdFlowInNoFlowThrough(node, state, cc, t, ap, stored) and + fwdFlowInNoFlowThrough(node, cc, t, ap, stored) and apa = getApprox(ap) and summaryCtx = TSummaryCtxNone() and // When the call contexts of source and sink needs to match then there's @@ -532,28 +521,27 @@ module MakeImpl Lang> { not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext or // flow into a callable with summary context (non-linear recursion) - fwdFlowInFlowThrough(node, state, cc, t, ap, stored) and + fwdFlowInFlowThrough(node, cc, t, ap, stored) and apa = getApprox(ap) and - summaryCtx = TSummaryCtxSome(node, state, t, ap, stored) + summaryCtx = TSummaryCtxSome(node, t, ap, stored) or // flow out of a callable - fwdFlowOut(_, _, node, state, cc, summaryCtx, t, ap, stored) and + fwdFlowOut(_, _, node, cc, summaryCtx, t, ap, stored) and apa = getApprox(ap) or // flow through a callable exists(DataFlowCall call, RetNd ret, boolean allowsFieldFlow | - fwdFlowThrough(call, cc, state, summaryCtx, t, ap, stored, ret) and + fwdFlowThrough(call, cc, summaryCtx, t, ap, stored, ret) and flowThroughOutOfCall(call, ret, node, allowsFieldFlow) and apa = getApprox(ap) and - not inBarrier(node, state) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParamNd p, FlowState state, Typ t, Ap ap, TypOption stored) { - fwdFlowInFlowThrough(p, state, _, t, ap, stored) + TSummaryCtxSome(ParamNd p, Typ t, Ap ap, TypOption stored) { + fwdFlowInFlowThrough(p, _, t, ap, stored) } /** @@ -578,12 +566,11 @@ module MakeImpl Lang> { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private ParamNd p; - private FlowState state; private Typ t; private Ap ap; private TypOption stored; - SummaryCtxSome() { this = TSummaryCtxSome(p, state, t, ap, stored) } + SummaryCtxSome() { this = TSummaryCtxSome(p, t, ap, stored) } ParamNd getParamNode() { result = p } @@ -596,36 +583,27 @@ module MakeImpl Lang> { override Location getLocation() { result = p.getLocation() } } - private predicate fwdFlowJump(Nd node, FlowState state, Typ t, Ap ap, TypOption stored) { + private predicate fwdFlowJump(Nd node, Typ t, Ap ap, TypOption stored) { exists(Nd mid | - fwdFlow(mid, state, _, _, t, ap, stored) and + fwdFlow(mid, _, _, t, ap, stored) and jumpStepEx(mid, node) ) or exists(Nd mid | - fwdFlow(mid, state, _, _, _, ap, stored) and + fwdFlow(mid, _, _, _, ap, stored) and additionalJumpStep(mid, node, _) and t = getNodeTyp(node) and ap instanceof ApNil ) - or - exists(Nd mid, FlowState state0 | - fwdFlow(mid, state0, _, _, _, ap, stored) and - additionalJumpStateStep(mid, state0, node, state, _) and - t = getNodeTyp(node) and - ap instanceof ApNil - ) } pragma[nomagic] private predicate fwdFlowStore( Nd node1, Typ t1, Ap ap1, TypOption stored1, Content c, Typ t2, TypOption stored2, - Nd node2, FlowState state, Cc cc, SummaryCtx summaryCtx + Nd node2, Cc cc, SummaryCtx summaryCtx ) { exists(DataFlowType contentType, DataFlowType containerType | - fwdFlow(node1, state, cc, summaryCtx, t1, ap1, stored1) and - not outBarrier(node1, state) and - not inBarrier(node2, state) and + fwdFlow(node1, cc, summaryCtx, t1, ap1, stored1) and PrevStage::storeStepCand(node1, c, node2, contentType, containerType) and t2 = getTyp(containerType) and // We need to typecheck stores here, since reverse flow through a getter @@ -642,7 +620,7 @@ module MakeImpl Lang> { */ pragma[nomagic] private predicate fwdFlowConsCand(Typ t2, Ap cons, Content c, Typ t1, Ap tail) { - fwdFlowStore(_, t1, tail, _, c, t2, _, _, _, _, _) and + fwdFlowStore(_, t1, tail, _, c, t2, _, _, _, _) and cons = apCons(c, tail) } @@ -660,13 +638,11 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowRead0( - Typ t, Ap ap, TypOption stored, Content c, Nd node1, Nd node2, FlowState state, Cc cc, + Typ t, Ap ap, TypOption stored, Content c, Nd node1, Nd node2, Cc cc, SummaryCtx summaryCtx ) { exists(ApHeadContent apc | - fwdFlow(node1, state, cc, summaryCtx, t, ap, stored) and - not outBarrier(node1, state) and - not inBarrier(node2, state) and + fwdFlow(node1, cc, summaryCtx, t, ap, stored) and apc = getHeadContent(ap) and readStepCand0(node1, apc, c, node2) ) @@ -675,10 +651,10 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowRead( Nd node1, Typ t1, Ap ap1, TypOption stored1, Content c, Nd node2, Typ t2, Ap ap2, - TypOption stored2, FlowState state, Cc cc, SummaryCtx summaryCtx + TypOption stored2, Cc cc, SummaryCtx summaryCtx ) { exists(Typ ct1, Typ ct2 | - fwdFlowRead0(t1, ap1, stored1, c, node1, node2, state, cc, summaryCtx) and + fwdFlowRead0(t1, ap1, stored1, c, node1, node2, cc, summaryCtx) and fwdFlowConsCand(ct1, ap1, c, ct2, ap2) and typecheck(t1, ct1) and typecheck(t2, ct2) and @@ -692,10 +668,10 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowIntoArg( - ArgNd arg, FlowState state, Cc outercc, SummaryCtx summaryCtx, Typ t, Ap ap, - boolean emptyAp, TypOption stored, boolean cc + ArgNd arg, Cc outercc, SummaryCtx summaryCtx, Typ t, Ap ap, boolean emptyAp, + TypOption stored, boolean cc ) { - fwdFlow(arg, state, outercc, summaryCtx, t, ap, stored) and + fwdFlow(arg, outercc, summaryCtx, t, ap, stored) and (if instanceofCcCall(outercc) then cc = true else cc = false) and emptyAp = isNil(ap) } @@ -784,28 +760,25 @@ module MakeImpl Lang> { pragma[inline] private predicate fwdFlowInCand( - DataFlowCall call, ArgNd arg, FlowState state, Cc outercc, DataFlowCallable inner, - ParamNd p, SummaryCtx summaryCtx, Typ t, Ap ap, boolean emptyAp, TypOption stored, - boolean cc + DataFlowCall call, ArgNd arg, Cc outercc, DataFlowCallable inner, ParamNd p, + SummaryCtx summaryCtx, Typ t, Ap ap, boolean emptyAp, TypOption stored, boolean cc ) { - fwdFlowIntoArg(arg, state, outercc, summaryCtx, t, ap, emptyAp, stored, cc) and + fwdFlowIntoArg(arg, outercc, summaryCtx, t, ap, emptyAp, stored, cc) and ( inner = viableImplCallContextReducedInlineLate(call, arg, outercc) or viableImplArgNotCallContextReduced(call, arg, outercc) ) and - not outBarrier(arg, state) and - not inBarrier(p, state) and callEdgeArgParamRestrictedInlineLate(call, inner, arg, p, emptyAp) } pragma[inline] private predicate fwdFlowInCandTypeFlowDisabled( - DataFlowCall call, ArgNd arg, FlowState state, Cc outercc, DataFlowCallable inner, - ParamNd p, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, boolean cc + DataFlowCall call, ArgNd arg, Cc outercc, DataFlowCallable inner, ParamNd p, + SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, boolean cc ) { not enableTypeFlow() and - fwdFlowInCand(call, arg, state, outercc, inner, p, summaryCtx, t, ap, _, stored, cc) + fwdFlowInCand(call, arg, outercc, inner, p, summaryCtx, t, ap, _, stored, cc) } pragma[nomagic] @@ -814,7 +787,7 @@ module MakeImpl Lang> { boolean emptyAp, boolean cc ) { enableTypeFlow() and - fwdFlowInCand(call, arg, _, outercc, inner, p, _, _, _, emptyAp, _, cc) + fwdFlowInCand(call, arg, outercc, inner, p, _, _, _, emptyAp, _, cc) } pragma[nomagic] @@ -838,18 +811,17 @@ module MakeImpl Lang> { pragma[inline] predicate fwdFlowIn( - DataFlowCall call, ArgNd arg, DataFlowCallable inner, ParamNd p, FlowState state, - Cc outercc, CcCall innercc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, - boolean cc + DataFlowCall call, ArgNd arg, DataFlowCallable inner, ParamNd p, Cc outercc, + CcCall innercc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, boolean cc ) { // type flow disabled: linear recursion - fwdFlowInCandTypeFlowDisabled(call, arg, state, outercc, inner, p, summaryCtx, t, ap, - stored, cc) and + fwdFlowInCandTypeFlowDisabled(call, arg, outercc, inner, p, summaryCtx, t, ap, stored, + cc) and fwdFlowInValidEdgeTypeFlowDisabled(call, inner, innercc, pragma[only_bind_into](cc)) or // type flow enabled: non-linear recursion exists(boolean emptyAp | - fwdFlowIntoArg(arg, state, outercc, summaryCtx, t, ap, emptyAp, stored, cc) and + fwdFlowIntoArg(arg, outercc, summaryCtx, t, ap, emptyAp, stored, cc) and fwdFlowInValidEdgeTypeFlowEnabled(call, arg, outercc, inner, p, innercc, emptyAp, cc) ) } @@ -861,9 +833,9 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowInNoFlowThrough( - ParamNd p, FlowState state, CcCall innercc, Typ t, Ap ap, TypOption stored + ParamNd p, CcCall innercc, Typ t, Ap ap, TypOption stored ) { - FwdFlowInNoThrough::fwdFlowIn(_, _, _, p, state, _, innercc, _, t, ap, stored, _) + FwdFlowInNoThrough::fwdFlowIn(_, _, _, p, _, innercc, _, t, ap, stored, _) } private predicate top() { any() } @@ -872,9 +844,9 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowInFlowThrough( - ParamNd p, FlowState state, CcCall innercc, Typ t, Ap ap, TypOption stored + ParamNd p, CcCall innercc, Typ t, Ap ap, TypOption stored ) { - FwdFlowInThrough::fwdFlowIn(_, _, _, p, state, _, innercc, _, t, ap, stored, _) + FwdFlowInThrough::fwdFlowIn(_, _, _, p, _, innercc, _, t, ap, stored, _) } pragma[nomagic] @@ -914,12 +886,10 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowIntoRet( - RetNd ret, FlowState state, CcNoCall cc, SummaryCtx summaryCtx, Typ t, Ap ap, - TypOption stored + RetNd ret, CcNoCall cc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored ) { instanceofCcNoCall(cc) and - not outBarrier(ret, state) and - fwdFlow(ret, state, cc, summaryCtx, t, ap, stored) + fwdFlow(ret, cc, summaryCtx, t, ap, stored) } pragma[nomagic] @@ -927,7 +897,7 @@ module MakeImpl Lang> { DataFlowCall call, RetNd ret, CcNoCall innercc, DataFlowCallable inner, Nd out, boolean allowsFieldFlow ) { - fwdFlowIntoRet(ret, _, innercc, _, _, _, _) and + fwdFlowIntoRet(ret, innercc, _, _, _, _) and inner = ret.getEnclosingCallable() and ( call = viableImplCallContextReducedReverseInlineLate(inner, innercc) and @@ -949,13 +919,12 @@ module MakeImpl Lang> { pragma[inline] private predicate fwdFlowOut( - DataFlowCall call, DataFlowCallable inner, Nd out, FlowState state, CcNoCall outercc, + DataFlowCall call, DataFlowCallable inner, Nd out, CcNoCall outercc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored ) { exists(RetNd ret, CcNoCall innercc, boolean allowsFieldFlow | - fwdFlowIntoRet(ret, state, innercc, summaryCtx, t, ap, stored) and + fwdFlowIntoRet(ret, innercc, summaryCtx, t, ap, stored) and fwdFlowOutValidEdge(call, ret, innercc, inner, out, outercc, allowsFieldFlow) and - not inBarrier(out, state) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } @@ -969,65 +938,60 @@ module MakeImpl Lang> { pragma[nomagic] private predicate dataFlowTakenCallEdgeIn0( - DataFlowCall call, DataFlowCallable c, ParamNd p, FlowState state, CcCall innercc, - Typ t, Ap ap, TypOption stored, boolean cc + DataFlowCall call, DataFlowCallable c, ParamNd p, CcCall innercc, Typ t, Ap ap, + TypOption stored, boolean cc ) { - FwdFlowInNoThrough::fwdFlowIn(call, _, c, p, state, _, innercc, _, t, ap, stored, cc) + FwdFlowInNoThrough::fwdFlowIn(call, _, c, p, _, innercc, _, t, ap, stored, cc) or - FwdFlowInThrough::fwdFlowIn(call, _, c, p, state, _, innercc, _, t, ap, stored, cc) + FwdFlowInThrough::fwdFlowIn(call, _, c, p, _, innercc, _, t, ap, stored, cc) } pragma[nomagic] - private predicate fwdFlow1Param( - ParamNd p, FlowState state, CcCall cc, Typ t0, Ap ap, TypOption stored - ) { + private predicate fwdFlow1Param(ParamNd p, CcCall cc, Typ t0, Ap ap, TypOption stored) { instanceofCcCall(cc) and - fwdFlow1(p, state, cc, _, t0, _, ap, stored) + fwdFlow1(p, cc, _, t0, _, ap, stored) } pragma[nomagic] predicate dataFlowTakenCallEdgeIn(DataFlowCall call, DataFlowCallable c, boolean cc) { - exists(ParamNd p, FlowState state, CcCall innercc, Typ t, Ap ap, TypOption stored | - dataFlowTakenCallEdgeIn0(call, c, p, state, innercc, t, ap, stored, cc) and - fwdFlow1Param(p, state, innercc, t, ap, stored) + exists(ParamNd p, CcCall innercc, Typ t, Ap ap, TypOption stored | + dataFlowTakenCallEdgeIn0(call, c, p, innercc, t, ap, stored, cc) and + fwdFlow1Param(p, innercc, t, ap, stored) ) } pragma[nomagic] private predicate dataFlowTakenCallEdgeOut0( - DataFlowCall call, DataFlowCallable c, Nd node, FlowState state, Cc cc, Typ t, Ap ap, - TypOption stored + DataFlowCall call, DataFlowCallable c, Nd node, Cc cc, Typ t, Ap ap, TypOption stored ) { - fwdFlowOut(call, c, node, state, cc, _, t, ap, stored) + fwdFlowOut(call, c, node, cc, _, t, ap, stored) } pragma[nomagic] - private predicate fwdFlow1Out( - Nd node, FlowState state, Cc cc, Typ t0, Ap ap, TypOption stored - ) { - fwdFlow1(node, state, cc, _, t0, _, ap, stored) and + private predicate fwdFlow1Out(Nd node, Cc cc, Typ t0, Ap ap, TypOption stored) { + fwdFlow1(node, cc, _, t0, _, ap, stored) and PrevStage::callEdgeReturn(_, _, _, _, node, _) } pragma[nomagic] predicate dataFlowTakenCallEdgeOut(DataFlowCall call, DataFlowCallable c) { - exists(Nd node, FlowState state, Cc cc, Typ t, Ap ap, TypOption stored | - dataFlowTakenCallEdgeOut0(call, c, node, state, cc, t, ap, stored) and - fwdFlow1Out(node, state, cc, t, ap, stored) + exists(Nd node, Cc cc, Typ t, Ap ap, TypOption stored | + dataFlowTakenCallEdgeOut0(call, c, node, cc, t, ap, stored) and + fwdFlow1Out(node, cc, t, ap, stored) ) } predicate dataFlowNonCallEntry(DataFlowCallable c, boolean cc) { - exists(Nd node, FlowState state | - sourceNode(node, state) and + exists(Nd node | + sourceNode(node) and (if hasSourceCallCtx() then cc = true else cc = false) and - PrevStage::revFlow(node, state, any(PrevStage::ApNil nil)) and + PrevStage::revFlow(node, any(PrevStage::ApNil nil)) and c = node.getEnclosingCallable() ) or exists(Nd node | cc = false and - fwdFlowJump(node, _, _, _, _) and + fwdFlowJump(node, _, _, _) and c = node.getEnclosingCallable() ) } @@ -1044,15 +1008,13 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowRetFromArg( - RetNd ret, FlowState state, CcCall ccc, SummaryCtxSome summaryCtx, Typ t, Ap ap, - TypOption stored + RetNd ret, CcCall ccc, SummaryCtxSome summaryCtx, Typ t, Ap ap, TypOption stored ) { exists(ReturnKindExt kind, ParamNd p, Ap argAp | instanceofCcCall(ccc) and - fwdFlow(pragma[only_bind_into](ret), state, ccc, summaryCtx, t, ap, stored) and + fwdFlow(pragma[only_bind_into](ret), ccc, summaryCtx, t, ap, stored) and summaryCtx = - TSummaryCtxSome(pragma[only_bind_into](p), _, _, pragma[only_bind_into](argAp), _) and - not outBarrier(ret, state) and + TSummaryCtxSome(pragma[only_bind_into](p), _, pragma[only_bind_into](argAp), _) and kind = ret.getKind() and Stage1::parameterFlowThroughAllowed(p, kind) and PrevStage::returnMayFlowThrough(ret, kind) @@ -1061,28 +1023,26 @@ module MakeImpl Lang> { pragma[inline] private predicate fwdFlowThrough0( - DataFlowCall call, ArgNd arg, Cc cc, FlowState state, CcCall ccc, SummaryCtx summaryCtx, - Typ t, Ap ap, TypOption stored, RetNd ret, SummaryCtxSome innerSummaryCtx + DataFlowCall call, ArgNd arg, Cc cc, CcCall ccc, SummaryCtx summaryCtx, Typ t, Ap ap, + TypOption stored, RetNd ret, SummaryCtxSome innerSummaryCtx ) { - fwdFlowRetFromArg(ret, state, ccc, innerSummaryCtx, t, ap, stored) and + fwdFlowRetFromArg(ret, ccc, innerSummaryCtx, t, ap, stored) and fwdFlowIsEntered(call, arg, cc, ccc, summaryCtx, innerSummaryCtx) } pragma[nomagic] private predicate fwdFlowThrough( - DataFlowCall call, Cc cc, FlowState state, SummaryCtx summaryCtx, Typ t, Ap ap, - TypOption stored, RetNd ret + DataFlowCall call, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, RetNd ret ) { - fwdFlowThrough0(call, _, cc, state, _, summaryCtx, t, ap, stored, ret, _) + fwdFlowThrough0(call, _, cc, _, summaryCtx, t, ap, stored, ret, _) } pragma[nomagic] private predicate fwdFlowIsEntered0( DataFlowCall call, ArgNd arg, Cc cc, CcCall innerCc, SummaryCtx summaryCtx, ParamNd p, - FlowState state, Typ t, Ap ap, TypOption stored + Typ t, Ap ap, TypOption stored ) { - FwdFlowInThrough::fwdFlowIn(call, arg, _, p, state, cc, innerCc, summaryCtx, t, ap, - stored, _) + FwdFlowInThrough::fwdFlowIn(call, arg, _, p, cc, innerCc, summaryCtx, t, ap, stored, _) } /** @@ -1094,39 +1054,37 @@ module MakeImpl Lang> { DataFlowCall call, ArgNd arg, Cc cc, CcCall innerCc, SummaryCtx summaryCtx, SummaryCtxSome innerSummaryCtx ) { - exists(ParamNd p, FlowState state, Typ t, Ap ap, TypOption stored | - fwdFlowIsEntered0(call, arg, cc, innerCc, summaryCtx, p, state, t, ap, stored) and - innerSummaryCtx = TSummaryCtxSome(p, state, t, ap, stored) + exists(ParamNd p, Typ t, Ap ap, TypOption stored | + fwdFlowIsEntered0(call, arg, cc, innerCc, summaryCtx, p, t, ap, stored) and + innerSummaryCtx = TSummaryCtxSome(p, t, ap, stored) ) } pragma[nomagic] private predicate storeStepFwd(Nd node1, Ap ap1, Content c, Nd node2, Ap ap2) { - fwdFlowStore(node1, _, ap1, _, c, _, _, node2, _, _, _) and + fwdFlowStore(node1, _, ap1, _, c, _, _, node2, _, _) and readStepFwd(_, ap2, c, _, ap1) } pragma[nomagic] private predicate readStepFwd(Nd n1, Ap ap1, Content c, Nd n2, Ap ap2) { - fwdFlowRead(n1, _, ap1, _, c, n2, _, ap2, _, _, _, _) + fwdFlowRead(n1, _, ap1, _, c, n2, _, ap2, _, _, _) } pragma[nomagic] private predicate returnFlowsThrough0( - DataFlowCall call, FlowState state, CcCall ccc, Ap ap, RetNd ret, - SummaryCtxSome innerSummaryCtx + DataFlowCall call, CcCall ccc, Ap ap, RetNd ret, SummaryCtxSome innerSummaryCtx ) { - fwdFlowThrough0(call, _, _, state, ccc, _, _, ap, _, ret, innerSummaryCtx) + fwdFlowThrough0(call, _, _, ccc, _, _, ap, _, ret, innerSummaryCtx) } pragma[nomagic] private predicate returnFlowsThrough( - RetNd ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNd p, Typ argT, Ap argAp, + RetNd ret, ReturnPosition pos, CcCall ccc, ParamNd p, Typ argT, Ap argAp, TypOption argStored, Ap ap ) { exists(DataFlowCall call, boolean allowsFieldFlow | - returnFlowsThrough0(call, state, ccc, ap, ret, - TSummaryCtxSome(p, _, argT, argAp, argStored)) and + returnFlowsThrough0(call, ccc, ap, ret, TSummaryCtxSome(p, argT, argAp, argStored)) and flowThroughOutOfCall(call, ret, _, allowsFieldFlow) and pos = ret.getReturnPosition() and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1136,10 +1094,10 @@ module MakeImpl Lang> { pragma[nomagic] private predicate flowThroughIntoCall(DataFlowCall call, ArgNd arg, ParamNd p, Ap argAp) { exists(Typ argT, TypOption argStored | - returnFlowsThrough(_, _, _, _, pragma[only_bind_into](p), pragma[only_bind_into](argT), + returnFlowsThrough(_, _, _, pragma[only_bind_into](p), pragma[only_bind_into](argT), pragma[only_bind_into](argAp), pragma[only_bind_into](argStored), _) and flowIntoCallTaken(call, _, pragma[only_bind_into](arg), p, isNil(argAp)) and - fwdFlow(arg, _, _, _, pragma[only_bind_into](argT), pragma[only_bind_into](argAp), + fwdFlow(arg, _, _, pragma[only_bind_into](argT), pragma[only_bind_into](argAp), pragma[only_bind_into](argStored)) ) } @@ -1149,7 +1107,7 @@ module MakeImpl Lang> { DataFlowCall call, DataFlowCallable c, ArgNd arg, ParamNd p, Ap ap ) { flowIntoCallTaken(call, c, arg, p, isNil(ap)) and - fwdFlow(arg, _, _, _, _, ap, _) + fwdFlow(arg, _, _, _, ap, _) } pragma[nomagic] @@ -1158,7 +1116,7 @@ module MakeImpl Lang> { boolean allowsFieldFlow ) { PrevStage::callEdgeReturn(call, c, ret, _, out, allowsFieldFlow) and - fwdFlow(ret, _, _, _, _, ap, _) and + fwdFlow(ret, _, _, _, ap, _) and pos = ret.getReturnPosition() and (if allowsFieldFlow = false then ap instanceof ApNil else any()) and ( @@ -1177,19 +1135,15 @@ module MakeImpl Lang> { * records the access path of the returned value. */ pragma[nomagic] - additional predicate revFlow( - Nd node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap - ) { - revFlow0(node, state, returnCtx, returnAp, ap) and - fwdFlow(node, state, _, _, _, ap, _) + additional predicate revFlow(Nd node, ReturnCtx returnCtx, ApOption returnAp, Ap ap) { + revFlow0(node, returnCtx, returnAp, ap) and + fwdFlow(node, _, _, _, ap, _) } pragma[nomagic] - private predicate revFlow0( - Nd node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap - ) { - fwdFlow(node, state, _, _, _, ap, _) and - sinkNode(node, state) and + private predicate revFlow0(Nd node, ReturnCtx returnCtx, ApOption returnAp, Ap ap) { + fwdFlow(node, _, _, _, ap, _) and + sinkNode(node) and ( if hasSinkCallCtx() then returnCtx = TReturnCtxNoFlowThrough() @@ -1198,48 +1152,48 @@ module MakeImpl Lang> { returnAp = apNone() and ap instanceof ApNil or - exists(Nd mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, _, _) and - revFlow(mid, state0, returnCtx, returnAp, ap) + exists(Nd mid | + localStep(node, mid, true, _, _, _) and + revFlow(mid, returnCtx, returnAp, ap) ) or - exists(Nd mid, FlowState state0 | - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, _, _) and - revFlow(mid, state0, returnCtx, returnAp, ap) and + exists(Nd mid | + localStep(node, mid, false, _, _, _) and + revFlow(mid, returnCtx, returnAp, ap) and ap instanceof ApNil ) or - revFlowJump(node, state, ap) and + revFlowJump(node, ap) and returnCtx = TReturnCtxNone() and returnAp = apNone() or // store exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, returnCtx, returnAp) and + revFlowStore(ap0, c, ap, node, _, returnCtx, returnAp) and revFlowConsCand(ap0, c, ap) ) or // read exists(Nd mid, Ap ap0 | - revFlow(mid, state, returnCtx, returnAp, ap0) and + revFlow(mid, returnCtx, returnAp, ap0) and readStepFwd(node, ap, _, mid, ap0) ) or // flow into a callable - revFlowIn(_, _, node, state, ap) and + revFlowIn(_, _, node, ap) and returnCtx = TReturnCtxNone() and returnAp = apNone() or // flow through a callable exists(DataFlowCall call, ParamNd p | - revFlowThrough(call, returnCtx, p, state, returnAp, ap) and + revFlowThrough(call, returnCtx, p, returnAp, ap) and flowThroughIntoCall(call, node, p, ap) ) or // flow out of a callable exists(ReturnPosition pos | - revFlowOut(_, node, pos, state, _, _, _, ap) and - if returnFlowsThrough(node, pos, state, _, _, _, _, _, ap) + revFlowOut(_, node, pos, _, _, _, ap) and + if returnFlowsThrough(node, pos, _, _, _, _, _, ap) then ( returnCtx = TReturnCtxMaybeFlowThrough(pos) and returnAp = apSome(ap) @@ -1249,31 +1203,24 @@ module MakeImpl Lang> { ) } - private predicate revFlowJump(Nd node, FlowState state, Ap ap) { + private predicate revFlowJump(Nd node, Ap ap) { exists(Nd mid | jumpStepEx(node, mid) and - revFlow(mid, state, _, _, ap) + revFlow(mid, _, _, ap) ) or exists(Nd mid | additionalJumpStep(node, mid, _) and - revFlow(pragma[only_bind_into](mid), state, _, _, ap) and - ap instanceof ApNil - ) - or - exists(Nd mid, FlowState state0 | - additionalJumpStateStep(node, state, mid, state0, _) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, ap) and + revFlow(pragma[only_bind_into](mid), _, _, ap) and ap instanceof ApNil ) } pragma[nomagic] private predicate revFlowStore( - Ap ap0, Content c, Ap ap, Nd node, FlowState state, Nd mid, ReturnCtx returnCtx, - ApOption returnAp + Ap ap0, Content c, Ap ap, Nd node, Nd mid, ReturnCtx returnCtx, ApOption returnAp ) { - revFlow(mid, state, returnCtx, returnAp, ap0) and + revFlow(mid, returnCtx, returnAp, ap0) and storeStepFwd(node, ap, c, mid, ap0) } @@ -1284,7 +1231,7 @@ module MakeImpl Lang> { pragma[nomagic] private predicate revFlowConsCand(Ap cons, Content c, Ap tail) { exists(Nd mid, Ap tail0 | - revFlow(mid, _, _, _, tail) and + revFlow(mid, _, _, tail) and tail = pragma[only_bind_into](tail0) and readStepFwd(_, cons, c, mid, tail0) ) @@ -1304,27 +1251,27 @@ module MakeImpl Lang> { pragma[nomagic] predicate dataFlowTakenCallEdgeIn(DataFlowCall call, DataFlowCallable c, boolean cc) { exists(RetNd ret | - revFlowOut(call, ret, _, _, _, cc, _, _) and + revFlowOut(call, ret, _, _, cc, _, _) and c = ret.getEnclosingCallable() ) } pragma[nomagic] predicate dataFlowTakenCallEdgeOut(DataFlowCall call, DataFlowCallable c) { - revFlowIn(call, c, _, _, _) + revFlowIn(call, c, _, _) } predicate dataFlowNonCallEntry(DataFlowCallable c, boolean cc) { - exists(Nd node, FlowState state, ApNil nil | - fwdFlow(node, state, _, _, _, nil, _) and - sinkNode(node, state) and + exists(Nd node, ApNil nil | + fwdFlow(node, _, _, _, nil, _) and + sinkNode(node) and (if hasSinkCallCtx() then cc = true else cc = false) and c = node.getEnclosingCallable() ) or exists(Nd node | cc = false and - revFlowJump(node, _, _) and + revFlowJump(node, _) and c = node.getEnclosingCallable() ) } @@ -1350,44 +1297,39 @@ module MakeImpl Lang> { ) } - private predicate revFlowIn( - DataFlowCall call, DataFlowCallable c, ArgNd arg, FlowState state, Ap ap - ) { + private predicate revFlowIn(DataFlowCall call, DataFlowCallable c, ArgNd arg, Ap ap) { exists(ParamNd p | - revFlow(p, state, TReturnCtxNone(), _, ap) and + revFlow(p, TReturnCtxNone(), _, ap) and flowIntoCallApValid(call, c, arg, p, ap) ) } pragma[nomagic] private predicate revFlowOut( - DataFlowCall call, RetNd ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx, - boolean cc, ApOption returnAp, Ap ap + DataFlowCall call, RetNd ret, ReturnPosition pos, ReturnCtx returnCtx, boolean cc, + ApOption returnAp, Ap ap ) { exists(Nd out | - revFlow(out, state, returnCtx, returnAp, ap) and + revFlow(out, returnCtx, returnAp, ap) and flowOutOfCallApValid(call, ret, pos, out, ap, cc) and if returnCtx instanceof TReturnCtxNone then cc = false else cc = true ) } pragma[nomagic] - private predicate revFlowParamToReturn( - ParamNd p, FlowState state, ReturnPosition pos, Ap returnAp, Ap ap - ) { - revFlow(pragma[only_bind_into](p), state, TReturnCtxMaybeFlowThrough(pos), - apSome(returnAp), pragma[only_bind_into](ap)) and + private predicate revFlowParamToReturn(ParamNd p, ReturnPosition pos, Ap returnAp, Ap ap) { + revFlow(pragma[only_bind_into](p), TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), + pragma[only_bind_into](ap)) and Stage1::parameterFlowThroughAllowed(p, pos.getKind()) and PrevStage::parameterMayFlowThrough(p, isNil(ap)) } pragma[nomagic] private predicate revFlowThrough( - DataFlowCall call, ReturnCtx returnCtx, ParamNd p, FlowState state, ApOption returnAp, - Ap ap + DataFlowCall call, ReturnCtx returnCtx, ParamNd p, ApOption returnAp, Ap ap ) { exists(ReturnPosition pos, Ap innerReturnAp | - revFlowParamToReturn(p, state, pos, innerReturnAp, ap) and + revFlowParamToReturn(p, pos, innerReturnAp, ap) and revFlowIsReturned(call, returnCtx, returnAp, pos, innerReturnAp) ) } @@ -1401,9 +1343,9 @@ module MakeImpl Lang> { private predicate revFlowIsReturned( DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap ) { - exists(RetNd ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, pos, state, returnCtx, _, returnAp, ap) and - returnFlowsThrough(ret, pos, state, ccc, _, _, _, _, ap) and + exists(RetNd ret, CcCall ccc | + revFlowOut(call, ret, pos, returnCtx, _, returnAp, ap) and + returnFlowsThrough(ret, pos, ccc, _, _, _, _, ap) and matchesCall(ccc, call) ) } @@ -1414,35 +1356,35 @@ module MakeImpl Lang> { ) { exists(Ap ap2, Ap ap1 | PrevStage::storeStepCand(node1, c, node2, contentType, containerType) and - revFlowStore(ap2, c, ap1, node1, _, node2, _, _) and + revFlowStore(ap2, c, ap1, node1, node2, _, _) and revFlowConsCand(ap2, c, ap1) ) } predicate readStepCand(Nd node1, Content c, Nd node2) { exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2)) and + revFlow(node2, _, _, pragma[only_bind_into](ap2)) and readStepFwd(node1, ap1, c, node2, ap2) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _) + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _) ) } - predicate revFlow(Nd node, FlowState state, Ap ap) { revFlow(node, state, _, _, ap) } + predicate revFlow(Nd node, Ap ap) { revFlow(node, _, _, ap) } pragma[nomagic] - predicate revFlow(Nd node) { revFlow(node, _, _, _, _) } + predicate revFlow(Nd node) { revFlow(node, _, _, _) } private predicate fwdConsCand(Content c, Ap ap) { storeStepFwd(_, ap, c, _, _) } private predicate revConsCand(Content c, Ap ap) { exists(Ap ap2 | - revFlowStore(ap2, c, ap, _, _, _, _, _) and + revFlowStore(ap2, c, ap, _, _, _, _) and revFlowConsCand(ap2, c, ap) ) } private predicate validAp(Ap ap) { - revFlow(_, _, _, _, ap) and ap instanceof ApNil + revFlow(_, _, _, ap) and ap instanceof ApNil or exists(Content head, Ap tail | consCand(head, tail) and @@ -1457,14 +1399,14 @@ module MakeImpl Lang> { pragma[nomagic] private predicate parameterFlowsThroughRev(ParamNd p, Ap ap, ReturnPosition pos, Ap returnAp) { - revFlow(p, _, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap) and + revFlow(p, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap) and Stage1::parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] private predicate parameterMayFlowThroughAp(ParamNd p, Ap ap) { exists(ReturnPosition pos | - returnFlowsThrough(_, pos, _, _, p, _, ap, _, _) and + returnFlowsThrough(_, pos, _, p, _, ap, _, _) and parameterFlowsThroughRev(p, ap, pos, _) ) } @@ -1478,11 +1420,11 @@ module MakeImpl Lang> { } pragma[nomagic] - private predicate nodeMayUseSummary0(Nd n, ParamNd p, FlowState state, Ap ap) { + private predicate nodeMayUseSummary0(Nd n, ParamNd p, Ap ap) { exists(Ap ap0 | parameterMayFlowThrough(p, _) and - revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, ap0) and - fwdFlow(n, state, any(CcCall ccc), TSummaryCtxSome(p, _, _, ap, _), _, ap0, _) + revFlow(n, TReturnCtxMaybeFlowThrough(_), _, ap0) and + fwdFlow(n, any(CcCall ccc), TSummaryCtxSome(p, _, ap, _), _, ap0, _) ) } @@ -1491,17 +1433,17 @@ module MakeImpl Lang> { * and remains relevant for the following pruning stage. */ pragma[nomagic] - additional predicate nodeMayUseSummary(Nd n, FlowState state, Ap ap) { + additional predicate nodeMayUseSummary(Nd n, Ap ap) { exists(ParamNd p | parameterMayFlowThroughAp(p, ap) and - nodeMayUseSummary0(n, p, state, ap) + nodeMayUseSummary0(n, p, ap) ) } pragma[nomagic] predicate returnMayFlowThrough(RetNd ret, ReturnKindExt kind) { exists(ParamNd p, ReturnPosition pos, Ap argAp, Ap ap | - returnFlowsThrough(ret, pos, _, _, p, _, argAp, _, ap) and + returnFlowsThrough(ret, pos, _, p, _, argAp, _, ap) and parameterFlowsThroughRev(p, argAp, pos, ap) and kind = pos.getKind() ) @@ -1509,30 +1451,29 @@ module MakeImpl Lang> { pragma[nomagic] private predicate revFlowThroughArg( - DataFlowCall call, ArgNd arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, - Ap ap + DataFlowCall call, ArgNd arg, ReturnCtx returnCtx, ApOption returnAp, Ap ap ) { exists(ParamNd p | - revFlowThrough(call, returnCtx, p, state, returnAp, ap) and + revFlowThrough(call, returnCtx, p, returnAp, ap) and flowThroughIntoCall(call, arg, p, ap) ) } pragma[nomagic] predicate callMayFlowThroughRev(DataFlowCall call) { - exists(ArgNd arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap | - revFlow(arg, state, returnCtx, returnAp, ap) and - revFlowThroughArg(call, arg, state, returnCtx, returnAp, ap) + exists(ArgNd arg, ReturnCtx returnCtx, ApOption returnAp, Ap ap | + revFlow(arg, returnCtx, returnAp, ap) and + revFlowThroughArg(call, arg, returnCtx, returnAp, ap) ) } predicate callEdgeArgParam( DataFlowCall call, DataFlowCallable c, ArgNd arg, ParamNd p, boolean emptyAp ) { - exists(FlowState state, Ap ap | + exists(Ap ap | flowIntoCallAp(call, c, arg, p, ap) and - revFlow(arg, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and - revFlow(p, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and + revFlow(arg, pragma[only_bind_into](ap)) and + revFlow(p, pragma[only_bind_into](ap)) and emptyAp = isNil(ap) | // both directions are needed for flow-through @@ -1545,10 +1486,10 @@ module MakeImpl Lang> { DataFlowCall call, DataFlowCallable c, RetNd ret, ReturnKindExt kind, Nd out, boolean allowsFieldFlow ) { - exists(FlowState state, ReturnPosition pos, Ap ap | + exists(ReturnPosition pos, Ap ap | flowOutOfCallAp(call, c, ret, pos, out, ap, allowsFieldFlow) and - revFlow(ret, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and - revFlow(out, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and + revFlow(ret, pragma[only_bind_into](ap)) and + revFlow(out, pragma[only_bind_into](ap)) and kind = pos.getKind() and RevTypeFlowInput::dataFlowTakenCallEdgeIn(call, c, _) ) @@ -1563,11 +1504,11 @@ module MakeImpl Lang> { } /** Holds if `node1` can step to `node2` in one or more local steps. */ - bindingset[node1, state1] - bindingset[node2, state2] + bindingset[node1] + bindingset[node2] signature predicate localStepSig( - Nd node1, FlowState state1, Nd node2, FlowState state2, boolean preservesValue, - DataFlowType t, LocalCallContext lcc, string label + Nd node1, Nd node2, boolean preservesValue, DataFlowType t, LocalCallContext lcc, + string label ); /** @@ -1577,7 +1518,7 @@ module MakeImpl Lang> { * restricted to nodes that are forwards and backwards reachable in * this stage. */ - additional module LocalFlowBigStep { + additional module LocalFlowBigStep { final private class FinalNd = Nd; /** @@ -1595,14 +1536,13 @@ module MakeImpl Lang> { } private predicate additionalLocalStateStep( - Nd node1, FlowState state1, Nd node2, FlowState state2, DataFlowType t, - LocalCallContext lcc, string label + Nd node1, Nd node2, DataFlowType t, LocalCallContext lcc, string label ) { exists(ApNil nil | - revFlow(node1, state1, pragma[only_bind_into](nil)) and - revFlow(node2, state2, pragma[only_bind_into](nil)) and - localStepInput(node1, state1, node2, state2, false, t, lcc, label) and - state1 != state2 + revFlow(node1, pragma[only_bind_into](nil)) and + revFlow(node2, pragma[only_bind_into](nil)) and + localStepInput(node1, node2, false, t, lcc, label) and + isStateStep(node1, node2) ) } @@ -1610,17 +1550,15 @@ module MakeImpl Lang> { * Holds if `node` can be the first node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowEntry(Nd node, FlowState state, Ap ap) { - revFlow(node, state, ap) and + private predicate localFlowEntry(Nd node, Ap ap) { + revFlow(node, ap) and ( - sourceNode(node, state) + sourceNode(node) or jumpStepEx(_, node) or additionalJumpStep(_, node, _) or - additionalJumpStateStep(_, _, node, state, _) - or node instanceof ParamNd or node instanceof OutNd @@ -1631,7 +1569,7 @@ module MakeImpl Lang> { or node instanceof FlowCheckNode or - additionalLocalStateStep(_, _, node, state, _, _, _) + additionalLocalStateStep(_, node, _, _, _) ) } @@ -1639,10 +1577,10 @@ module MakeImpl Lang> { * Holds if `node` can be the last node in a maximal subsequence of local * flow steps in a dataflow path. */ - private predicate localFlowExit(Nd node, FlowState state, Ap ap) { - revFlow(node, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and + private predicate localFlowExit(Nd node, Ap ap) { + revFlow(node, pragma[only_bind_into](ap)) and ( - exists(Nd next, Ap apNext | revFlow(next, pragma[only_bind_into](state), apNext) | + exists(Nd next, Ap apNext | revFlow(next, apNext) | jumpStepEx(node, next) and apNext = ap or @@ -1659,19 +1597,15 @@ module MakeImpl Lang> { storeStepCand(node, _, next, _, _) or readStepCand(node, _, next) - ) - or - exists(Nd next, FlowState s | - revFlow(next, s, pragma[only_bind_into](ap)) and ap instanceof ApNil - | - additionalJumpStateStep(node, state, next, s, _) or - additionalLocalStateStep(node, state, next, s, _, _, _) + additionalLocalStateStep(node, next, _, _, _) and + apNext = ap and + ap instanceof ApNil ) or node instanceof FlowCheckNode or - sinkNode(node, state) and + sinkNode(node) and ap instanceof ApNil ) } @@ -1685,27 +1619,24 @@ module MakeImpl Lang> { */ pragma[nomagic] private predicate localFlowStepPlus( - Nd node1, FlowState state, Nd node2, boolean preservesValue, DataFlowType t, - LocalCallContext cc, string label + Nd node1, Nd node2, boolean preservesValue, DataFlowType t, LocalCallContext cc, + string label ) { - not inBarrier(node2, state) and - not outBarrier(node1, state) and exists(Nd mid, boolean preservesValue2, DataFlowType t2, string label2, Ap ap | - localStepInput(mid, state, node2, state, preservesValue2, t2, cc, label2) and - revFlow(node2, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and - not outBarrier(mid, state) and + localStepInput(mid, node2, preservesValue2, t2, cc, label2) and + not isStateStep(mid, node2) and + revFlow(node2, pragma[only_bind_into](ap)) and (preservesValue = true or ap instanceof ApNil) | node1 = mid and - localFlowEntry(node1, pragma[only_bind_into](state), pragma[only_bind_into](ap)) and + localFlowEntry(node1, pragma[only_bind_into](ap)) and preservesValue = preservesValue2 and label = label2 and t = t2 and node1 != node2 or exists(boolean preservesValue1, DataFlowType t1, string label1 | - localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue1, t1, - cc, label1) and + localFlowStepPlus(node1, mid, preservesValue1, t1, cc, label1) and not mid instanceof FlowCheckNode and preservesValue = preservesValue2.booleanAnd(preservesValue1) and label = mergeLabels(label1, label2) and @@ -1720,19 +1651,18 @@ module MakeImpl Lang> { */ pragma[nomagic] predicate localFlowBigStep( - Nd node1, FlowState state1, Nd node2, FlowState state2, boolean preservesValue, - DataFlowType t, LocalCallContext callContext, string label + Nd node1, Nd node2, boolean preservesValue, DataFlowType t, + LocalCallContext callContext, string label ) { exists(Ap ap | - localFlowStepPlus(node1, state1, node2, preservesValue, t, callContext, label) and - localFlowExit(node2, state1, ap) and - state1 = state2 and + localFlowStepPlus(node1, node2, preservesValue, t, callContext, label) and + localFlowExit(node2, ap) and node1 != node2 | preservesValue = true or ap instanceof ApNil ) or - additionalLocalStateStep(node1, state1, node2, state2, t, callContext, label) and + additionalLocalStateStep(node1, node2, t, callContext, label) and preservesValue = false } @@ -1746,19 +1676,19 @@ module MakeImpl Lang> { */ pragma[nomagic] predicate localFlowBigStepTc( - Nd node1, FlowState state1, Nd node2, FlowState state2, boolean preservesValue, - DataFlowType t, LocalCallContext callContext, string label + Nd node1, Nd node2, boolean preservesValue, DataFlowType t, + LocalCallContext callContext, string label ) { exists(Ap ap | - localFlowEntry(node1, pragma[only_bind_into](state1), pragma[only_bind_into](ap)) and - localStepInput(node1, state1, node2, state2, preservesValue, t, callContext, label) and - localFlowExit(node2, pragma[only_bind_into](state2), pragma[only_bind_into](ap)) and - state1 = state2 + localFlowEntry(node1, pragma[only_bind_into](ap)) and + localStepInput(node1, node2, preservesValue, t, callContext, label) and + localFlowExit(node2, pragma[only_bind_into](ap)) and + not isStateStep(node1, node2) | preservesValue = true or ap instanceof ApNil ) or - additionalLocalStateStep(node1, state1, node2, state2, t, callContext, label) and + additionalLocalStateStep(node1, node2, t, callContext, label) and preservesValue = false } } @@ -1768,17 +1698,14 @@ module MakeImpl Lang> { */ additional module Graph { private newtype TPathNode = - TPathNodeMid( - Nd node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored - ) { - fwdFlow(node, state, cc, summaryCtx, t, ap, stored) and - revFlow(node, state, _, _, ap) + TPathNodeMid(Nd node, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored) { + fwdFlow(node, cc, summaryCtx, t, ap, stored) and + revFlow(node, _, _, ap) } or - TPathNodeSink(Nd node, FlowState state) { + TPathNodeSink(Nd node) { exists(PathNodeMid sink | sink.isAtSink() and - node = sink.toNormalSinkNode() and - state = sink.getState() + node = sink.toNormalSinkNode() ) } or TPathNodeSrcGrp() or @@ -1907,18 +1834,17 @@ module MakeImpl Lang> { */ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { Nd node; - FlowState state; Cc cc; SummaryCtx summaryCtx; Typ t; Ap ap; TypOption stored; - PathNodeMid() { this = TPathNodeMid(node, state, cc, summaryCtx, t, ap, stored) } + PathNodeMid() { this = TPathNodeMid(node, cc, summaryCtx, t, ap, stored) } override NodeEx getNodeEx() { result = node.getNodeEx() } - override FlowState getState() { result = state } + override FlowState getState() { result = node.getState() } private PathNodeMid getSuccMid(string label) { localStep(this, result, label) @@ -2000,7 +1926,7 @@ module MakeImpl Lang> { } override predicate isSource() { - sourceNode(node, state) and + sourceNode(node) and (if hasSourceCallCtx() then cc = ccSomeCall() else cc = ccNone()) and summaryCtx = TSummaryCtxNone() and t = getNodeTyp(node) and @@ -2008,7 +1934,7 @@ module MakeImpl Lang> { } predicate isAtSink() { - sinkNode(node, state) and + sinkNode(node) and ap instanceof ApNil and // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall` // is exactly what we need to check. @@ -2032,7 +1958,7 @@ module MakeImpl Lang> { exists(string model | this.isAtSink() and sinkModel(node, model) and - result = TPathNodeSink(this.toNormalSinkNode(), state) and + result = TPathNodeSink(this.toNormalSinkNode()) and if model != "" then label = "Sink:" + model else label = "" ) } @@ -2045,13 +1971,12 @@ module MakeImpl Lang> { */ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { Nd node; - FlowState state; - PathNodeSink() { this = TPathNodeSink(node, state) } + PathNodeSink() { this = TPathNodeSink(node) } override NodeEx getNodeEx() { result = node.getNodeEx() } - override FlowState getState() { result = state } + override FlowState getState() { result = node.getState() } override string toString() { result = node.toString() } @@ -2059,110 +1984,106 @@ module MakeImpl Lang> { result.isArbitrarySink() and label = "" } - override predicate isSource() { sourceNode(node, state) } + override predicate isSource() { sourceNode(node) } } - bindingset[p, state, t, ap, stored] + bindingset[p, t, ap, stored] pragma[inline_late] - private SummaryCtxSome mkSummaryCtxSome( - ParamNd p, FlowState state, Typ t, Ap ap, TypOption stored - ) { - result = TSummaryCtxSome(p, state, t, ap, stored) + private SummaryCtxSome mkSummaryCtxSome(ParamNd p, Typ t, Ap ap, TypOption stored) { + result = TSummaryCtxSome(p, t, ap, stored) } pragma[nomagic] private predicate fwdFlowInStep( - ArgNd arg, ParamNd p, FlowState state, Cc outercc, CcCall innercc, - SummaryCtx outerSummaryCtx, SummaryCtx innerSummaryCtx, Typ t, Ap ap, TypOption stored + ArgNd arg, ParamNd p, Cc outercc, CcCall innercc, SummaryCtx outerSummaryCtx, + SummaryCtx innerSummaryCtx, Typ t, Ap ap, TypOption stored ) { - FwdFlowInNoThrough::fwdFlowIn(_, arg, _, p, state, outercc, innercc, outerSummaryCtx, t, - ap, stored, _) and + FwdFlowInNoThrough::fwdFlowIn(_, arg, _, p, outercc, innercc, outerSummaryCtx, t, ap, + stored, _) and innerSummaryCtx = TSummaryCtxNone() or - FwdFlowInThrough::fwdFlowIn(_, arg, _, p, state, outercc, innercc, outerSummaryCtx, t, - ap, stored, _) and - innerSummaryCtx = mkSummaryCtxSome(p, state, t, ap, stored) + FwdFlowInThrough::fwdFlowIn(_, arg, _, p, outercc, innercc, outerSummaryCtx, t, ap, + stored, _) and + innerSummaryCtx = mkSummaryCtxSome(p, t, ap, stored) } pragma[nomagic] private predicate fwdFlowThroughStep0( - DataFlowCall call, ArgNd arg, Cc cc, FlowState state, CcCall ccc, SummaryCtx summaryCtx, - Typ t, Ap ap, TypOption stored, RetNd ret, SummaryCtxSome innerSummaryCtx + DataFlowCall call, ArgNd arg, Cc cc, CcCall ccc, SummaryCtx summaryCtx, Typ t, Ap ap, + TypOption stored, RetNd ret, SummaryCtxSome innerSummaryCtx ) { - fwdFlowThrough0(call, arg, cc, state, ccc, summaryCtx, t, ap, stored, ret, - innerSummaryCtx) + fwdFlowThrough0(call, arg, cc, ccc, summaryCtx, t, ap, stored, ret, innerSummaryCtx) } - bindingset[node, state, cc, summaryCtx, t, ap, stored] + bindingset[node, cc, summaryCtx, t, ap, stored] pragma[inline_late] private PathNodeImpl mkPathNode( - Nd node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored + Nd node, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored ) { - result = TPathNodeMid(node, state, cc, summaryCtx, t, ap, stored) + result = TPathNodeMid(node, cc, summaryCtx, t, ap, stored) } private PathNodeImpl typeStrengthenToPathNode( - Nd node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, TypOption stored + Nd node, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, TypOption stored ) { exists(Typ t | - fwdFlow1(node, state, cc, summaryCtx, t0, t, ap, stored) and - result = TPathNodeMid(node, state, cc, summaryCtx, t, ap, stored) + fwdFlow1(node, cc, summaryCtx, t0, t, ap, stored) and + result = TPathNodeMid(node, cc, summaryCtx, t, ap, stored) ) } pragma[nomagic] private predicate fwdFlowThroughStep1( PathNodeImpl pn1, PathNodeImpl pn2, PathNodeImpl pn3, DataFlowCall call, Cc cc, - FlowState state, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, RetNd ret + SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, RetNd ret ) { exists( - FlowState state0, ArgNd arg, SummaryCtxSome innerSummaryCtx, ParamNd p, Typ innerArgT, - Ap innerArgAp, TypOption innerArgStored, CcCall ccc + ArgNd arg, SummaryCtxSome innerSummaryCtx, ParamNd p, Typ innerArgT, Ap innerArgAp, + TypOption innerArgStored, CcCall ccc | - fwdFlowThroughStep0(call, arg, cc, state, ccc, summaryCtx, t, ap, stored, ret, + fwdFlowThroughStep0(call, arg, cc, ccc, summaryCtx, t, ap, stored, ret, innerSummaryCtx) and - innerSummaryCtx = TSummaryCtxSome(p, state0, innerArgT, innerArgAp, innerArgStored) and - pn1 = mkPathNode(arg, state0, cc, summaryCtx, innerArgT, innerArgAp, innerArgStored) and + innerSummaryCtx = TSummaryCtxSome(p, innerArgT, innerArgAp, innerArgStored) and + pn1 = mkPathNode(arg, cc, summaryCtx, innerArgT, innerArgAp, innerArgStored) and pn2 = - typeStrengthenToPathNode(p, state0, ccc, innerSummaryCtx, innerArgT, innerArgAp, + typeStrengthenToPathNode(p, ccc, innerSummaryCtx, innerArgT, innerArgAp, innerArgStored) and - pn3 = mkPathNode(ret, state, ccc, innerSummaryCtx, t, ap, stored) + pn3 = mkPathNode(ret, ccc, innerSummaryCtx, t, ap, stored) ) } pragma[nomagic] private predicate fwdFlowThroughStep2( - PathNodeImpl pn1, PathNodeImpl pn2, PathNodeImpl pn3, Nd node, Cc cc, FlowState state, + PathNodeImpl pn1, PathNodeImpl pn2, PathNodeImpl pn3, Nd node, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored ) { exists(DataFlowCall call, RetNd ret, boolean allowsFieldFlow | - fwdFlowThroughStep1(pn1, pn2, pn3, call, cc, state, summaryCtx, t, ap, stored, ret) and + fwdFlowThroughStep1(pn1, pn2, pn3, call, cc, summaryCtx, t, ap, stored, ret) and flowThroughOutOfCall(call, ret, node, allowsFieldFlow) and - not inBarrier(node, state) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } private predicate localStep( - PathNodeImpl pn1, Nd node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, - TypOption stored, string label, boolean isStoreStep + PathNodeImpl pn1, Nd node, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, + string label, boolean isStoreStep ) { - exists(Nd mid, FlowState state0, Typ t0, LocalCc localCc | - pn1 = TPathNodeMid(mid, state0, cc, summaryCtx, t0, ap, stored) and + exists(Nd mid, Typ t0, LocalCc localCc | + pn1 = TPathNodeMid(mid, cc, summaryCtx, t0, ap, stored) and localCc = getLocalCc(cc) and isStoreStep = false | - localStep(mid, state0, node, state, true, _, localCc, label) and + localStep(mid, node, true, _, localCc, label) and t = t0 or - localStep(mid, state0, node, state, false, t, localCc, label) and + localStep(mid, node, false, t, localCc, label) and ap instanceof ApNil ) or // store exists(Nd mid, Content c, Typ t0, Ap ap0, TypOption stored0 | - pn1 = TPathNodeMid(mid, state, cc, summaryCtx, t0, ap0, stored0) and - fwdFlowStore(mid, t0, ap0, stored0, c, t, stored, node, state, cc, summaryCtx) and + pn1 = TPathNodeMid(mid, cc, summaryCtx, t0, ap0, stored0) and + fwdFlowStore(mid, t0, ap0, stored0, c, t, stored, node, cc, summaryCtx) and ap = apCons(c, ap0) and label = "" and isStoreStep = true @@ -2170,8 +2091,8 @@ module MakeImpl Lang> { or // read exists(Nd mid, Typ t0, Ap ap0, TypOption stored0 | - pn1 = TPathNodeMid(mid, state, cc, summaryCtx, t0, ap0, stored0) and - fwdFlowRead(mid, t0, ap0, stored0, _, node, t, ap, stored, state, cc, summaryCtx) and + pn1 = TPathNodeMid(mid, cc, summaryCtx, t0, ap0, stored0) and + fwdFlowRead(mid, t0, ap0, stored0, _, node, t, ap, stored, cc, summaryCtx) and label = "" and isStoreStep = false ) @@ -2179,11 +2100,11 @@ module MakeImpl Lang> { private predicate localStep(PathNodeImpl pn1, PathNodeImpl pn2, string label) { exists( - Nd node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, - TypOption stored, boolean isStoreStep + Nd node, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, TypOption stored, + boolean isStoreStep | - localStep(pn1, node, state, cc, summaryCtx, t0, ap, stored, label, isStoreStep) and - pn2 = typeStrengthenToPathNode(node, state, cc, summaryCtx, t0, ap, stored) and + localStep(pn1, node, cc, summaryCtx, t0, ap, stored, label, isStoreStep) and + pn2 = typeStrengthenToPathNode(node, cc, summaryCtx, t0, ap, stored) and stepFilter(node, ap, isStoreStep) ) or @@ -2210,60 +2131,45 @@ module MakeImpl Lang> { } private predicate nonLocalStep( - PathNodeImpl pn1, Nd node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, - TypOption stored, string label + PathNodeImpl pn1, Nd node, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored, + string label ) { // jump - exists(Nd mid, FlowState state0, Typ t0 | - pn1 = TPathNodeMid(mid, state0, _, _, t0, ap, stored) and + exists(Nd mid, Typ t0 | + pn1 = TPathNodeMid(mid, _, _, t0, ap, stored) and cc = ccNone() and summaryCtx = TSummaryCtxNone() | jumpStepEx(mid, node) and - state = state0 and - not outBarrier(mid, state) and - not inBarrier(node, state) and t = t0 and label = "" or additionalJumpStep(mid, node, label) and - state = state0 and - not outBarrier(mid, state) and - not inBarrier(node, state) and - t = getNodeTyp(node) and - ap instanceof ApNil - or - additionalJumpStateStep(mid, state0, node, state, label) and t = getNodeTyp(node) and ap instanceof ApNil ) or // flow into a callable exists(ArgNd arg, Cc outercc, SummaryCtx outerSummaryCtx | - pn1 = TPathNodeMid(arg, state, outercc, outerSummaryCtx, t, ap, stored) and - fwdFlowInStep(arg, node, state, outercc, cc, outerSummaryCtx, summaryCtx, t, ap, - stored) and + pn1 = TPathNodeMid(arg, outercc, outerSummaryCtx, t, ap, stored) and + fwdFlowInStep(arg, node, outercc, cc, outerSummaryCtx, summaryCtx, t, ap, stored) and label = "" ) or // flow out of a callable exists(RetNd ret, CcNoCall innercc, boolean allowsFieldFlow | - pn1 = TPathNodeMid(ret, state, innercc, summaryCtx, t, ap, stored) and - fwdFlowIntoRet(ret, state, innercc, summaryCtx, t, ap, stored) and + pn1 = TPathNodeMid(ret, innercc, summaryCtx, t, ap, stored) and + fwdFlowIntoRet(ret, innercc, summaryCtx, t, ap, stored) and fwdFlowOutValidEdge(_, ret, innercc, _, node, cc, allowsFieldFlow) and - not inBarrier(node, state) and label = "" and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } private predicate nonLocalStep(PathNodeImpl pn1, PathNodeImpl pn2, string label) { - exists( - Nd node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, - TypOption stored - | - nonLocalStep(pn1, node, state, cc, summaryCtx, t0, ap, stored, label) and - pn2 = typeStrengthenToPathNode(node, state, cc, summaryCtx, t0, ap, stored) and + exists(Nd node, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, TypOption stored | + nonLocalStep(pn1, node, cc, summaryCtx, t0, ap, stored, label) and + pn2 = typeStrengthenToPathNode(node, cc, summaryCtx, t0, ap, stored) and stepFilter(node, ap, false) ) } @@ -2277,11 +2183,11 @@ module MakeImpl Lang> { PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out ) { exists( - Nd node, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, - TypOption stored, PathNodeImpl out0 + Nd node, Cc cc, SummaryCtx summaryCtx, Typ t0, Ap ap, TypOption stored, + PathNodeImpl out0 | - fwdFlowThroughStep2(arg, par, ret, node, cc, state, summaryCtx, t0, ap, stored) and - out0 = typeStrengthenToPathNode(node, state, cc, summaryCtx, t0, ap, stored) and + fwdFlowThroughStep2(arg, par, ret, node, cc, summaryCtx, t0, ap, stored) and + out0 = typeStrengthenToPathNode(node, cc, summaryCtx, t0, ap, stored) and stepFilter(node, ap, false) | out = out0 or out = out0.(PathNodeMid).projectToSink(_) @@ -2553,13 +2459,14 @@ module MakeImpl Lang> { int tfnodes, int tftuples ) { fwd = true and - nodes = count(NodeEx node | fwdFlow(any(Nd n | n.getNodeEx() = node), _, _, _, _, _, _)) and + nodes = count(NodeEx node | fwdFlow(any(Nd n | n.getNodeEx() = node), _, _, _, _, _)) and fields = count(Content f0 | fwdConsCand(f0, _)) and conscand = count(Content f0, Ap ap | fwdConsCand(f0, ap)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, _, _)) and + states = count(FlowState state | fwdFlow(any(Nd n | n.getState() = state), _, _, _, _, _)) and tuples = - count(Nd n, FlowState state, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, - TypOption stored | fwdFlow(n, state, cc, summaryCtx, t, ap, stored)) and + count(Nd n, Cc cc, SummaryCtx summaryCtx, Typ t, Ap ap, TypOption stored | + fwdFlow(n, cc, summaryCtx, t, ap, stored) + ) and calledges = count(DataFlowCall call, DataFlowCallable c | FwdTypeFlowInput::dataFlowTakenCallEdgeIn(call, c, _) or @@ -2568,13 +2475,13 @@ module MakeImpl Lang> { FwdTypeFlow::typeFlowStats(tfnodes, tftuples) or fwd = false and - nodes = count(NodeEx node | revFlow(any(Nd n | n.getNodeEx() = node), _, _, _, _)) and + nodes = count(NodeEx node | revFlow(any(Nd n | n.getNodeEx() = node), _, _, _)) and fields = count(Content f0 | consCand(f0, _)) and conscand = count(Content f0, Ap ap | consCand(f0, ap)) and - states = count(FlowState state | revFlow(_, state, _, _, _)) and + states = count(FlowState state | revFlow(any(Nd n | n.getState() = state), _, _, _)) and tuples = - count(Nd n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap | - revFlow(n, state, returnCtx, retAp, ap) + count(Nd n, ReturnCtx returnCtx, ApOption retAp, Ap ap | + revFlow(n, returnCtx, retAp, ap) ) and calledges = count(DataFlowCall call, DataFlowCallable c | @@ -2675,19 +2582,12 @@ module MakeImpl Lang> { import CachedCallContextSensitivity import NoLocalCallContext - bindingset[node1, state1] - bindingset[node2, state2] + bindingset[node1] + bindingset[node2] predicate localStep( - Nd node1, FlowState state1, Nd node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc, string label + Nd node1, Nd node2, boolean preservesValue, Typ t, LocalCc lcc, string label ) { - ( - localStepNodeCand1(node1, node2, preservesValue, _, _, label) and - state1 = state2 - or - localStateStepNodeCand1(node1, state1, node2, state2, _, _, label) and - preservesValue = false - ) and + localStep1(node1, node2, preservesValue, _, _, label) and exists(t) and exists(lcc) } @@ -2701,12 +2601,10 @@ module MakeImpl Lang> { ) } - bindingset[node, state, t0, ap] - predicate filter(Nd node, FlowState state, Typ t0, Ap ap, Typ t) { - PrevStage::revFlowState(state) and + bindingset[node, t0, ap] + predicate filter(Nd node, Typ t0, Ap ap, Typ t) { t0 = t and exists(ap) and - not stateBarrier(node, state) and ( Stage1::notExpectsContent(node) or @@ -2770,34 +2668,29 @@ module MakeImpl Lang> { import CallContextSensitivity import NoLocalCallContext - bindingset[node1, state1] - bindingset[node2, state2] + bindingset[node1] + bindingset[node2] private predicate localStepInput( - Nd node1, FlowState state1, Nd node2, FlowState state2, boolean preservesValue, - DataFlowType t, LocalCallContext lcc, string label + Nd node1, Nd node2, boolean preservesValue, DataFlowType t, LocalCallContext lcc, + string label ) { - localStepNodeCand1(node1, node2, preservesValue, t, lcc, label) and - state1 = state2 - or - localStateStepNodeCand1(node1, state1, node2, state2, t, lcc, label) and - preservesValue = false + localStep1(node1, node2, preservesValue, t, lcc, label) } additional predicate localFlowBigStep( - Nd node1, FlowState state1, Nd node2, FlowState state2, boolean preservesValue, - DataFlowType t, LocalCallContext lcc, string label + Nd node1, Nd node2, boolean preservesValue, DataFlowType t, LocalCallContext lcc, + string label ) { - PrevStage::LocalFlowBigStep::localFlowBigStep(node1, state1, node2, - state2, preservesValue, t, lcc, label) + PrevStage::LocalFlowBigStep::localFlowBigStep(node1, node2, + preservesValue, t, lcc, label) } - bindingset[node1, state1] - bindingset[node2, state2] + bindingset[node1] + bindingset[node2] predicate localStep( - Nd node1, FlowState state1, Nd node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc, string label + Nd node1, Nd node2, boolean preservesValue, Typ t, LocalCc lcc, string label ) { - localFlowBigStep(node1, state1, node2, state2, preservesValue, _, _, label) and + localFlowBigStep(node1, node2, preservesValue, _, _, label) and exists(t) and exists(lcc) } @@ -2812,9 +2705,8 @@ module MakeImpl Lang> { ) } - bindingset[node, state, t0, ap] - predicate filter(Nd node, FlowState state, Typ t0, Ap ap, Typ t) { - exists(state) and + bindingset[node, t0, ap] + predicate filter(Nd node, Typ t0, Ap ap, Typ t) { t0 = t and ( Stage1::notExpectsContent(node) @@ -2879,12 +2771,11 @@ module MakeImpl Lang> { pragma[nomagic] predicate localStep( - Nd node1, FlowState state1, Nd node2, FlowState state2, boolean preservesValue, Typ t, - LocalCc lcc, string label + Nd node1, Nd node2, boolean preservesValue, Typ t, LocalCc lcc, string label ) { - Stage3Param::localFlowBigStep(node1, state1, node2, state2, preservesValue, _, _, label) and - PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and - PrevStage::revFlow(node2, pragma[only_bind_into](state2), _) and + Stage3Param::localFlowBigStep(node1, node2, preservesValue, _, _, label) and + PrevStage::revFlow(node1) and + PrevStage::revFlow(node2) and exists(t) and exists(lcc) } @@ -2928,9 +2819,8 @@ module MakeImpl Lang> { ) } - bindingset[node, state, t0, ap] - predicate filter(Nd node, FlowState state, Typ t0, Ap ap, Typ t) { - exists(state) and + bindingset[node, t0, ap] + predicate filter(Nd node, Typ t0, Ap ap, Typ t) { not clear(node, ap) and t0 = t and ( @@ -2959,10 +2849,10 @@ module MakeImpl Lang> { exists(int tails, int nodes, int apLimit, int tupleLimit | tails = strictcount(AccessPathFront apf | Stage4::consCand(c, apf)) and nodes = - strictcount(Nd n, FlowState state | - Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) + strictcount(Nd n | + Stage4::revFlow(n, any(AccessPathFrontHead apf | apf.getHead() = c)) or - Stage4::nodeMayUseSummary(n, state, any(AccessPathFrontHead apf | apf.getHead() = c)) + Stage4::nodeMayUseSummary(n, any(AccessPathFrontHead apf | apf.getHead() = c)) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and @@ -3163,12 +3053,11 @@ module MakeImpl Lang> { import LocalCallContext predicate localStep = - PrevStage::LocalFlowBigStep::localFlowBigStepTc/8; + PrevStage::LocalFlowBigStep::localFlowBigStepTc/6; - bindingset[node, state, t0, ap] - predicate filter(Nd node, FlowState state, Typ t0, Ap ap, Typ t) { + bindingset[node, t0, ap] + predicate filter(Nd node, Typ t0, Ap ap, Typ t) { strengthenType(node, t0, t) and - exists(state) and exists(ap) } @@ -3205,10 +3094,7 @@ module MakeImpl Lang> { } private int countNodesUsingAccessPath(AccessPathApprox apa) { - result = - strictcount(Nd n, FlowState state | - Stage5::revFlow(n, state, apa) or Stage5::nodeMayUseSummary(n, state, apa) - ) + result = strictcount(Nd n | Stage5::revFlow(n, apa) or Stage5::nodeMayUseSummary(n, apa)) } /** @@ -3360,12 +3246,11 @@ module MakeImpl Lang> { import LocalCallContext predicate localStep = - PrevStage::LocalFlowBigStep::localFlowBigStepTc/8; + PrevStage::LocalFlowBigStep::localFlowBigStepTc/6; - bindingset[node, state, t0, ap] - predicate filter(Nd node, FlowState state, Typ t0, Ap ap, Typ t) { + bindingset[node, t0, ap] + predicate filter(Nd node, Typ t0, Ap ap, Typ t) { strengthenType(node, t0, t) and - exists(state) and exists(ap) } diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll index f54412d8fd85..2fbabf1e9eff 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll @@ -855,6 +855,8 @@ module MakeImplCommon Lang> { final class NodeEx extends TNodeEx { NodeEx getNodeEx() { result = this } + Unit getState() { any() } + string toString() { result = this.asNode().toString() or diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll index 0475c9b1000f..00a934a727e1 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll @@ -27,6 +27,8 @@ module MakeImplStage1 Lang> { class Nd { NodeEx getNodeEx(); + FlowState getState(); + string toString(); Location getLocation(); @@ -50,18 +52,12 @@ module MakeImplStage1 Lang> { class CastingNd extends Nd; - predicate inBarrier(Nd node, FlowState state); - - predicate outBarrier(Nd node, FlowState state); - - predicate stateBarrier(Nd node, FlowState state); - /** If `node` corresponds to a sink, gets the normal node for that sink. */ Nd toNormalSinkNode(Nd node); - predicate sourceNode(Nd node, FlowState state); + predicate sourceNode(Nd node); - predicate sinkNode(Nd node, FlowState state); + predicate sinkNode(Nd node); predicate hasSourceCallCtx(); @@ -71,16 +67,11 @@ module MakeImplStage1 Lang> { predicate additionalJumpStep(Nd node1, Nd node2, string model); - predicate additionalJumpStateStep(Nd node1, FlowState s1, Nd node2, FlowState s2, string model); - - predicate localStepNodeCand1( + predicate localStep1( Nd node1, Nd node2, boolean preservesValue, DataFlowType t, LocalCallContext lcc, string label ); - predicate localStateStepNodeCand1( - Nd node1, FlowState state1, Nd node2, FlowState state2, DataFlowType t, LocalCallContext lcc, - string label - ); + predicate isStateStep(Nd node1, Nd node2); bindingset[c] predicate expectsContentEx(Nd n, Content c); @@ -97,8 +88,8 @@ module MakeImplStage1 Lang> { predicate revFlow(Nd node); - bindingset[node, state] - predicate revFlow(Nd node, FlowState state, Ap ap); + bindingset[node] + predicate revFlow(Nd node, Ap ap); predicate callMayFlowThroughRev(DataFlowCall call); @@ -131,8 +122,6 @@ module MakeImplStage1 Lang> { predicate stats( boolean fwd, int nodes, int fields, int conscand, int states, int tuples, int calledges ); - - predicate revFlowState(FlowState state); } module ImplStage1 { @@ -870,13 +859,6 @@ module MakeImplStage1 Lang> { pragma[nomagic] predicate revFlow(NodeEx node) { revFlow(node, _) } - bindingset[node, state] - predicate revFlow(NodeEx node, FlowState state, Ap ap) { - revFlow(node, _) and - exists(state) and - exists(ap) - } - private predicate throughFlowNodeCand(NodeEx node) { revFlow(node, true) and fwdFlow(node, true) and @@ -1231,25 +1213,12 @@ module MakeImplStage1 Lang> { ) } - private predicate inBarrierAlias = inBarrier/2; - - private predicate outBarrierAlias = outBarrier/2; - - private predicate stateBarrierAlias = stateBarrier/2; - - private predicate sourceNodeAlias = sourceNode/2; - private predicate jumpStepExAlias = jumpStepEx/2; private predicate additionalJumpStepAlias = additionalJumpStep/3; - private predicate additionalJumpStateStepAlias = additionalJumpStateStep/5; - private predicate localStepNodeCand1Alias = localStepNodeCand1/6; - - private predicate localStateStepNodeCand1Alias = localStateStepNodeCand1/7; - - module Stage1NoState implements Stage1Output { + module Stage1NoState implements Stage1Output { class Nd = NodeEx; class ArgNd = ArgNodeEx; @@ -1262,12 +1231,6 @@ module MakeImplStage1 Lang> { class CastingNd = CastingNodeEx; - predicate inBarrier = inBarrierAlias/2; - - predicate outBarrier = outBarrierAlias/2; - - predicate stateBarrier = stateBarrierAlias/2; - // inline to reduce fan-out via `getAReadContent` bindingset[c] predicate expectsContentEx(NodeEx n, Content c) { @@ -1285,23 +1248,24 @@ module MakeImplStage1 Lang> { import Stage1 import Stage1Common + predicate revFlow(NodeEx node, Ap ap) { Stage1::revFlow(node) and exists(ap) } + predicate toNormalSinkNode = toNormalSinkNodeEx/1; - predicate sourceNode = sourceNodeAlias/2; + predicate sourceNode(NodeEx node) { sourceNode(node, _) } + + predicate sinkNode(NodeEx node) { sinkNode(node, _) } predicate jumpStepEx = jumpStepExAlias/2; predicate additionalJumpStep = additionalJumpStepAlias/3; - predicate additionalJumpStateStep = additionalJumpStateStepAlias/5; - - predicate localStepNodeCand1 = localStepNodeCand1Alias/6; + predicate localStep1 = localStepNodeCand1/6; - predicate localStateStepNodeCand1 = localStateStepNodeCand1Alias/7; + predicate isStateStep(NodeEx node1, NodeEx node2) { none() } } - // TODO: implements Stage1Output - module Stage1WithState { + module Stage1WithState implements Stage1Output { private predicate flowState(NodeEx node, FlowState state) { Stage1::revFlow(node) and Stage1::revFlowState(state) and From e0cb70a492dc98928cd4378cd11fcad76e62129e Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 30 Jan 2025 11:55:20 +0100 Subject: [PATCH 08/14] Dataflow: Minor cleanup. --- .../codeql/dataflow/internal/DataFlowImpl.qll | 22 ++----------------- .../dataflow/internal/DataFlowImplStage1.qll | 1 - 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll index b271694e1609..f5d0568e4ef0 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll @@ -246,7 +246,6 @@ module MakeImpl Lang> { predicate revFlow(Nd node); - bindingset[node] predicate revFlow(Nd node, Ap ap); predicate callMayFlowThroughRev(DataFlowCall call); @@ -361,8 +360,6 @@ module MakeImpl Lang> { bindingset[cc] LocalCc getLocalCc(Cc cc); - bindingset[node1] - bindingset[node2] predicate localStep( Nd node1, Nd node2, boolean preservesValue, Typ t, LocalCc lcc, string label ); @@ -1504,8 +1501,6 @@ module MakeImpl Lang> { } /** Holds if `node1` can step to `node2` in one or more local steps. */ - bindingset[node1] - bindingset[node2] signature predicate localStepSig( Nd node1, Nd node2, boolean preservesValue, DataFlowType t, LocalCallContext lcc, string label @@ -2582,8 +2577,6 @@ module MakeImpl Lang> { import CachedCallContextSensitivity import NoLocalCallContext - bindingset[node1] - bindingset[node2] predicate localStep( Nd node1, Nd node2, boolean preservesValue, Typ t, LocalCc lcc, string label ) { @@ -2668,25 +2661,14 @@ module MakeImpl Lang> { import CallContextSensitivity import NoLocalCallContext - bindingset[node1] - bindingset[node2] - private predicate localStepInput( - Nd node1, Nd node2, boolean preservesValue, DataFlowType t, LocalCallContext lcc, - string label - ) { - localStep1(node1, node2, preservesValue, t, lcc, label) - } - additional predicate localFlowBigStep( Nd node1, Nd node2, boolean preservesValue, DataFlowType t, LocalCallContext lcc, string label ) { - PrevStage::LocalFlowBigStep::localFlowBigStep(node1, node2, - preservesValue, t, lcc, label) + PrevStage::LocalFlowBigStep::localFlowBigStep(node1, node2, preservesValue, t, + lcc, label) } - bindingset[node1] - bindingset[node2] predicate localStep( Nd node1, Nd node2, boolean preservesValue, Typ t, LocalCc lcc, string label ) { diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll index 00a934a727e1..62984d112019 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll @@ -88,7 +88,6 @@ module MakeImplStage1 Lang> { predicate revFlow(Nd node); - bindingset[node] predicate revFlow(Nd node, Ap ap); predicate callMayFlowThroughRev(DataFlowCall call); From b2d42ee49a57848df37385101bfeb59da63180db Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 30 Jan 2025 11:57:21 +0100 Subject: [PATCH 09/14] Dataflow: Rename two predicates to remove need for alias defs. --- .../dataflow/internal/DataFlowImplStage1.qll | 41 ++++++++----------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll index 62984d112019..110141777f5e 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll @@ -351,7 +351,7 @@ module MakeImplStage1 Lang> { /** * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ - private predicate jumpStepEx(NodeEx node1, NodeEx node2) { + private predicate jumpStepEx1(NodeEx node1, NodeEx node2) { exists(Node n1, Node n2 | node1.asNode() = n1 and node2.asNode() = n2 and @@ -364,7 +364,7 @@ module MakeImplStage1 Lang> { /** * Holds if the additional step from `node1` to `node2` jumps between callables. */ - private predicate additionalJumpStep(NodeEx node1, NodeEx node2, string model) { + private predicate additionalJumpStep1(NodeEx node1, NodeEx node2, string model) { exists(Node n1, Node n2 | node1.asNodeOrImplicitRead() = n1 and node2.asNode() = n2 and @@ -478,8 +478,8 @@ module MakeImplStage1 Lang> { ) or exists(NodeEx mid | fwdFlow(mid, _) and cc = false | - jumpStepEx(mid, node) or - additionalJumpStep(mid, node, _) or + jumpStepEx1(mid, node) or + additionalJumpStep1(mid, node, _) or additionalJumpStateStep(mid, _, node, _, _) ) or @@ -693,8 +693,8 @@ module MakeImplStage1 Lang> { ) or exists(NodeEx mid | revFlow(mid, _) and toReturn = false | - jumpStepEx(node, mid) or - additionalJumpStep(node, mid, _) or + jumpStepEx1(node, mid) or + additionalJumpStep1(node, mid, _) or additionalJumpStateStep(node, _, mid, _, _) ) or @@ -1212,11 +1212,6 @@ module MakeImplStage1 Lang> { ) } - private predicate jumpStepExAlias = jumpStepEx/2; - - private predicate additionalJumpStepAlias = additionalJumpStep/3; - - module Stage1NoState implements Stage1Output { class Nd = NodeEx; @@ -1255,9 +1250,9 @@ module MakeImplStage1 Lang> { predicate sinkNode(NodeEx node) { sinkNode(node, _) } - predicate jumpStepEx = jumpStepExAlias/2; + predicate jumpStepEx = jumpStepEx1/2; - predicate additionalJumpStep = additionalJumpStepAlias/3; + predicate additionalJumpStep = additionalJumpStep1/3; predicate localStep1 = localStepNodeCand1/6; @@ -1280,8 +1275,8 @@ module MakeImplStage1 Lang> { exists(NodeEx mid | flowState(mid, state) | localFlowStepEx(mid, node, _) or additionalLocalFlowStep(mid, node, _) or - jumpStepExAlias(mid, node) or - additionalJumpStepAlias(mid, node, _) or + jumpStepEx1(mid, node) or + additionalJumpStep1(mid, node, _) or store(mid, _, node, _, _) or readSetEx(mid, _, node) or flowIntoCallNodeCand1(_, mid, node) or @@ -1437,7 +1432,7 @@ module MakeImplStage1 Lang> { predicate jumpStepEx(Nd node1, Nd node2) { exists(NodeEx n1, NodeEx n2, FlowState s | - jumpStepExAlias(n1, n2) and + jumpStepEx1(n1, n2) and node1 = TNodeState(n1, pragma[only_bind_into](s)) and node2 = TNodeState(n2, pragma[only_bind_into](s)) and not outBarrier(n1, s) and @@ -1447,7 +1442,7 @@ module MakeImplStage1 Lang> { predicate additionalJumpStep(Nd node1, Nd node2, string model) { exists(NodeEx n1, NodeEx n2, FlowState s | - additionalJumpStepAlias(n1, n2, model) and + additionalJumpStep1(n1, n2, model) and node1 = TNodeState(n1, pragma[only_bind_into](s)) and node2 = TNodeState(n2, pragma[only_bind_into](s)) and not outBarrier(n1, s) and @@ -1529,9 +1524,9 @@ module MakeImplStage1 Lang> { private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2) { exists(NodeEx node1, NodeEx node2 | - jumpStepEx(node1, node2) + jumpStepEx1(node1, node2) or - additionalJumpStep(node1, node2, _) + additionalJumpStep1(node1, node2, _) or additionalJumpStateStep(node1, _, node2, _, _) or @@ -2012,7 +2007,7 @@ module MakeImplStage1 Lang> { ) and isStoreStep = false or - jumpStepEx(mid.getNodeEx(), node) and + jumpStepEx1(mid.getNodeEx(), node) and state = mid.getState() and cc = callContextNone() and sc1 = TSummaryCtx1None() and @@ -2023,7 +2018,7 @@ module MakeImplStage1 Lang> { ap = mid.getAp() and isStoreStep = false or - additionalJumpStep(mid.getNodeEx(), node, _) and + additionalJumpStep1(mid.getNodeEx(), node, _) and state = mid.getState() and cc = callContextNone() and sc1 = TSummaryCtx1None() and @@ -2314,7 +2309,7 @@ module MakeImplStage1 Lang> { ap = TPartialNil() and isStoreStep = false or - jumpStepEx(node, mid.getNodeEx()) and + jumpStepEx1(node, mid.getNodeEx()) and state = mid.getState() and sc1 = TRevSummaryCtx1None() and sc2 = TRevSummaryCtx2None() and @@ -2322,7 +2317,7 @@ module MakeImplStage1 Lang> { ap = mid.getAp() and isStoreStep = false or - additionalJumpStep(node, mid.getNodeEx(), _) and + additionalJumpStep1(node, mid.getNodeEx(), _) and state = mid.getState() and sc1 = TRevSummaryCtx1None() and sc2 = TRevSummaryCtx2None() and From 2597ef651be92e10420a13b40c788e3ef35ce9dd Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Tue, 28 Jan 2025 15:33:22 +0100 Subject: [PATCH 10/14] Dataflow: Avoid duplication in fwdFlow1 disjunction. --- shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll index f5d0568e4ef0..ed52b473c4b4 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll @@ -415,7 +415,7 @@ module MakeImpl Lang> { private predicate compatibleContainer0(ApHeadContent apc, DataFlowType containerType) { exists(DataFlowType containerType0, Content c | PrevStage::storeStepCand(_, c, _, _, containerType0) and - not isTopType(containerType0) and + not topTypeContent(apc) and compatibleTypesCached(containerType0, containerType) and apc = projectToHeadContent(c) ) From e55130ebceeee4d7d0c8f73b52e3d5467f7ec465 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 30 Jan 2025 12:43:05 +0100 Subject: [PATCH 11/14] Dataflow: Remove unused predicate. --- shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll | 2 -- 1 file changed, 2 deletions(-) diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll index 110141777f5e..ef68fcf4d22e 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll @@ -1520,8 +1520,6 @@ module MakeImplStage1 Lang> { predicate callContextNone = CachedCallContextSensitivity::ccNone/0; - predicate callContextSomeCall = CachedCallContextSensitivity::ccSomeCall/0; - private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2) { exists(NodeEx node1, NodeEx node2 | jumpStepEx1(node1, node2) From db1ed67e52116b2262bed470db16dd4e65c1d74d Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Fri, 31 Jan 2025 14:59:39 +0100 Subject: [PATCH 12/14] JS: Simplify config in PrototypePollutingFunction.ql. --- .../CWE-915/PrototypePollutingFunction.ql | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/javascript/ql/src/Security/CWE-915/PrototypePollutingFunction.ql b/javascript/ql/src/Security/CWE-915/PrototypePollutingFunction.ql index cb4709d5fccd..c8a1585c356d 100644 --- a/javascript/ql/src/Security/CWE-915/PrototypePollutingFunction.ql +++ b/javascript/ql/src/Security/CWE-915/PrototypePollutingFunction.ql @@ -251,25 +251,19 @@ module PropNameTrackingConfig implements DataFlow::StateConfigSig { node = DataFlow::MakeStateBarrierGuard::getABarrierNode(state) } - predicate isAdditionalFlowStep( - DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2 - ) { - exists(state1) and - state2 = state1 and - ( - // Step through `p -> x[p]` - exists(DataFlow::PropRead read | - node1 = read.getPropertyNameExpr().flow() and - not read.(DynamicPropRead).hasDominatingAssignment() and - node2 = read - ) - or - // Step through `x -> x[p]` - exists(DynamicPropRead read | - not read.hasDominatingAssignment() and - node1 = read.getBase() and - node2 = read - ) + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + // Step through `p -> x[p]` + exists(DataFlow::PropRead read | + node1 = read.getPropertyNameExpr().flow() and + not read.(DynamicPropRead).hasDominatingAssignment() and + node2 = read + ) + or + // Step through `x -> x[p]` + exists(DynamicPropRead read | + not read.hasDominatingAssignment() and + node1 = read.getBase() and + node2 = read ) } From da34c0b3aceb0c68f93df9faf725c72aea5ca410 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Fri, 31 Jan 2025 15:13:12 +0100 Subject: [PATCH 13/14] Dataflow: Fixup some qldoc. --- shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll | 3 ++- .../dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll index ed52b473c4b4..6883b385284e 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll @@ -169,7 +169,8 @@ module MakeImpl Lang> { } /** - * Constructs a data flow computation given a full input configuration. + * Constructs a data flow computation given a full input configuration, and + * an initial stage 1 pruning. */ module Impl Stage1> { private class FlowState = Config::FlowState; diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll index ef68fcf4d22e..7d786f5fa9f2 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll @@ -460,7 +460,6 @@ module MakeImplStage1 Lang> { private class Cc = boolean; - /* Begin: Stage 1 logic. */ /** * Holds if `node` is reachable from a source. * @@ -954,7 +953,6 @@ module MakeImplStage1 Lang> { callEdgeReturn(call, c, _, _, _, _) ) } - /* End: Stage 1 logic. */ } private module Stage1Common { From 73d7250688577bfa814f9e1cd8036fa056036e36 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Mon, 3 Feb 2025 10:47:50 +0100 Subject: [PATCH 14/14] Dataflow: Fix join-order issue. --- .../dataflow/internal/DataFlowImplStage1.qll | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll index 7d786f5fa9f2..d709571fad04 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll @@ -1356,14 +1356,18 @@ module MakeImplStage1 Lang> { Stage1::returnMayFlowThrough(ret.getNodeEx(), kind) } + bindingset[node] + pragma[inline_late] + private Nd mkNodeState(NodeEx node, FlowState state) { result = TNodeState(node, state) } + pragma[nomagic] predicate storeStepCand( Nd node1, Content c, Nd node2, DataFlowType contentType, DataFlowType containerType ) { exists(NodeEx n1, NodeEx n2, FlowState s | Stage1::storeStepCand(n1, c, n2, contentType, containerType) and - node1 = TNodeState(n1, pragma[only_bind_into](s)) and - node2 = TNodeState(n2, pragma[only_bind_into](s)) and + node1 = mkNodeState(n1, s) and + node2 = mkNodeState(n2, s) and not outBarrier(n1, s) and not inBarrier(n2, s) ) @@ -1373,8 +1377,8 @@ module MakeImplStage1 Lang> { predicate readStepCand(Nd node1, Content c, Nd node2) { exists(NodeEx n1, NodeEx n2, FlowState s | Stage1::readStepCand(n1, c, n2) and - node1 = TNodeState(n1, pragma[only_bind_into](s)) and - node2 = TNodeState(n2, pragma[only_bind_into](s)) and + node1 = mkNodeState(n1, s) and + node2 = mkNodeState(n2, s) and not outBarrier(n1, s) and not inBarrier(n2, s) ) @@ -1385,8 +1389,8 @@ module MakeImplStage1 Lang> { ) { exists(ArgNodeEx arg0, ParamNodeEx p0, FlowState s | Stage1::callEdgeArgParam(call, c, arg0, p0, emptyAp) and - arg = TNodeState(arg0, pragma[only_bind_into](s)) and - p = TNodeState(p0, pragma[only_bind_into](s)) and + arg = mkNodeState(arg0, s) and + p = mkNodeState(p0, s) and not outBarrier(arg0, s) and not inBarrier(p0, s) ) @@ -1398,8 +1402,8 @@ module MakeImplStage1 Lang> { ) { exists(RetNodeEx ret0, NodeEx out0, FlowState s | Stage1::callEdgeReturn(call, c, ret0, kind, out0, allowsFieldFlow) and - ret = TNodeState(ret0, pragma[only_bind_into](s)) and - out = TNodeState(out0, pragma[only_bind_into](s)) and + ret = mkNodeState(ret0, s) and + out = mkNodeState(out0, s) and not outBarrier(ret0, s) and not inBarrier(out0, s) ) @@ -1409,8 +1413,8 @@ module MakeImplStage1 Lang> { Nd toNormalSinkNode(Nd node) { exists(NodeEx res, NodeEx n, FlowState s | res = toNormalSinkNodeEx(n) and - node = TNodeState(n, pragma[only_bind_into](s)) and - result = TNodeState(res, pragma[only_bind_into](s)) + node = mkNodeState(n, s) and + result = mkNodeState(res, s) ) } @@ -1431,8 +1435,8 @@ module MakeImplStage1 Lang> { predicate jumpStepEx(Nd node1, Nd node2) { exists(NodeEx n1, NodeEx n2, FlowState s | jumpStepEx1(n1, n2) and - node1 = TNodeState(n1, pragma[only_bind_into](s)) and - node2 = TNodeState(n2, pragma[only_bind_into](s)) and + node1 = mkNodeState(n1, s) and + node2 = mkNodeState(n2, s) and not outBarrier(n1, s) and not inBarrier(n2, s) ) @@ -1441,16 +1445,16 @@ module MakeImplStage1 Lang> { predicate additionalJumpStep(Nd node1, Nd node2, string model) { exists(NodeEx n1, NodeEx n2, FlowState s | additionalJumpStep1(n1, n2, model) and - node1 = TNodeState(n1, pragma[only_bind_into](s)) and - node2 = TNodeState(n2, pragma[only_bind_into](s)) and + node1 = mkNodeState(n1, s) and + node2 = mkNodeState(n2, s) and not outBarrier(n1, s) and not inBarrier(n2, s) ) or exists(NodeEx n1, FlowState s1, NodeEx n2, FlowState s2 | additionalJumpStateStep(n1, s1, n2, s2, model) and - node1 = TNodeState(n1, s1) and - node2 = TNodeState(n2, s2) + node1 = mkNodeState(n1, s1) and + node2 = mkNodeState(n2, s2) ) } @@ -1461,8 +1465,8 @@ module MakeImplStage1 Lang> { ) { exists(NodeEx n1, NodeEx n2, FlowState s | localStepNodeCand1(n1, n2, preservesValue, t, lcc, label) and - node1 = TNodeState(n1, pragma[only_bind_into](s)) and - node2 = TNodeState(n2, pragma[only_bind_into](s)) and + node1 = mkNodeState(n1, s) and + node2 = mkNodeState(n2, s) and not outBarrier(n1, s) and not inBarrier(n2, s) ) @@ -1470,8 +1474,8 @@ module MakeImplStage1 Lang> { exists(NodeEx n1, NodeEx n2, FlowState s1, FlowState s2 | localStateStepNodeCand1(n1, s1, n2, s2, t, lcc, label) and preservesValue = false and - node1 = TNodeState(n1, s1) and - node2 = TNodeState(n2, s2) + node1 = mkNodeState(n1, s1) and + node2 = mkNodeState(n2, s2) ) }