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 debaa1fa524..6ebb914e3de 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 | 2149 +--------------- .../dataflow/internal/DataFlowImplStage1.qll | 2280 +++++++++++++++++ 4 files changed, 2365 insertions(+), 2115 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 0b6ed84da36..b74ea9a0e08 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 491d7794382..762583528d4 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 2b69e583d28..9cfc73ce3e5 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,879 +171,33 @@ 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() - } + private predicate stateBarrier = Stage1::stateBarrier/2; - 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() - ) - } - - // 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() - ) - } - - pragma[nomagic] - private predicate notExpectsContent(NodeEx n) { not expectsContentSet(n, _) } - - 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 - } - - private predicate hasSourceCallCtx() { - exists(FlowFeature feature | feature = Config::getAFeature() | - feature instanceof FeatureHasSourceCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) - } - - private predicate hasSinkCallCtx() { - exists(FlowFeature feature | feature = Config::getAFeature() | - feature instanceof FeatureHasSinkCallContext or - feature instanceof FeatureEqualSourceSinkCallContext - ) - } - - /** - * 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) - ) - } - - private module Stage1 implements StageSig { - 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) - ) - } - - 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) - } - - /** - * 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) - } - - 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, _, _) - } - - pragma[nomagic] - additional 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] - additional 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] - 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) - ) - } - - 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 - parameterFlowThroughAllowed(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, _, _, _, _) - } - - 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 sourceNode = Stage1::sourceNode/2; private predicate sinkNode = Stage1::sinkNode/2; + private predicate hasSourceCallCtx = Stage1::hasSourceCallCtx/0; + + private predicate hasSinkCallCtx = Stage1::hasSinkCallCtx/0; + + private predicate jumpStepEx = Stage1::jumpStepEx/2; + + private predicate additionalJumpStep = Stage1::additionalJumpStep/3; + + private predicate additionalJumpStateStep = Stage1::additionalJumpStateStep/5; + + private predicate localStepNodeCand1 = Stage1::localStepNodeCand1/6; + + private predicate localStateStepNodeCand1 = Stage1::localStateStepNodeCand1/7; + private predicate sourceModel(NodeEx node, string model) { sourceNode(node, _) and ( @@ -1074,231 +230,9 @@ module MakeImpl Lang> { else result = label1 } - 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 - ) - } - pragma[nomagic] private predicate allowsFieldFlowThrough(DataFlowCall call, DataFlowCallable c) { - exists(RetNodeEx ret | - flowOutOfCallNodeCand1(call, ret, _, _, true) and - c = ret.getEnclosingCallable() - ) - } - - /** - * 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 - ) + Stage1::callEdgeReturn(call, c, _, _, _, true) } private signature module StageSig { @@ -2108,7 +1042,7 @@ module MakeImpl Lang> { 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 + Stage1::parameterFlowThroughAllowed(p, kind) and PrevStage::returnMayFlowThrough(ret, kind) ) } @@ -2434,7 +1368,7 @@ module MakeImpl Lang> { ) { revFlow(pragma[only_bind_into](p), state, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), pragma[only_bind_into](ap)) and - parameterFlowThroughAllowed(p, pos.getKind()) and + Stage1::parameterFlowThroughAllowed(p, pos.getKind()) and PrevStage::parameterMayFlowThrough(p, isNil(ap)) } @@ -2518,7 +1452,7 @@ module MakeImpl Lang> { ParamNodeEx p, Ap ap, ReturnPosition pos, Ap returnAp ) { revFlow(p, _, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap) and - parameterFlowThroughAllowed(p, pos.getKind()) + Stage1::parameterFlowThroughAllowed(p, pos.getKind()) } pragma[nomagic] @@ -2727,7 +1661,7 @@ module MakeImpl Lang> { | additionalJumpStateStep(node, state, next, s, _) or - additionalLocalStateStep(node, state, next, s, _) and + additionalLocalStateStep(node, state, next, s, _, _, _) and s != state ) or @@ -3003,7 +1937,7 @@ module MakeImpl Lang> { NodeEx toNormalSinkNodeEx() { exists(Node n | pragma[only_bind_out](node.asNodeOrImplicitRead()) = n and - (isRelevantSink(n) or isRelevantSink(n, _)) and + (Stage1::isRelevantSink(n) or Stage1::isRelevantSink(n, _)) and result.asNode() = n ) } @@ -3582,7 +2516,7 @@ module MakeImpl Lang> { // 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()) + then Stage1::isRelevantSourceSinkPair(source.getNode(), sink.getNode()) else any() ) and exists(PathNodeImpl flowsource, PathNodeImpl flowsink | @@ -3705,7 +2639,11 @@ module MakeImpl Lang> { CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) { any() } } - private module Stage2Param implements MkStage::StageParam { + private module S1 implements StageSig { + import Stage1 + } + + private module Stage2Param implements MkStage::StageParam { private module PrevStage = Stage1; class Typ = Unit; @@ -3766,7 +2704,7 @@ module MakeImpl Lang> { exists(Content c | PrevStage::revFlow(node) and PrevStage::revFlowIsReadAndStored(c) and - expectsContentEx(node, c) + Stage1::expectsContentEx(node, c) ) } @@ -3777,7 +2715,7 @@ module MakeImpl Lang> { exists(ap) and not stateBarrier(node, state) and ( - notExpectsContent(node) + Stage1::notExpectsContent(node) or ap = true and expectsContentCand(node) @@ -3793,7 +2731,7 @@ module MakeImpl Lang> { predicate enableTypeFlow() { none() } } - private module Stage2 = MkStage::Stage; + private module Stage2 = MkStage::Stage; private module Stage3Param implements MkStage::StageParam { private module PrevStage = Stage2; @@ -3876,7 +2814,7 @@ module MakeImpl Lang> { exists(Content c | PrevStage::revFlow(node) and PrevStage::readStepCand(_, c, _) and - expectsContentEx(node, c) and + Stage1::expectsContentEx(node, c) and c = ap.getAHead() ) } @@ -3886,7 +2824,7 @@ module MakeImpl Lang> { exists(state) and t0 = t and ( - notExpectsContent(node) + Stage1::notExpectsContent(node) or expectsContentCand(node, ap) ) @@ -3994,7 +2932,7 @@ module MakeImpl Lang> { exists(Content c | PrevStage::revFlow(node) and PrevStage::readStepCand(_, c, _) and - expectsContentEx(node, c) and + Stage1::expectsContentEx(node, c) and c = ap.getHead() ) } @@ -4005,7 +2943,7 @@ module MakeImpl Lang> { not clear(node, ap) and t0 = t and ( - notExpectsContent(node) + Stage1::notExpectsContent(node) or expectsContentCand(node, ap) ) @@ -4767,1012 +3705,5 @@ module MakeImpl Lang> { Stage6::stats(false, nodes, fields, conscand, states, tuples, calledges, tfnodes, tftuples) } } - - 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; - } - - 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) - } - - 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()) - ) - } - - 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 - ( - 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()) - ) - ) - } - - 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() - } - } } } diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll new file mode 100644 index 00000000000..86c8c2b25e1 --- /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 9cfc73ce3e5..6a2f7f2a0e0 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 6a2f7f2a0e0..3d76de1014e 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 86c8c2b25e1..b2142cfbe83 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 3d76de1014e..6bf5cb894f6 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 6ebb914e3de..f54412d8fd8 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 b2142cfbe83..7f1faf6d3c6 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 7f1faf6d3c6..0475c9b1000 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 b74ea9a0e08..0d78d13c884 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 762583528d4..b08f1e4af46 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 6bf5cb894f6..b271694e160 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 f54412d8fd8..2fbabf1e9ef 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 0475c9b1000..00a934a727e 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 localStep1 = localStepNodeCand1/6; - predicate localStepNodeCand1 = localStepNodeCand1Alias/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 b271694e160..f5d0568e4ef 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 00a934a727e..62984d11201 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 62984d11201..110141777f5 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 f5d0568e4ef..ed52b473c4b 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 110141777f5..ef68fcf4d22 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 cb4709d5fcc..c8a1585c356 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 ed52b473c4b..6883b385284 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 ef68fcf4d22..7d786f5fa9f 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 7d786f5fa9f..d709571fad0 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) ) }