From b1797349727e7791a3dcaf0499c27b3336f93b38 Mon Sep 17 00:00:00 2001 From: Josh Brown Date: Wed, 12 Nov 2025 21:20:03 -0800 Subject: [PATCH 1/2] Generic FlowStack module covering both DataFlowStack and TaintTrackingStack --- .../codeql/dataflowstack/FlowStack.qll | 443 ++++++++++++++++++ 1 file changed, 443 insertions(+) create mode 100644 shared/dataflowstack/codeql/dataflowstack/FlowStack.qll diff --git a/shared/dataflowstack/codeql/dataflowstack/FlowStack.qll b/shared/dataflowstack/codeql/dataflowstack/FlowStack.qll new file mode 100644 index 000000000000..a8963bf76ab9 --- /dev/null +++ b/shared/dataflowstack/codeql/dataflowstack/FlowStack.qll @@ -0,0 +1,443 @@ +overlay[local?] +module; + +private import codeql.dataflow.DataFlow as DF +private import codeql.dataflow.TaintTracking as TT +private import codeql.util.Location + +/** + * A Language-initialized grouping of DataFlow/TaintFlow types and primitives. + */ +module LanguageDataFlow Lang>{ + module AbstractDF = DF::Configs; + module AbstractDataFlow = DF::DataFlowMake; + module AbstractDataFlowOverlay = DF::DataFlowMakeOverlay; + + /** + * Signatures and modules bound by a common DataFlow Config + */ + module DataFlowConfigContext{ + + signature module FlowInstance{ + class PathNode; + + Lang::Node getNode(PathNode n); + + predicate isSource(PathNode n); + + PathNode getASuccessor(PathNode n); + + Lang::DataFlowCallable getARuntimeTarget(Lang::DataFlowCall call); + + Lang::Node getAnArgumentNode(Lang::DataFlowCall call); + } + + module TaintFlowContext TTLang>{ + module AbstractTaintFlow = TT::TaintFlowMake; + module AbstractTaintFlowOverlay = TT::TaintFlowMakeOverlay; + module TaintFlowGlobal = AbstractTaintFlow::Global; + module TaintFlowOverlayGlobal = AbstractTaintFlowOverlay::Global; + } + } + + module BiStackAnalysis< + AbstractDF::ConfigSig ConfigA, + DataFlowConfigContext::FlowInstance FlowA, + AbstractDF::ConfigSig ConfigB, + DataFlowConfigContext::FlowInstance FlowB> + { + module FlowStackA = FlowStack; + + module FlowStackB = FlowStack; + + /** + * Holds if either the Stack associated with `sourceNodeA` is a subset of the stack associated with `sourceNodeB` + * or vice-versa. + */ + predicate eitherStackSubset( + FlowA::PathNode sourceNodeA, FlowA::PathNode sinkNodeA, FlowB::PathNode sourceNodeB, + FlowB::PathNode sinkNodeB + ) { + FlowStackA::isSource(sourceNodeA) and + FlowStackB::isSource(sourceNodeB) and + FlowStackA::isSink(sinkNodeA) and + FlowStackB::isSink(sinkNodeB) and + exists(FlowStackA::FlowStack flowStackA, FlowStackB::FlowStack flowStackB | + flowStackA = FlowStackA::createFlowStack(sourceNodeA, sinkNodeA) and + flowStackB = FlowStackB::createFlowStack(sourceNodeB, sinkNodeB) and + ( + BiStackAnalysisImpl::flowStackIsSubsetOf(flowStackA, + flowStackB) + or + BiStackAnalysisImpl::flowStackIsSubsetOf(flowStackB, + flowStackA) + ) + ) + } + + /** + * Holds if the stack associated with path `sourceNodeA` is a subset (and shares a common stack bottom) with + * the stack associated with path `sourceNodeB`, or vice-versa. + * + * For the given pair of (source, sink) for two (potentially disparate) DataFlows, + * determine whether one Flow's Stack (at time of sink execution) is a subset of the other flow's Stack. + */ + predicate eitherStackTerminatingSubset( + FlowA::PathNode sourceNodeA, FlowA::PathNode sinkNodeA, FlowB::PathNode sourceNodeB, + FlowB::PathNode sinkNodeB + ) { + FlowStackA::isSource(sourceNodeA) and + FlowStackB::isSource(sourceNodeB) and + FlowStackA::isSink(sinkNodeA) and + FlowStackB::isSink(sinkNodeB) and + exists(FlowStackA::FlowStack flowStackA, FlowStackB::FlowStack flowStackB | + flowStackA = FlowStackA::createFlowStack(sourceNodeA, sinkNodeA) and + flowStackB = FlowStackB::createFlowStack(sourceNodeB, sinkNodeB) and + ( + BiStackAnalysisImpl::flowStackIsConvergingTerminatingSubsetOf(flowStackA, + flowStackB) + or + BiStackAnalysisImpl::flowStackIsConvergingTerminatingSubsetOf(flowStackB, + flowStackA) + ) + ) + } + + /** + * Alias for BiStackAnalysisImpl::flowStackIsSubsetOf + * + * Holds if stackA is a subset of stackB, + * The top of stackA is in stackB and the bottom of stackA is then some successor further down stackB. + */ + predicate flowStackIsSubsetOf(FlowStackA::FlowStack flowStackA, FlowStackB::FlowStack flowStackB) { + BiStackAnalysisImpl::flowStackIsSubsetOf(flowStackA, + flowStackB) + } + + /** + * Alias for BiStackAnalysisImpl::flowStackIsConvergingTerminatingSubsetOf + * + * If the top of stackA is in stackB at any location, and the bottoms of the stack are the same call. + */ + predicate flowStackIsConvergingTerminatingSubsetOf( + FlowStackA::FlowStack flowStackA, FlowStackB::FlowStack flowStackB + ) { + BiStackAnalysisImpl::flowStackIsConvergingTerminatingSubsetOf(flowStackA, + flowStackB) + } + } + + private module BiStackAnalysisImpl< + AbstractDF::ConfigSig ConfigA, + DataFlowConfigContext::FlowInstance FlowA, + AbstractDF::ConfigSig ConfigB, + DataFlowConfigContext::FlowInstance FlowB> + { + + module FlowStackA = FlowStack; + + module FlowStackB = FlowStack; + + /** + * Holds if stackA is a subset of stackB, + * The top of stackA is in stackB and the bottom of stackA is then some successor further down stackB. + */ + predicate flowStackIsSubsetOf(FlowStackA::FlowStack flowStackA, FlowStackB::FlowStack flowStackB) { + exists( + FlowStackA::FlowStackFrame highestStackFrameA, FlowStackB::FlowStackFrame highestStackFrameB + | + highestStackFrameA = flowStackA.getTopFrame() and + highestStackFrameB = flowStackB.getTopFrame() and + // Check if some intermediary frame `intStackFrameB`of StackB is in the stack of highestStackFrameA + exists(FlowStackB::FlowStackFrame intStackFrameB | + intStackFrameB = highestStackFrameB.getASucceedingTerminalStateFrame*() and + sharesCallWith(highestStackFrameA, intStackFrameB) and + sharesCallWith(flowStackA.getTerminalFrame(), + intStackFrameB.getASucceedingTerminalStateFrame*()) + ) + ) + } + + /** + * If the top of stackA is in stackB at any location, and the bottoms of the stack are the same call. + */ + predicate flowStackIsConvergingTerminatingSubsetOf( + FlowStackA::FlowStack flowStackA, FlowStackB::FlowStack flowStackB + ) { + flowStackA.getTerminalFrame().getCall() = flowStackB.getTerminalFrame().getCall() and + exists(FlowStackB::FlowStackFrame intStackFrameB | + intStackFrameB = flowStackB.getTopFrame().getASucceedingTerminalStateFrame*() and + sharesCallWith(flowStackA.getTopFrame(), intStackFrameB) + ) + } + + /** + * Holds if the given FlowStackFrames share the same call. + * i.e. they are both arguments of the same function call. + */ + predicate sharesCallWith(FlowStackA::FlowStackFrame frameA, FlowStackB::FlowStackFrame frameB) { + frameA.getCall() = frameB.getCall() + } + } + + module FlowStack< + AbstractDF::ConfigSig Config, + DataFlowConfigContext::FlowInstance Flow>{ + + /** + * Determines whether or not the given PathNode is a source + */ + predicate isSource = Flow::isSource/1; + + /** + * Determines whether or not the given PathNode is a sink + */ + predicate isSink(Flow::PathNode node) { not exists(Flow::getASuccessor(node)) } + + /** A FlowStack encapsulates flows between a source and a sink, and all the pathways inbetween (possibly multiple) */ + private newtype FlowStackType = + TFlowStack(Flow::PathNode source, Flow::PathNode sink) { + Flow::isSource(source) and + not exists(Flow::getASuccessor(sink)) and + Flow::getASuccessor*(source) = sink + } + + class FlowStack extends FlowStackType, TFlowStack { + string toString() { result = "FlowStack" } + + /** + * Get the first frame in the DataFlowStack, irregardless of whether or not it has a parent. + */ + FlowStackFrame getFirstFrame() { + exists(FlowStackFrame flowStackFrame, CallFrame frame | + flowStackFrame = TFlowStackFrame(this, frame) and + not exists(frame.getPredecessor()) and + result = flowStackFrame + ) + } + + /** + * Get the top frame in the DataFlowStack, ie the frame that is the highest in the stack for the given flow. + */ + FlowStackFrame getTopFrame() { + exists(FlowStackFrame flowStackFrame | + flowStackFrame = TFlowStackFrame(this, _) and + not exists(flowStackFrame.getParentStackFrame()) and + result = flowStackFrame + ) + } + + /** + * Get the terminal frame in the DataFlowStack, ie the frame that is the end of the flow. + */ + FlowStackFrame getTerminalFrame() { + exists(FlowStackFrame flowStackFrame, CallFrame frame | + flowStackFrame = TFlowStackFrame(this, frame) and + not exists(frame.getSuccessor()) and + result = flowStackFrame + ) + } + } + + FlowStack createFlowStack(Flow::PathNode source, Flow::PathNode sink) { + result = TFlowStack(source, sink) + } + + /** A FlowStackFrame encapsulates a Stack frame that is bound between a given source and sink. */ + private newtype FlowStackFrameType = + TFlowStackFrame(FlowStack flowStack, CallFrame frame) { + exists(Flow::PathNode source, Flow::PathNode sink | + flowStack = TFlowStack(source, sink) and + frame.getPathNode() = Flow::getASuccessor*(source) and + Flow::getASuccessor*(frame.getPathNode()) = sink + ) + } + + class FlowStackFrame extends FlowStackFrameType, TFlowStackFrame { + string toString() { result = "FlowStackFrame" } + + /** + * Get the next frame in the DataFlow Stack + */ + FlowStackFrame getASuccessor() { + exists(FlowStack flowStack, CallFrame frame, CallFrame nextFrame | + this = TFlowStackFrame(flowStack, frame) and + nextFrame = frame.getSuccessor() and + result = TFlowStackFrame(flowStack, nextFrame) + ) + } + + /** + * Gets the next FlowStackFrame from the direct descendents that is a frame in the end-state (terminal) stack. + */ + FlowStackFrame getASucceedingTerminalStateFrame() { + result = this.getChildStackFrame() and + // There are no other direct children that are further in the flow + not result.getASuccessor+() = this.getChildStackFrame() + } + + /** + * Gets a predecessor FlowStackFrame of this FlowStackFrame. + */ + FlowStackFrame getAPredecessor() { result.getASuccessor() = this } + + /** + * Gets a predecessor FlowStackFrame that is a parent in the stack. + */ + FlowStackFrame getParentStackFrame() { result.getChildStackFrame() = this } + + /** + * Gets the set of succeeding FlowStackFrame which are a direct descendant of this frame in the Stack. + */ + FlowStackFrame getChildStackFrame() { + exists(FlowStackFrame transitiveSuccessor | + transitiveSuccessor = this.getASuccessor+() and + Flow::getARuntimeTarget(this.getCall()) = + transitiveSuccessor.getCall().getEnclosingCallable() and + result = transitiveSuccessor + ) + } + + /** + * Unpacks the PathNode associated with this FlowStackFrame + */ + Flow::PathNode getPathNode() { + exists(CallFrame callFrame | + this = TFlowStackFrame(_, callFrame) and + result = callFrame.getPathNode() + ) + } + + /** + * Unpacks the DataFlowCall associated with this FlowStackFrame + */ + Lang::DataFlowCall getCall() { result = this.getCallFrame().getCall() } + + /** + * Unpacks the CallFrame associated with this FlowStackFrame + */ + CallFrame getCallFrame() { this = TFlowStackFrame(_, result) } + } + + /** + * A CallFrame is a PathNode that represents a (DataFlowCall/Accessor). + */ + private newtype TCallFrameType = + TCallFrame(Flow::PathNode node) { + exists(Lang::DataFlowCall c | + Flow::getAnArgumentNode(c) = Flow::getNode(node) + ) + } + + /** + * The CallFrame is a PathNode that represents an argument to a Call. + */ + private class CallFrame extends TCallFrameType, TCallFrame { + string toString() { + exists(Lang::DataFlowCall call | + call = this.getCall() and + result = call.toString() + ) + } + + /** + * Find the set of CallFrames that are immediate successors of this CallFrame. + */ + CallFrame getSuccessor() { result = TCallFrame(getSuccessorCall(this.getPathNode())) } + + /** + * Find the set of CallFrames that are an immediate predecessor of this CallFrame. + */ + CallFrame getPredecessor() { + exists(CallFrame prior | + prior.getSuccessor() = this and + result = prior + ) + } + + /** + * Unpack the CallFrame and retrieve the associated DataFlowCall. + */ + Lang::DataFlowCall getCall() { + exists(Lang::DataFlowCall call, Flow::PathNode node | + this = TCallFrame(node) and + Flow::getAnArgumentNode(call) = Flow::getNode(node) and + result = call + ) + } + + /** + * Unpack the CallFrame and retrieve the associated PathNode. + */ + Flow::PathNode getPathNode() { + exists(Flow::PathNode n | + this = TCallFrame(n) and + result = n + ) + } + } + + /** + * From the given PathNode argument, find the set of successors that are an argument in a DataFlowCall, + * and return them as the result. + */ + private Flow::PathNode getSuccessorCall(Flow::PathNode n) { + exists(Flow::PathNode succ | + succ = Flow::getASuccessor(n) and + if + exists(Lang::DataFlowCall c | + Flow::getAnArgumentNode(c) = Flow::getNode(succ) + ) + then result = succ + else result = getSuccessorCall(succ) + ) + } + + /** + * A user-supplied predicate which given a Stack Frame, returns some Node associated with it. + */ + signature Lang::Node extractNodeFromFrame(Flow::PathNode pathNode); + + /** + * Provides some higher-order predicates for analyzing Stacks + */ + module StackFrameAnalysis { + /** + * Find the highest stack frame that satisfies the given predicate, + * and return the Node(s) that the user-supplied predicate returns. + * + * There should be no higher stack frame that satisfies the user-supplied predicate FROM the point that the + * argument . + */ + Lang::Node extractingFromHighestStackFrame(FlowStack flowStack) { + exists(FlowStackFrame topStackFrame, FlowStackFrame someStackFrame | + topStackFrame = flowStack.getTopFrame() and + someStackFrame = topStackFrame.getASuccessor*() and + result = customFrameCond(someStackFrame.getPathNode()) and + not exists(FlowStackFrame predecessor | + predecessor = someStackFrame.getAPredecessor+() and + // The predecessor is *not* prior to the user-given 'top' of the stack frame. + not predecessor = topStackFrame.getAPredecessor+() and + exists(customFrameCond(predecessor.getPathNode())) + ) + ) + } + + /** + * Find the lowest stack frame that satisfies the given predicate, + * and return the Node(s) that the user-supplied predicate returns. + */ + Lang::Node extractingFromLowestStackFrame(FlowStack flowStack) { + exists(FlowStackFrame topStackFrame, FlowStackFrame someStackFrame | + topStackFrame = flowStack.getTopFrame() and + someStackFrame = topStackFrame.getChildStackFrame*() and + result = customFrameCond(someStackFrame.getPathNode()) and + not exists(FlowStackFrame successor | + successor = someStackFrame.getChildStackFrame+() and + exists(customFrameCond(successor.getPathNode())) + ) + ) + } + } + } +} \ No newline at end of file From 4bf4363942385191e1f75b2e1564bc58eebcca08 Mon Sep 17 00:00:00 2001 From: Josh Brown Date: Thu, 13 Nov 2025 15:39:50 -0800 Subject: [PATCH 2/2] Refactor {Data,TaintTracking}FlowStack --- .../code/csharp/dataflow/DataFlowStack.qll | 11 +- .../csharp/dataflow/TaintTrackingStack.qll | 13 +- .../code/java/dataflow/DataFlowStack.qll | 17 +- .../code/java/dataflow/TaintTrackingStack.qll | 19 +- .../codeql/dataflowstack/DataFlowStack.qll | 456 ----------------- .../dataflowstack/TaintTrackingStack.qll | 458 ------------------ 6 files changed, 32 insertions(+), 942 deletions(-) delete mode 100644 shared/dataflowstack/codeql/dataflowstack/DataFlowStack.qll delete mode 100644 shared/dataflowstack/codeql/dataflowstack/TaintTrackingStack.qll diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/DataFlowStack.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/DataFlowStack.qll index d6befff5b6b8..55b3b0342e37 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/DataFlowStack.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/DataFlowStack.qll @@ -1,14 +1,15 @@ import csharp private import codeql.dataflow.DataFlow private import semmle.code.csharp.dataflow.internal.DataFlowImplSpecific -private import codeql.dataflowstack.DataFlowStack as DFS +private import codeql.dataflowstack.FlowStack as FlowStack -module LanguageDataFlowStack = DFS::LanguageDataFlow; +module LanguageFlowStack = FlowStack::LanguageDataFlow; private module FlowStackInput - implements LanguageDataFlowStack::DataFlowGroup::DataFlowStackSig> + implements LanguageFlowStack::DataFlowConfigContext::FlowInstance { private module Flow = DataFlow::Global; + class PathNode = Flow::PathNode; CsharpDataFlow::Node getNode(Flow::PathNode n) { result = n.getNode() } @@ -26,12 +27,12 @@ private module FlowStackInput } module DataFlowStackMake { - import LanguageDataFlowStack::FlowStack, Config, FlowStackInput> + import LanguageFlowStack::FlowStack> } module BiStackAnalysisMake< DataFlow::ConfigSig ConfigA, DataFlow::ConfigSig ConfigB >{ - import LanguageDataFlowStack::BiStackAnalysis, FlowStackInput, ConfigB, DataFlow::Global, FlowStackInput> + import LanguageFlowStack::BiStackAnalysis, ConfigB, FlowStackInput> } \ No newline at end of file diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/TaintTrackingStack.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/TaintTrackingStack.qll index 4c5868e2695d..22bb3705f4ad 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/TaintTrackingStack.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/TaintTrackingStack.qll @@ -2,14 +2,15 @@ import csharp private import codeql.dataflow.DataFlow private import semmle.code.csharp.dataflow.internal.DataFlowImplSpecific private import semmle.code.csharp.dataflow.internal.TaintTrackingImplSpecific -private import codeql.dataflowstack.TaintTrackingStack as TTS +private import codeql.dataflowstack.FlowStack as FlowStack -module LanguageTaintTrackingStack = TTS::LanguageTaintTracking; +module LanguageFlowStack = FlowStack::LanguageDataFlow; -private module TaintTrackingStackInput - implements LanguageTaintTrackingStack::DataFlowGroup::TaintTrackingStackSig> +private module FlowStackInput + implements LanguageFlowStack::DataFlowConfigContext::FlowInstance { private module Flow = TaintTracking::Global; + class PathNode = Flow::PathNode; CsharpDataFlow::Node getNode(Flow::PathNode n) { result = n.getNode() } @@ -27,12 +28,12 @@ private module TaintTrackingStackInput } module DataFlowStackMake { - import LanguageTaintTrackingStack::FlowStack, Config, TaintTrackingStackInput> + import LanguageFlowStack::FlowStack> } module BiStackAnalysisMake< DataFlow::ConfigSig ConfigA, DataFlow::ConfigSig ConfigB >{ - import LanguageTaintTrackingStack::BiStackAnalysis, TaintTrackingStackInput, ConfigB, TaintTracking::Global, TaintTrackingStackInput> + import LanguageFlowStack::BiStackAnalysis, ConfigB, FlowStackInput> } \ No newline at end of file diff --git a/java/ql/lib/semmle/code/java/dataflow/DataFlowStack.qll b/java/ql/lib/semmle/code/java/dataflow/DataFlowStack.qll index 1beb87ff72b6..e5f8de8cf8b3 100644 --- a/java/ql/lib/semmle/code/java/dataflow/DataFlowStack.qll +++ b/java/ql/lib/semmle/code/java/dataflow/DataFlowStack.qll @@ -4,20 +4,21 @@ module; import java private import semmle.code.java.dataflow.DataFlow private import semmle.code.java.dataflow.internal.DataFlowImplSpecific -private import codeql.dataflowstack.DataFlowStack as DFS +private import codeql.dataflowstack.FlowStack as FlowStack -module LanguageDataFlowStack = DFS::LanguageDataFlow; +module LanguageFlowStack = FlowStack::LanguageDataFlow; private module FlowStackInput - implements LanguageDataFlowStack::DataFlowGroup::DataFlowStackSig> + implements LanguageFlowStack::DataFlowConfigContext::FlowInstance { private module Flow = DataFlow::Global; + class PathNode = Flow::PathNode; - JavaDataFlow::Node getNode(Flow::PathNode n) { result = n.getNode() } + JavaDataFlow::Node getNode(PathNode n) { result = n.getNode() } - predicate isSource(Flow::PathNode n) { n.isSource() } + predicate isSource(PathNode n) { n.isSource() } - Flow::PathNode getASuccessor(Flow::PathNode n) { result = n.getASuccessor() } + PathNode getASuccessor(PathNode n) { result = n.getASuccessor() } JavaDataFlow::DataFlowCallable getARuntimeTarget(JavaDataFlow::DataFlowCall call) { result.asCallable() = call.asCall().getCallee() @@ -29,12 +30,12 @@ private module FlowStackInput } module DataFlowStackMake { - import LanguageDataFlowStack::FlowStack, Config, FlowStackInput> + import LanguageFlowStack::FlowStack> } module BiStackAnalysisMake< DataFlow::ConfigSig ConfigA, DataFlow::ConfigSig ConfigB >{ - import LanguageDataFlowStack::BiStackAnalysis, FlowStackInput, ConfigB, DataFlow::Global, FlowStackInput> + import LanguageFlowStack::BiStackAnalysis, ConfigB, FlowStackInput> } \ No newline at end of file diff --git a/java/ql/lib/semmle/code/java/dataflow/TaintTrackingStack.qll b/java/ql/lib/semmle/code/java/dataflow/TaintTrackingStack.qll index b83d4fce814f..a26bb888f18c 100644 --- a/java/ql/lib/semmle/code/java/dataflow/TaintTrackingStack.qll +++ b/java/ql/lib/semmle/code/java/dataflow/TaintTrackingStack.qll @@ -6,20 +6,21 @@ private import semmle.code.java.dataflow.DataFlow private import semmle.code.java.dataflow.TaintTracking private import semmle.code.java.dataflow.internal.DataFlowImplSpecific private import semmle.code.java.dataflow.internal.TaintTrackingImplSpecific -private import codeql.dataflowstack.TaintTrackingStack as TTS +private import codeql.dataflowstack.FlowStack as FlowStack -module LanguageTaintTrackingStack = TTS::LanguageTaintTracking; +module LanguageFlowStack = FlowStack::LanguageDataFlow; -private module TaintTrackingStackInput - implements LanguageTaintTrackingStack::DataFlowGroup::TaintTrackingStackSig> +private module FlowStackInput + implements LanguageFlowStack::DataFlowConfigContext::FlowInstance { private module Flow = TaintTracking::Global; + class PathNode = Flow::PathNode; - JavaDataFlow::Node getNode(Flow::PathNode n) { result = n.getNode() } + JavaDataFlow::Node getNode(PathNode n) { result = n.getNode() } - predicate isSource(Flow::PathNode n) { n.isSource() } + predicate isSource(PathNode n) { n.isSource() } - Flow::PathNode getASuccessor(Flow::PathNode n) { result = n.getASuccessor() } + PathNode getASuccessor(PathNode n) { result = n.getASuccessor() } JavaDataFlow::DataFlowCallable getARuntimeTarget(JavaDataFlow::DataFlowCall call) { result.asCallable() = call.asCall().getCallee() @@ -31,12 +32,12 @@ private module TaintTrackingStackInput } module DataFlowStackMake { - import LanguageTaintTrackingStack::FlowStack, Config, TaintTrackingStackInput> + import LanguageFlowStack::FlowStack> } module BiStackAnalysisMake< DataFlow::ConfigSig ConfigA, DataFlow::ConfigSig ConfigB >{ - import LanguageTaintTrackingStack::BiStackAnalysis, TaintTrackingStackInput, ConfigB, TaintTracking::Global, TaintTrackingStackInput> + import LanguageFlowStack::BiStackAnalysis, ConfigB, FlowStackInput> } \ No newline at end of file diff --git a/shared/dataflowstack/codeql/dataflowstack/DataFlowStack.qll b/shared/dataflowstack/codeql/dataflowstack/DataFlowStack.qll deleted file mode 100644 index 2598b70e8961..000000000000 --- a/shared/dataflowstack/codeql/dataflowstack/DataFlowStack.qll +++ /dev/null @@ -1,456 +0,0 @@ -overlay[local?] -module; - -private import codeql.dataflow.DataFlow as DF -private import codeql.util.Location - -/** - * A Language-initialized grouping of DataFlow types and primitives. - */ -module LanguageDataFlow Lang>{ - module AbstractDF = DF::Configs; - module AbstractDataFlow = DF::DataFlowMake; - module AbstractDataFlowOverlay = DF::DataFlowMakeOverlay; - - /** - * A collection of modules that are scoped to a specific DataFlow config implementation - */ - module DataFlowGroup{ - - module MyConfig = Config; - module DataFlowGlobal = AbstractDataFlow::Global; - module DataFlowOverlayGlobal = AbstractDataFlowOverlay::Global; - - /** - * A Taint tracking implementation, paramaterized over a DataFlow type - */ - signature module DataFlowStackSig{ - - Lang::Node getNode(GlobalFlow::PathNode n); - - predicate isSource(GlobalFlow::PathNode n); - - GlobalFlow::PathNode getASuccessor( - GlobalFlow::PathNode n - ); - - Lang::DataFlowCallable getARuntimeTarget(Lang::DataFlowCall call); - - Lang::Node getAnArgumentNode(Lang::DataFlowCall call); - } - } - - module BiStackAnalysis< - AbstractDF::ConfigSig ConfigA, - AbstractDataFlow::GlobalFlowSig GlobalFlowA, - DataFlowGroup::DataFlowStackSig TaintTrackingStackA, - AbstractDF::ConfigSig ConfigB, - AbstractDataFlow::GlobalFlowSig GlobalFlowB, - DataFlowGroup::DataFlowStackSig TaintTrackingStackB> - { - module FlowA = GlobalFlowA; - - module FlowStackA = FlowStack; - - module FlowB = GlobalFlowB; - - module FlowStackB = FlowStack; - - /** - * Holds if either the Stack associated with `sourceNodeA` is a subset of the stack associated with `sourceNodeB` - * or vice-versa. - */ - predicate eitherStackSubset( - FlowA::PathNode sourceNodeA, FlowA::PathNode sinkNodeA, FlowB::PathNode sourceNodeB, - FlowB::PathNode sinkNodeB - ) { - FlowStackA::isSource(sourceNodeA) and - FlowStackB::isSource(sourceNodeB) and - FlowStackA::isSink(sinkNodeA) and - FlowStackB::isSink(sinkNodeB) and - exists(FlowStackA::FlowStack flowStackA, FlowStackB::FlowStack flowStackB | - flowStackA = FlowStackA::createFlowStack(sourceNodeA, sinkNodeA) and - flowStackB = FlowStackB::createFlowStack(sourceNodeB, sinkNodeB) and - ( - BiStackAnalysisImpl::flowStackIsSubsetOf(flowStackA, - flowStackB) - or - BiStackAnalysisImpl::flowStackIsSubsetOf(flowStackB, - flowStackA) - ) - ) - } - - /** - * Holds if the stack associated with path `sourceNodeA` is a subset (and shares a common stack bottom) with - * the stack associated with path `sourceNodeB`, or vice-versa. - * - * For the given pair of (source, sink) for two (potentially disparate) DataFlows, - * determine whether one Flow's Stack (at time of sink execution) is a subset of the other flow's Stack. - */ - predicate eitherStackTerminatingSubset( - FlowA::PathNode sourceNodeA, FlowA::PathNode sinkNodeA, FlowB::PathNode sourceNodeB, - FlowB::PathNode sinkNodeB - ) { - FlowStackA::isSource(sourceNodeA) and - FlowStackB::isSource(sourceNodeB) and - FlowStackA::isSink(sinkNodeA) and - FlowStackB::isSink(sinkNodeB) and - exists(FlowStackA::FlowStack flowStackA, FlowStackB::FlowStack flowStackB | - flowStackA = FlowStackA::createFlowStack(sourceNodeA, sinkNodeA) and - flowStackB = FlowStackB::createFlowStack(sourceNodeB, sinkNodeB) and - ( - BiStackAnalysisImpl::flowStackIsConvergingTerminatingSubsetOf(flowStackA, - flowStackB) - or - BiStackAnalysisImpl::flowStackIsConvergingTerminatingSubsetOf(flowStackB, - flowStackA) - ) - ) - } - - /** - * Alias for BiStackAnalysisImpl::flowStackIsSubsetOf - * - * Holds if stackA is a subset of stackB, - * The top of stackA is in stackB and the bottom of stackA is then some successor further down stackB. - */ - predicate flowStackIsSubsetOf(FlowStackA::FlowStack flowStackA, FlowStackB::FlowStack flowStackB) { - BiStackAnalysisImpl::flowStackIsSubsetOf(flowStackA, - flowStackB) - } - - /** - * Alias for BiStackAnalysisImpl::flowStackIsConvergingTerminatingSubsetOf - * - * If the top of stackA is in stackB at any location, and the bottoms of the stack are the same call. - */ - predicate flowStackIsConvergingTerminatingSubsetOf( - FlowStackA::FlowStack flowStackA, FlowStackB::FlowStack flowStackB - ) { - BiStackAnalysisImpl::flowStackIsConvergingTerminatingSubsetOf(flowStackA, - flowStackB) - } - } - - private module BiStackAnalysisImpl< - AbstractDataFlow::GlobalFlowSig GlobalFlowA, - AbstractDF::ConfigSig ConfigA, - DataFlowGroup::DataFlowStackSig DataFlowStackA, - AbstractDataFlow::GlobalFlowSig GlobalFlowB, - AbstractDF::ConfigSig ConfigB, - DataFlowGroup::DataFlowStackSig DataFlowStackB> - { - - module FlowStackA = FlowStack; - - module FlowStackB = FlowStack; - - /** - * Holds if stackA is a subset of stackB, - * The top of stackA is in stackB and the bottom of stackA is then some successor further down stackB. - */ - predicate flowStackIsSubsetOf(FlowStackA::FlowStack flowStackA, FlowStackB::FlowStack flowStackB) { - exists( - FlowStackA::FlowStackFrame highestStackFrameA, FlowStackB::FlowStackFrame highestStackFrameB - | - highestStackFrameA = flowStackA.getTopFrame() and - highestStackFrameB = flowStackB.getTopFrame() and - // Check if some intermediary frame `intStackFrameB`of StackB is in the stack of highestStackFrameA - exists(FlowStackB::FlowStackFrame intStackFrameB | - intStackFrameB = highestStackFrameB.getASucceedingTerminalStateFrame*() and - sharesCallWith(highestStackFrameA, intStackFrameB) and - sharesCallWith(flowStackA.getTerminalFrame(), - intStackFrameB.getASucceedingTerminalStateFrame*()) - ) - ) - } - - /** - * If the top of stackA is in stackB at any location, and the bottoms of the stack are the same call. - */ - predicate flowStackIsConvergingTerminatingSubsetOf( - FlowStackA::FlowStack flowStackA, FlowStackB::FlowStack flowStackB - ) { - flowStackA.getTerminalFrame().getCall() = flowStackB.getTerminalFrame().getCall() and - exists(FlowStackB::FlowStackFrame intStackFrameB | - intStackFrameB = flowStackB.getTopFrame().getASucceedingTerminalStateFrame*() and - sharesCallWith(flowStackA.getTopFrame(), intStackFrameB) - ) - } - - /** - * Holds if the given FlowStackFrames share the same call. - * i.e. they are both arguments of the same function call. - */ - predicate sharesCallWith(FlowStackA::FlowStackFrame frameA, FlowStackB::FlowStackFrame frameB) { - frameA.getCall() = frameB.getCall() - } - } - - module FlowStack< - AbstractDataFlow::GlobalFlowSig GlobalFlow, - AbstractDF::ConfigSig Config, - DataFlowGroup::DataFlowStackSig TaintTrackingStack> - { - private module Flow = GlobalFlow; - - /** - * Determines whether or not the given PathNode is a source - * TODO: Refactor to Flow::PathNode signature - */ - predicate isSource(Flow::PathNode node) { TaintTrackingStack::isSource(node) } - - /** - * Determines whether or not the given PathNode is a sink - * TODO: Refactor to Flow::PathNode signature - */ - predicate isSink(Flow::PathNode node) { not exists(TaintTrackingStack::getASuccessor(node)) } - - /** A FlowStack encapsulates flows between a source and a sink, and all the pathways inbetween (possibly multiple) */ - private newtype FlowStackType = - TFlowStack(Flow::PathNode source, Flow::PathNode sink) { - TaintTrackingStack::isSource(source) and - not exists(TaintTrackingStack::getASuccessor(sink)) and - TaintTrackingStack::getASuccessor*(source) = sink - } - - class FlowStack extends FlowStackType, TFlowStack { - string toString() { result = "FlowStack" } - - /** - * Get the first frame in the DataFlowStack, irregardless of whether or not it has a parent. - */ - FlowStackFrame getFirstFrame() { - exists(FlowStackFrame flowStackFrame, CallFrame frame | - flowStackFrame = TFlowStackFrame(this, frame) and - not exists(frame.getPredecessor()) and - result = flowStackFrame - ) - } - - /** - * Get the top frame in the DataFlowStack, ie the frame that is the highest in the stack for the given flow. - */ - FlowStackFrame getTopFrame() { - exists(FlowStackFrame flowStackFrame | - flowStackFrame = TFlowStackFrame(this, _) and - not exists(flowStackFrame.getParentStackFrame()) and - result = flowStackFrame - ) - } - - /** - * Get the terminal frame in the DataFlowStack, ie the frame that is the end of the flow. - */ - FlowStackFrame getTerminalFrame() { - exists(FlowStackFrame flowStackFrame, CallFrame frame | - flowStackFrame = TFlowStackFrame(this, frame) and - not exists(frame.getSuccessor()) and - result = flowStackFrame - ) - } - } - - FlowStack createFlowStack(Flow::PathNode source, Flow::PathNode sink) { - result = TFlowStack(source, sink) - } - - /** A FlowStackFrame encapsulates a Stack frame that is bound between a given source and sink. */ - private newtype FlowStackFrameType = - TFlowStackFrame(FlowStack flowStack, CallFrame frame) { - exists(Flow::PathNode source, Flow::PathNode sink | - flowStack = TFlowStack(source, sink) and - frame.getPathNode() = TaintTrackingStack::getASuccessor*(source) and - TaintTrackingStack::getASuccessor*(frame.getPathNode()) = sink - ) - } - - class FlowStackFrame extends FlowStackFrameType, TFlowStackFrame { - string toString() { result = "FlowStackFrame" } - - /** - * Get the next frame in the DataFlow Stack - */ - FlowStackFrame getASuccessor() { - exists(FlowStack flowStack, CallFrame frame, CallFrame nextFrame | - this = TFlowStackFrame(flowStack, frame) and - nextFrame = frame.getSuccessor() and - result = TFlowStackFrame(flowStack, nextFrame) - ) - } - - /** - * Gets the next FlowStackFrame from the direct descendents that is a frame in the end-state (terminal) stack. - */ - FlowStackFrame getASucceedingTerminalStateFrame() { - result = this.getChildStackFrame() and - // There are no other direct children that are further in the flow - not result.getASuccessor+() = this.getChildStackFrame() - } - - /** - * Gets a predecessor FlowStackFrame of this FlowStackFrame. - */ - FlowStackFrame getAPredecessor() { result.getASuccessor() = this } - - /** - * Gets a predecessor FlowStackFrame that is a parent in the stack. - */ - FlowStackFrame getParentStackFrame() { result.getChildStackFrame() = this } - - /** - * Gets the set of succeeding FlowStackFrame which are a direct descendant of this frame in the Stack. - */ - FlowStackFrame getChildStackFrame() { - exists(FlowStackFrame transitiveSuccessor | - transitiveSuccessor = this.getASuccessor+() and - TaintTrackingStack::getARuntimeTarget(this.getCall()) = - transitiveSuccessor.getCall().getEnclosingCallable() and - result = transitiveSuccessor - ) - } - - /** - * Unpacks the PathNode associated with this FlowStackFrame - */ - Flow::PathNode getPathNode() { - exists(CallFrame callFrame | - this = TFlowStackFrame(_, callFrame) and - result = callFrame.getPathNode() - ) - } - - /** - * Unpacks the DataFlowCall associated with this FlowStackFrame - */ - Lang::DataFlowCall getCall() { result = this.getCallFrame().getCall() } - - /** - * Unpacks the CallFrame associated with this FlowStackFrame - */ - CallFrame getCallFrame() { this = TFlowStackFrame(_, result) } - } - - /** - * A CallFrame is a PathNode that represents a (DataFlowCall/Accessor). - */ - private newtype TCallFrameType = - TCallFrame(Flow::PathNode node) { - exists(Lang::DataFlowCall c | - TaintTrackingStack::getAnArgumentNode(c) = TaintTrackingStack::getNode(node) - ) - } - - /** - * The CallFrame is a PathNode that represents an argument to a Call. - */ - private class CallFrame extends TCallFrameType, TCallFrame { - string toString() { - exists(Lang::DataFlowCall call | - call = this.getCall() and - result = call.toString() - ) - } - - /** - * Find the set of CallFrames that are immediate successors of this CallFrame. - */ - CallFrame getSuccessor() { result = TCallFrame(getSuccessorCall(this.getPathNode())) } - - /** - * Find the set of CallFrames that are an immediate predecessor of this CallFrame. - */ - CallFrame getPredecessor() { - exists(CallFrame prior | - prior.getSuccessor() = this and - result = prior - ) - } - - /** - * Unpack the CallFrame and retrieve the associated DataFlowCall. - */ - Lang::DataFlowCall getCall() { - exists(Lang::DataFlowCall call, Flow::PathNode node | - this = TCallFrame(node) and - TaintTrackingStack::getAnArgumentNode(call) = TaintTrackingStack::getNode(node) and - result = call - ) - } - - /** - * Unpack the CallFrame and retrieve the associated PathNode. - */ - Flow::PathNode getPathNode() { - exists(Flow::PathNode n | - this = TCallFrame(n) and - result = n - ) - } - } - - /** - * From the given PathNode argument, find the set of successors that are an argument in a DataFlowCall, - * and return them as the result. - */ - private Flow::PathNode getSuccessorCall(Flow::PathNode n) { - exists(Flow::PathNode succ | - succ = TaintTrackingStack::getASuccessor(n) and - if - exists(Lang::DataFlowCall c | - TaintTrackingStack::getAnArgumentNode(c) = TaintTrackingStack::getNode(succ) - ) - then result = succ - else result = getSuccessorCall(succ) - ) - } - - /** - * A user-supplied predicate which given a Stack Frame, returns some Node associated with it. - */ - signature Lang::Node extractNodeFromFrame(Flow::PathNode pathNode); - - /** - * Provides some higher-order predicates for analyzing Stacks - */ - module StackFrameAnalysis { - /** - * Find the highest stack frame that satisfies the given predicate, - * and return the Node(s) that the user-supplied predicate returns. - * - * There should be no higher stack frame that satisfies the user-supplied predicate FROM the point that the - * argument . - */ - Lang::Node extractingFromHighestStackFrame(FlowStack flowStack) { - exists(FlowStackFrame topStackFrame, FlowStackFrame someStackFrame | - topStackFrame = flowStack.getTopFrame() and - someStackFrame = topStackFrame.getASuccessor*() and - result = customFrameCond(someStackFrame.getPathNode()) and - not exists(FlowStackFrame predecessor | - predecessor = someStackFrame.getAPredecessor+() and - // The predecessor is *not* prior to the user-given 'top' of the stack frame. - not predecessor = topStackFrame.getAPredecessor+() and - exists(customFrameCond(predecessor.getPathNode())) - ) - ) - } - - /** - * Find the lowest stack frame that satisfies the given predicate, - * and return the Node(s) that the user-supplied predicate returns. - */ - Lang::Node extractingFromLowestStackFrame(FlowStack flowStack) { - exists(FlowStackFrame topStackFrame, FlowStackFrame someStackFrame | - topStackFrame = flowStack.getTopFrame() and - someStackFrame = topStackFrame.getChildStackFrame*() and - result = customFrameCond(someStackFrame.getPathNode()) and - not exists(FlowStackFrame successor | - successor = someStackFrame.getChildStackFrame+() and - exists(customFrameCond(successor.getPathNode())) - ) - ) - } - } - } -} \ No newline at end of file diff --git a/shared/dataflowstack/codeql/dataflowstack/TaintTrackingStack.qll b/shared/dataflowstack/codeql/dataflowstack/TaintTrackingStack.qll deleted file mode 100644 index 763e4438ea79..000000000000 --- a/shared/dataflowstack/codeql/dataflowstack/TaintTrackingStack.qll +++ /dev/null @@ -1,458 +0,0 @@ -overlay[local?] -module; - -private import codeql.dataflow.DataFlow as DF -private import codeql.dataflow.TaintTracking as TT -private import codeql.util.Location - -/** - * A Language-initialized grouping of DataFlow types and primitives. - */ -module LanguageTaintTracking Lang, TT::InputSig TTLang>{ - module AbstractDF = DF::Configs; - module AbstractDataFlow = DF::DataFlowMake; - module AbstractTaintFlow = TT::TaintFlowMake; - module AbstractTaintFlowOverlay = TT::TaintFlowMakeOverlay; - - /** - * A collection of modules that are scoped to a specific DataFlow config implementation - */ - module DataFlowGroup{ - - module MyConfig = Config; - module TaintFlowGlobal = AbstractTaintFlow::Global; - module TaintFlowOverlayGlobal = AbstractTaintFlowOverlay::Global; - - /** - * A Taint tracking implementation, paramaterized over a DataFlow type - */ - signature module TaintTrackingStackSig{ - - Lang::Node getNode(GlobalFlow::PathNode n); - - predicate isSource(GlobalFlow::PathNode n); - - GlobalFlow::PathNode getASuccessor( - GlobalFlow::PathNode n - ); - - Lang::DataFlowCallable getARuntimeTarget(Lang::DataFlowCall call); - - Lang::Node getAnArgumentNode(Lang::DataFlowCall call); - } - } - - module BiStackAnalysis< - AbstractDF::ConfigSig ConfigA, - AbstractDataFlow::GlobalFlowSig GlobalFlowA, - DataFlowGroup::TaintTrackingStackSig TaintTrackingStackA, - AbstractDF::ConfigSig ConfigB, - AbstractDataFlow::GlobalFlowSig GlobalFlowB, - DataFlowGroup::TaintTrackingStackSig TaintTrackingStackB> - { - module FlowA = GlobalFlowA; - - module FlowStackA = FlowStack; - - module FlowB = GlobalFlowB; - - module FlowStackB = FlowStack; - - /** - * Holds if either the Stack associated with `sourceNodeA` is a subset of the stack associated with `sourceNodeB` - * or vice-versa. - */ - predicate eitherStackSubset( - FlowA::PathNode sourceNodeA, FlowA::PathNode sinkNodeA, FlowB::PathNode sourceNodeB, - FlowB::PathNode sinkNodeB - ) { - FlowStackA::isSource(sourceNodeA) and - FlowStackB::isSource(sourceNodeB) and - FlowStackA::isSink(sinkNodeA) and - FlowStackB::isSink(sinkNodeB) and - exists(FlowStackA::FlowStack flowStackA, FlowStackB::FlowStack flowStackB | - flowStackA = FlowStackA::createFlowStack(sourceNodeA, sinkNodeA) and - flowStackB = FlowStackB::createFlowStack(sourceNodeB, sinkNodeB) and - ( - BiStackAnalysisImpl::flowStackIsSubsetOf(flowStackA, - flowStackB) - or - BiStackAnalysisImpl::flowStackIsSubsetOf(flowStackB, - flowStackA) - ) - ) - } - - /** - * Holds if the stack associated with path `sourceNodeA` is a subset (and shares a common stack bottom) with - * the stack associated with path `sourceNodeB`, or vice-versa. - * - * For the given pair of (source, sink) for two (potentially disparate) DataFlows, - * determine whether one Flow's Stack (at time of sink execution) is a subset of the other flow's Stack. - */ - predicate eitherStackTerminatingSubset( - FlowA::PathNode sourceNodeA, FlowA::PathNode sinkNodeA, FlowB::PathNode sourceNodeB, - FlowB::PathNode sinkNodeB - ) { - FlowStackA::isSource(sourceNodeA) and - FlowStackB::isSource(sourceNodeB) and - FlowStackA::isSink(sinkNodeA) and - FlowStackB::isSink(sinkNodeB) and - exists(FlowStackA::FlowStack flowStackA, FlowStackB::FlowStack flowStackB | - flowStackA = FlowStackA::createFlowStack(sourceNodeA, sinkNodeA) and - flowStackB = FlowStackB::createFlowStack(sourceNodeB, sinkNodeB) and - ( - BiStackAnalysisImpl::flowStackIsConvergingTerminatingSubsetOf(flowStackA, - flowStackB) - or - BiStackAnalysisImpl::flowStackIsConvergingTerminatingSubsetOf(flowStackB, - flowStackA) - ) - ) - } - - /** - * Alias for BiStackAnalysisImpl::flowStackIsSubsetOf - * - * Holds if stackA is a subset of stackB, - * The top of stackA is in stackB and the bottom of stackA is then some successor further down stackB. - */ - predicate flowStackIsSubsetOf(FlowStackA::FlowStack flowStackA, FlowStackB::FlowStack flowStackB) { - BiStackAnalysisImpl::flowStackIsSubsetOf(flowStackA, - flowStackB) - } - - /** - * Alias for BiStackAnalysisImpl::flowStackIsConvergingTerminatingSubsetOf - * - * If the top of stackA is in stackB at any location, and the bottoms of the stack are the same call. - */ - predicate flowStackIsConvergingTerminatingSubsetOf( - FlowStackA::FlowStack flowStackA, FlowStackB::FlowStack flowStackB - ) { - BiStackAnalysisImpl::flowStackIsConvergingTerminatingSubsetOf(flowStackA, - flowStackB) - } - } - - private module BiStackAnalysisImpl< - AbstractDataFlow::GlobalFlowSig GlobalFlowA, - AbstractDF::ConfigSig ConfigA, - DataFlowGroup::TaintTrackingStackSig DataFlowStackA, - AbstractDataFlow::GlobalFlowSig GlobalFlowB, - AbstractDF::ConfigSig ConfigB, - DataFlowGroup::TaintTrackingStackSig DataFlowStackB> - { - - module FlowStackA = FlowStack; - - module FlowStackB = FlowStack; - - /** - * Holds if stackA is a subset of stackB, - * The top of stackA is in stackB and the bottom of stackA is then some successor further down stackB. - */ - predicate flowStackIsSubsetOf(FlowStackA::FlowStack flowStackA, FlowStackB::FlowStack flowStackB) { - exists( - FlowStackA::FlowStackFrame highestStackFrameA, FlowStackB::FlowStackFrame highestStackFrameB - | - highestStackFrameA = flowStackA.getTopFrame() and - highestStackFrameB = flowStackB.getTopFrame() and - // Check if some intermediary frame `intStackFrameB`of StackB is in the stack of highestStackFrameA - exists(FlowStackB::FlowStackFrame intStackFrameB | - intStackFrameB = highestStackFrameB.getASucceedingTerminalStateFrame*() and - sharesCallWith(highestStackFrameA, intStackFrameB) and - sharesCallWith(flowStackA.getTerminalFrame(), - intStackFrameB.getASucceedingTerminalStateFrame*()) - ) - ) - } - - /** - * If the top of stackA is in stackB at any location, and the bottoms of the stack are the same call. - */ - predicate flowStackIsConvergingTerminatingSubsetOf( - FlowStackA::FlowStack flowStackA, FlowStackB::FlowStack flowStackB - ) { - flowStackA.getTerminalFrame().getCall() = flowStackB.getTerminalFrame().getCall() and - exists(FlowStackB::FlowStackFrame intStackFrameB | - intStackFrameB = flowStackB.getTopFrame().getASucceedingTerminalStateFrame*() and - sharesCallWith(flowStackA.getTopFrame(), intStackFrameB) - ) - } - - /** - * Holds if the given FlowStackFrames share the same call. - * i.e. they are both arguments of the same function call. - */ - predicate sharesCallWith(FlowStackA::FlowStackFrame frameA, FlowStackB::FlowStackFrame frameB) { - frameA.getCall() = frameB.getCall() - } - } - - module FlowStack< - AbstractDataFlow::GlobalFlowSig GlobalFlow, - AbstractDF::ConfigSig Config, - DataFlowGroup::TaintTrackingStackSig TaintTrackingStack> - { - private module Flow = GlobalFlow; - - /** - * Determines whether or not the given PathNode is a source - * TODO: Refactor to Flow::PathNode signature - */ - predicate isSource(Flow::PathNode node) { TaintTrackingStack::isSource(node) } - - /** - * Determines whether or not the given PathNode is a sink - * TODO: Refactor to Flow::PathNode signature - */ - predicate isSink(Flow::PathNode node) { not exists(TaintTrackingStack::getASuccessor(node)) } - - /** A FlowStack encapsulates flows between a source and a sink, and all the pathways inbetween (possibly multiple) */ - private newtype FlowStackType = - TFlowStack(Flow::PathNode source, Flow::PathNode sink) { - TaintTrackingStack::isSource(source) and - not exists(TaintTrackingStack::getASuccessor(sink)) and - TaintTrackingStack::getASuccessor*(source) = sink - } - - class FlowStack extends FlowStackType, TFlowStack { - string toString() { result = "FlowStack" } - - /** - * Get the first frame in the DataFlowStack, irregardless of whether or not it has a parent. - */ - FlowStackFrame getFirstFrame() { - exists(FlowStackFrame flowStackFrame, CallFrame frame | - flowStackFrame = TFlowStackFrame(this, frame) and - not exists(frame.getPredecessor()) and - result = flowStackFrame - ) - } - - /** - * Get the top frame in the DataFlowStack, ie the frame that is the highest in the stack for the given flow. - */ - FlowStackFrame getTopFrame() { - exists(FlowStackFrame flowStackFrame | - flowStackFrame = TFlowStackFrame(this, _) and - not exists(flowStackFrame.getParentStackFrame()) and - result = flowStackFrame - ) - } - - /** - * Get the terminal frame in the DataFlowStack, ie the frame that is the end of the flow. - */ - FlowStackFrame getTerminalFrame() { - exists(FlowStackFrame flowStackFrame, CallFrame frame | - flowStackFrame = TFlowStackFrame(this, frame) and - not exists(frame.getSuccessor()) and - result = flowStackFrame - ) - } - } - - FlowStack createFlowStack(Flow::PathNode source, Flow::PathNode sink) { - result = TFlowStack(source, sink) - } - - /** A FlowStackFrame encapsulates a Stack frame that is bound between a given source and sink. */ - private newtype FlowStackFrameType = - TFlowStackFrame(FlowStack flowStack, CallFrame frame) { - exists(Flow::PathNode source, Flow::PathNode sink | - flowStack = TFlowStack(source, sink) and - frame.getPathNode() = TaintTrackingStack::getASuccessor*(source) and - TaintTrackingStack::getASuccessor*(frame.getPathNode()) = sink - ) - } - - class FlowStackFrame extends FlowStackFrameType, TFlowStackFrame { - string toString() { result = "FlowStackFrame" } - - /** - * Get the next frame in the DataFlow Stack - */ - FlowStackFrame getASuccessor() { - exists(FlowStack flowStack, CallFrame frame, CallFrame nextFrame | - this = TFlowStackFrame(flowStack, frame) and - nextFrame = frame.getSuccessor() and - result = TFlowStackFrame(flowStack, nextFrame) - ) - } - - /** - * Gets the next FlowStackFrame from the direct descendents that is a frame in the end-state (terminal) stack. - */ - FlowStackFrame getASucceedingTerminalStateFrame() { - result = this.getChildStackFrame() and - // There are no other direct children that are further in the flow - not result.getASuccessor+() = this.getChildStackFrame() - } - - /** - * Gets a predecessor FlowStackFrame of this FlowStackFrame. - */ - FlowStackFrame getAPredecessor() { result.getASuccessor() = this } - - /** - * Gets a predecessor FlowStackFrame that is a parent in the stack. - */ - FlowStackFrame getParentStackFrame() { result.getChildStackFrame() = this } - - /** - * Gets the set of succeeding FlowStackFrame which are a direct descendant of this frame in the Stack. - */ - FlowStackFrame getChildStackFrame() { - exists(FlowStackFrame transitiveSuccessor | - transitiveSuccessor = this.getASuccessor+() and - TaintTrackingStack::getARuntimeTarget(this.getCall()) = - transitiveSuccessor.getCall().getEnclosingCallable() and - result = transitiveSuccessor - ) - } - - /** - * Unpacks the PathNode associated with this FlowStackFrame - */ - Flow::PathNode getPathNode() { - exists(CallFrame callFrame | - this = TFlowStackFrame(_, callFrame) and - result = callFrame.getPathNode() - ) - } - - /** - * Unpacks the DataFlowCall associated with this FlowStackFrame - */ - Lang::DataFlowCall getCall() { result = this.getCallFrame().getCall() } - - /** - * Unpacks the CallFrame associated with this FlowStackFrame - */ - CallFrame getCallFrame() { this = TFlowStackFrame(_, result) } - } - - /** - * A CallFrame is a PathNode that represents a (DataFlowCall/Accessor). - */ - private newtype TCallFrameType = - TCallFrame(Flow::PathNode node) { - exists(Lang::DataFlowCall c | - TaintTrackingStack::getAnArgumentNode(c) = TaintTrackingStack::getNode(node) - ) - } - - /** - * The CallFrame is a PathNode that represents an argument to a Call. - */ - private class CallFrame extends TCallFrameType, TCallFrame { - string toString() { - exists(Lang::DataFlowCall call | - call = this.getCall() and - result = call.toString() - ) - } - - /** - * Find the set of CallFrames that are immediate successors of this CallFrame. - */ - CallFrame getSuccessor() { result = TCallFrame(getSuccessorCall(this.getPathNode())) } - - /** - * Find the set of CallFrames that are an immediate predecessor of this CallFrame. - */ - CallFrame getPredecessor() { - exists(CallFrame prior | - prior.getSuccessor() = this and - result = prior - ) - } - - /** - * Unpack the CallFrame and retrieve the associated DataFlowCall. - */ - Lang::DataFlowCall getCall() { - exists(Lang::DataFlowCall call, Flow::PathNode node | - this = TCallFrame(node) and - TaintTrackingStack::getAnArgumentNode(call) = TaintTrackingStack::getNode(node) and - result = call - ) - } - - /** - * Unpack the CallFrame and retrieve the associated PathNode. - */ - Flow::PathNode getPathNode() { - exists(Flow::PathNode n | - this = TCallFrame(n) and - result = n - ) - } - } - - /** - * From the given PathNode argument, find the set of successors that are an argument in a DataFlowCall, - * and return them as the result. - */ - private Flow::PathNode getSuccessorCall(Flow::PathNode n) { - exists(Flow::PathNode succ | - succ = TaintTrackingStack::getASuccessor(n) and - if - exists(Lang::DataFlowCall c | - TaintTrackingStack::getAnArgumentNode(c) = TaintTrackingStack::getNode(succ) - ) - then result = succ - else result = getSuccessorCall(succ) - ) - } - - /** - * A user-supplied predicate which given a Stack Frame, returns some Node associated with it. - */ - signature Lang::Node extractNodeFromFrame(Flow::PathNode pathNode); - - /** - * Provides some higher-order predicates for analyzing Stacks - */ - module StackFrameAnalysis { - /** - * Find the highest stack frame that satisfies the given predicate, - * and return the Node(s) that the user-supplied predicate returns. - * - * There should be no higher stack frame that satisfies the user-supplied predicate FROM the point that the - * argument . - */ - Lang::Node extractingFromHighestStackFrame(FlowStack flowStack) { - exists(FlowStackFrame topStackFrame, FlowStackFrame someStackFrame | - topStackFrame = flowStack.getTopFrame() and - someStackFrame = topStackFrame.getASuccessor*() and - result = customFrameCond(someStackFrame.getPathNode()) and - not exists(FlowStackFrame predecessor | - predecessor = someStackFrame.getAPredecessor+() and - // The predecessor is *not* prior to the user-given 'top' of the stack frame. - not predecessor = topStackFrame.getAPredecessor+() and - exists(customFrameCond(predecessor.getPathNode())) - ) - ) - } - - /** - * Find the lowest stack frame that satisfies the given predicate, - * and return the Node(s) that the user-supplied predicate returns. - */ - Lang::Node extractingFromLowestStackFrame(FlowStack flowStack) { - exists(FlowStackFrame topStackFrame, FlowStackFrame someStackFrame | - topStackFrame = flowStack.getTopFrame() and - someStackFrame = topStackFrame.getChildStackFrame*() and - result = customFrameCond(someStackFrame.getPathNode()) and - not exists(FlowStackFrame successor | - successor = someStackFrame.getChildStackFrame+() and - exists(customFrameCond(successor.getPathNode())) - ) - ) - } - } - } -} \ No newline at end of file