diff --git a/config/identical-files.json b/config/identical-files.json index 77bee6b5097..c56fbb40f8f 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -454,10 +454,6 @@ "ruby/ql/lib/codeql/ruby/security/internal/SensitiveDataHeuristics.qll", "swift/ql/lib/codeql/swift/security/internal/SensitiveDataHeuristics.qll" ], - "SummaryTypeTracker": [ - "python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll", - "ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll" - ], "IncompleteUrlSubstringSanitization": [ "javascript/ql/src/Security/CWE-020/IncompleteUrlSubstringSanitization.qll", "ruby/ql/src/queries/security/cwe-020/IncompleteUrlSubstringSanitization.qll" diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll b/python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll deleted file mode 100644 index efe7629fffc..00000000000 --- a/python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll +++ /dev/null @@ -1,412 +0,0 @@ -/** - * Provides the implementation of type tracking steps through flow summaries. - * To use this, you must implement the `Input` signature. You can then use the predicates in the `Output` - * signature to implement the predicates of the same names inside `TypeTrackerSpecific.qll`. - */ - -/** The classes and predicates needed to generate type-tracking steps from summaries. */ -signature module Input { - // Dataflow nodes - class Node; - - // Content - class TypeTrackerContent; - - class TypeTrackerContentFilter; - - // Relating content and filters - /** - * Gets a content filter to use for a `WithoutContent[content]` step, (data is not allowed to be stored in `content`) - * or has no result if - * the step should be treated as ordinary flow. - * - * `WithoutContent` is often used to perform strong updates on individual collection elements, but for - * type-tracking this is rarely beneficial and quite expensive. However, `WithoutContent` can be quite useful - * for restricting the type of an object, and in these cases we translate it to a filter. - */ - TypeTrackerContentFilter getFilterFromWithoutContentStep(TypeTrackerContent content); - - /** - * Gets a content filter to use for a `WithContent[content]` step, (data must be stored in `content`) - * or has no result if - * the step cannot be handled by type-tracking. - * - * `WithContent` is often used to perform strong updates on individual collection elements (or rather - * to preserve those that didn't get updated). But for type-tracking this is rarely beneficial and quite expensive. - * However, `WithContent` can be quite useful for restricting the type of an object, and in these cases we translate it to a filter. - */ - TypeTrackerContentFilter getFilterFromWithContentStep(TypeTrackerContent content); - - // Summaries and their stacks - class SummaryComponent; - - class SummaryComponentStack { - SummaryComponent head(); - } - - /** Gets a singleton stack containing `component`. */ - SummaryComponentStack singleton(SummaryComponent component); - - /** - * Gets the stack obtained by pushing `head` onto `tail`. - */ - SummaryComponentStack push(SummaryComponent head, SummaryComponentStack tail); - - /** Gets a singleton stack representing a return. */ - SummaryComponent return(); - - // Relating content to summaries - /** Gets a summary component for content `c`. */ - SummaryComponent content(TypeTrackerContent contents); - - /** Gets a summary component where data is not allowed to be stored in `contents`. */ - SummaryComponent withoutContent(TypeTrackerContent contents); - - /** Gets a summary component where data must be stored in `contents`. */ - SummaryComponent withContent(TypeTrackerContent contents); - - // Callables - class SummarizedCallable { - predicate propagatesFlow( - SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue - ); - } - - // Relating nodes to summaries - /** - * Gets a dataflow node respresenting the argument of `call` indicated by `arg`. - * - * Returns the post-update node of the argument when `isPostUpdate` is true. - */ - Node argumentOf(Node call, SummaryComponent arg, boolean isPostUpdate); - - /** Gets a dataflow node respresenting the parameter of `callable` indicated by `param`. */ - Node parameterOf(Node callable, SummaryComponent param); - - /** Gets a dataflow node respresenting the return of `callable` indicated by `return`. */ - Node returnOf(Node callable, SummaryComponent return); - - // Relating callables to nodes - /** Gets a dataflow node respresenting a call to `callable`. */ - Node callTo(SummarizedCallable callable); -} - -/** - * The predicates provided by a summary type tracker. - * These are meant to be used in `TypeTrackerSpecific.qll` - * inside the predicates of the same names. - */ -signature module Output { - /** - * Holds if there is a level step from `nodeFrom` to `nodeTo`, which does not depend on the call graph. - */ - predicate levelStepNoCall(I::Node nodeFrom, I::Node nodeTo); - - /** - * Holds if `nodeTo` is the result of accessing the `content` content of `nodeFrom`. - */ - predicate basicLoadStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content); - - /** - * Holds if `nodeFrom` is being written to the `content` content of the object in `nodeTo`. - */ - predicate basicStoreStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content); - - /** - * Holds if the `loadContent` of `nodeFrom` is stored in the `storeContent` of `nodeTo`. - */ - predicate basicLoadStoreStep( - I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent loadContent, - I::TypeTrackerContent storeContent - ); - - /** - * Holds if type-tracking should step from `nodeFrom` to `nodeTo` but block flow of contents matched by `filter` through here. - */ - predicate basicWithoutContentStep( - I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter - ); - - /** - * Holds if type-tracking should step from `nodeFrom` to `nodeTo` if inside a content matched by `filter`. - */ - predicate basicWithContentStep( - I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter - ); -} - -/** - * Implementation of the summary type tracker, that is type tracking through flow summaries. - */ -module SummaryFlow implements Output { - pragma[nomagic] - private predicate isNonLocal(I::SummaryComponent component) { - component = I::content(_) - or - component = I::withContent(_) - } - - pragma[nomagic] - private predicate hasLoadSummary( - I::SummarizedCallable callable, I::TypeTrackerContent contents, I::SummaryComponentStack input, - I::SummaryComponentStack output - ) { - callable.propagatesFlow(I::push(I::content(contents), input), output, true) and - not isNonLocal(input.head()) and - not isNonLocal(output.head()) - } - - pragma[nomagic] - private predicate hasStoreSummary( - I::SummarizedCallable callable, I::TypeTrackerContent contents, I::SummaryComponentStack input, - I::SummaryComponentStack output - ) { - not isNonLocal(input.head()) and - not isNonLocal(output.head()) and - ( - callable.propagatesFlow(input, I::push(I::content(contents), output), true) - or - // Allow the input to start with an arbitrary WithoutContent[X]. - // Since type-tracking only tracks one content deep, and we're about to store into another content, - // we're already preventing the input from being in a content. - callable - .propagatesFlow(I::push(I::withoutContent(_), input), - I::push(I::content(contents), output), true) - ) - } - - pragma[nomagic] - private predicate hasLoadStoreSummary( - I::SummarizedCallable callable, I::TypeTrackerContent loadContents, - I::TypeTrackerContent storeContents, I::SummaryComponentStack input, - I::SummaryComponentStack output - ) { - callable - .propagatesFlow(I::push(I::content(loadContents), input), - I::push(I::content(storeContents), output), true) and - not isNonLocal(input.head()) and - not isNonLocal(output.head()) - } - - pragma[nomagic] - private predicate hasWithoutContentSummary( - I::SummarizedCallable callable, I::TypeTrackerContentFilter filter, - I::SummaryComponentStack input, I::SummaryComponentStack output - ) { - exists(I::TypeTrackerContent content | - callable.propagatesFlow(I::push(I::withoutContent(content), input), output, true) and - filter = I::getFilterFromWithoutContentStep(content) and - not isNonLocal(input.head()) and - not isNonLocal(output.head()) and - input != output - ) - } - - pragma[nomagic] - private predicate hasWithContentSummary( - I::SummarizedCallable callable, I::TypeTrackerContentFilter filter, - I::SummaryComponentStack input, I::SummaryComponentStack output - ) { - exists(I::TypeTrackerContent content | - callable.propagatesFlow(I::push(I::withContent(content), input), output, true) and - filter = I::getFilterFromWithContentStep(content) and - not isNonLocal(input.head()) and - not isNonLocal(output.head()) and - input != output - ) - } - - private predicate componentLevelStep(I::SummaryComponent component) { - exists(I::TypeTrackerContent content | - component = I::withoutContent(content) and - not exists(I::getFilterFromWithoutContentStep(content)) - ) - } - - /** - * Gets a data flow `I::Node` corresponding an argument or return value of `call`, - * as specified by `component`. `isOutput` indicates whether the node represents - * an output node or an input node. - */ - bindingset[call, component] - private I::Node evaluateSummaryComponentLocal( - I::Node call, I::SummaryComponent component, boolean isOutput - ) { - result = I::argumentOf(call, component, isOutput) - or - component = I::return() and - result = call and - isOutput = true - } - - /** - * Holds if `callable` is relevant for type-tracking and we therefore want `stack` to - * be evaluated locally at its call sites. - */ - pragma[nomagic] - private predicate dependsOnSummaryComponentStack( - I::SummarizedCallable callable, I::SummaryComponentStack stack - ) { - exists(I::callTo(callable)) and - ( - callable.propagatesFlow(stack, _, true) - or - callable.propagatesFlow(_, stack, true) - or - // include store summaries as they may skip an initial step at the input - hasStoreSummary(callable, _, stack, _) - ) - or - dependsOnSummaryComponentStackCons(callable, _, stack) - } - - pragma[nomagic] - private predicate dependsOnSummaryComponentStackCons( - I::SummarizedCallable callable, I::SummaryComponent head, I::SummaryComponentStack tail - ) { - dependsOnSummaryComponentStack(callable, I::push(head, tail)) - } - - pragma[nomagic] - private predicate dependsOnSummaryComponentStackConsLocal( - I::SummarizedCallable callable, I::SummaryComponent head, I::SummaryComponentStack tail - ) { - dependsOnSummaryComponentStackCons(callable, head, tail) and - not isNonLocal(head) - } - - pragma[nomagic] - private predicate dependsOnSummaryComponentStackLeaf( - I::SummarizedCallable callable, I::SummaryComponent leaf - ) { - dependsOnSummaryComponentStack(callable, I::singleton(leaf)) - } - - /** - * Gets a data flow I::Node corresponding to the local input or output of `call` - * identified by `stack`, if possible. - */ - pragma[nomagic] - private I::Node evaluateSummaryComponentStackLocal( - I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack stack, boolean isOutput - ) { - exists(I::SummaryComponent component | - dependsOnSummaryComponentStackLeaf(callable, component) and - stack = I::singleton(component) and - call = I::callTo(callable) and - result = evaluateSummaryComponentLocal(call, component, isOutput) - ) - or - exists( - I::Node prev, I::SummaryComponent head, I::SummaryComponentStack tail, boolean isOutput0 - | - prev = evaluateSummaryComponentStackLocal(callable, call, tail, isOutput0) and - dependsOnSummaryComponentStackConsLocal(callable, pragma[only_bind_into](head), - pragma[only_bind_out](tail)) and - stack = I::push(pragma[only_bind_out](head), pragma[only_bind_out](tail)) - | - // `Parameter[X]` is only allowed in the output of flow summaries (hence `isOutput = true`), - // however the target of the parameter (e.g. `Argument[Y].Parameter[X]`) should be fetched - // not from a post-update argument node (hence `isOutput0 = false`) - result = I::parameterOf(prev, head) and - isOutput0 = false and - isOutput = true - or - // `ReturnValue` is only allowed in the input of flow summaries (hence `isOutput = false`), - // and the target of the return value (e.g. `Argument[X].ReturnValue`) should be fetched not - // from a post-update argument node (hence `isOutput0 = false`) - result = I::returnOf(prev, head) and - isOutput0 = false and - isOutput = false - or - componentLevelStep(head) and - result = prev and - isOutput = isOutput0 - ) - } - - // Implement Output - predicate levelStepNoCall(I::Node nodeFrom, I::Node nodeTo) { - exists( - I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, - I::SummaryComponentStack output - | - callable.propagatesFlow(input, output, true) and - call = I::callTo(callable) and - nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and - nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true) - ) - } - - predicate basicLoadStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content) { - exists( - I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, - I::SummaryComponentStack output - | - hasLoadSummary(callable, content, pragma[only_bind_into](input), - pragma[only_bind_into](output)) and - call = I::callTo(callable) and - nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and - nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true) - ) - } - - predicate basicStoreStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content) { - exists( - I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, - I::SummaryComponentStack output - | - hasStoreSummary(callable, content, pragma[only_bind_into](input), - pragma[only_bind_into](output)) and - call = I::callTo(callable) and - nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and - nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true) - ) - } - - predicate basicLoadStoreStep( - I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent loadContent, - I::TypeTrackerContent storeContent - ) { - exists( - I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, - I::SummaryComponentStack output - | - hasLoadStoreSummary(callable, loadContent, storeContent, pragma[only_bind_into](input), - pragma[only_bind_into](output)) and - call = I::callTo(callable) and - nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and - nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true) - ) - } - - predicate basicWithoutContentStep( - I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter - ) { - exists( - I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, - I::SummaryComponentStack output - | - hasWithoutContentSummary(callable, filter, pragma[only_bind_into](input), - pragma[only_bind_into](output)) and - call = I::callTo(callable) and - nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and - nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true) - ) - } - - predicate basicWithContentStep( - I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter - ) { - exists( - I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, - I::SummaryComponentStack output - | - hasWithContentSummary(callable, filter, pragma[only_bind_into](input), - pragma[only_bind_into](output)) and - call = I::callTo(callable) and - nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and - nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true) - ) - } -} diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackingImpl.qll b/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackingImpl.qll index 6e1482ae74c..4c3538d75f7 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackingImpl.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackingImpl.qll @@ -5,7 +5,7 @@ private import python private import semmle.python.internal.CachedStages private import semmle.python.dataflow.new.internal.DataFlowPublic as DataFlowPublic private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate -private import SummaryTypeTracker as SummaryTypeTracker +private import codeql.typetracking.internal.SummaryTypeTracker as SummaryTypeTracker private import semmle.python.dataflow.new.internal.FlowSummaryImpl as FlowSummaryImpl private import semmle.python.dataflow.new.internal.DataFlowDispatch as DataFlowDispatch @@ -14,13 +14,13 @@ private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input { class Node = DataFlowPublic::Node; // Content - class TypeTrackerContent = DataFlowPublic::ContentSet; + class Content = DataFlowPublic::ContentSet; - class TypeTrackerContentFilter = TypeTrackingInput::ContentFilter; + class ContentFilter = TypeTrackingInput::ContentFilter; - TypeTrackerContentFilter getFilterFromWithoutContentStep(TypeTrackerContent content) { none() } + ContentFilter getFilterFromWithoutContentStep(Content content) { none() } - TypeTrackerContentFilter getFilterFromWithContentStep(TypeTrackerContent content) { none() } + ContentFilter getFilterFromWithContentStep(Content content) { none() } // Callables class SummarizedCallable = FlowSummaryImpl::Private::SummarizedCallableImpl; @@ -37,9 +37,9 @@ private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input { // Relating content to summaries predicate content = FlowSummaryImpl::Private::SummaryComponent::content/1; - SummaryComponent withoutContent(TypeTrackerContent contents) { none() } + SummaryComponent withoutContent(Content contents) { none() } - SummaryComponent withContent(TypeTrackerContent contents) { none() } + SummaryComponent withContent(Content contents) { none() } predicate return = FlowSummaryImpl::Private::SummaryComponent::return/0; diff --git a/ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll b/ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll deleted file mode 100644 index efe7629fffc..00000000000 --- a/ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll +++ /dev/null @@ -1,412 +0,0 @@ -/** - * Provides the implementation of type tracking steps through flow summaries. - * To use this, you must implement the `Input` signature. You can then use the predicates in the `Output` - * signature to implement the predicates of the same names inside `TypeTrackerSpecific.qll`. - */ - -/** The classes and predicates needed to generate type-tracking steps from summaries. */ -signature module Input { - // Dataflow nodes - class Node; - - // Content - class TypeTrackerContent; - - class TypeTrackerContentFilter; - - // Relating content and filters - /** - * Gets a content filter to use for a `WithoutContent[content]` step, (data is not allowed to be stored in `content`) - * or has no result if - * the step should be treated as ordinary flow. - * - * `WithoutContent` is often used to perform strong updates on individual collection elements, but for - * type-tracking this is rarely beneficial and quite expensive. However, `WithoutContent` can be quite useful - * for restricting the type of an object, and in these cases we translate it to a filter. - */ - TypeTrackerContentFilter getFilterFromWithoutContentStep(TypeTrackerContent content); - - /** - * Gets a content filter to use for a `WithContent[content]` step, (data must be stored in `content`) - * or has no result if - * the step cannot be handled by type-tracking. - * - * `WithContent` is often used to perform strong updates on individual collection elements (or rather - * to preserve those that didn't get updated). But for type-tracking this is rarely beneficial and quite expensive. - * However, `WithContent` can be quite useful for restricting the type of an object, and in these cases we translate it to a filter. - */ - TypeTrackerContentFilter getFilterFromWithContentStep(TypeTrackerContent content); - - // Summaries and their stacks - class SummaryComponent; - - class SummaryComponentStack { - SummaryComponent head(); - } - - /** Gets a singleton stack containing `component`. */ - SummaryComponentStack singleton(SummaryComponent component); - - /** - * Gets the stack obtained by pushing `head` onto `tail`. - */ - SummaryComponentStack push(SummaryComponent head, SummaryComponentStack tail); - - /** Gets a singleton stack representing a return. */ - SummaryComponent return(); - - // Relating content to summaries - /** Gets a summary component for content `c`. */ - SummaryComponent content(TypeTrackerContent contents); - - /** Gets a summary component where data is not allowed to be stored in `contents`. */ - SummaryComponent withoutContent(TypeTrackerContent contents); - - /** Gets a summary component where data must be stored in `contents`. */ - SummaryComponent withContent(TypeTrackerContent contents); - - // Callables - class SummarizedCallable { - predicate propagatesFlow( - SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue - ); - } - - // Relating nodes to summaries - /** - * Gets a dataflow node respresenting the argument of `call` indicated by `arg`. - * - * Returns the post-update node of the argument when `isPostUpdate` is true. - */ - Node argumentOf(Node call, SummaryComponent arg, boolean isPostUpdate); - - /** Gets a dataflow node respresenting the parameter of `callable` indicated by `param`. */ - Node parameterOf(Node callable, SummaryComponent param); - - /** Gets a dataflow node respresenting the return of `callable` indicated by `return`. */ - Node returnOf(Node callable, SummaryComponent return); - - // Relating callables to nodes - /** Gets a dataflow node respresenting a call to `callable`. */ - Node callTo(SummarizedCallable callable); -} - -/** - * The predicates provided by a summary type tracker. - * These are meant to be used in `TypeTrackerSpecific.qll` - * inside the predicates of the same names. - */ -signature module Output { - /** - * Holds if there is a level step from `nodeFrom` to `nodeTo`, which does not depend on the call graph. - */ - predicate levelStepNoCall(I::Node nodeFrom, I::Node nodeTo); - - /** - * Holds if `nodeTo` is the result of accessing the `content` content of `nodeFrom`. - */ - predicate basicLoadStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content); - - /** - * Holds if `nodeFrom` is being written to the `content` content of the object in `nodeTo`. - */ - predicate basicStoreStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content); - - /** - * Holds if the `loadContent` of `nodeFrom` is stored in the `storeContent` of `nodeTo`. - */ - predicate basicLoadStoreStep( - I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent loadContent, - I::TypeTrackerContent storeContent - ); - - /** - * Holds if type-tracking should step from `nodeFrom` to `nodeTo` but block flow of contents matched by `filter` through here. - */ - predicate basicWithoutContentStep( - I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter - ); - - /** - * Holds if type-tracking should step from `nodeFrom` to `nodeTo` if inside a content matched by `filter`. - */ - predicate basicWithContentStep( - I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter - ); -} - -/** - * Implementation of the summary type tracker, that is type tracking through flow summaries. - */ -module SummaryFlow implements Output { - pragma[nomagic] - private predicate isNonLocal(I::SummaryComponent component) { - component = I::content(_) - or - component = I::withContent(_) - } - - pragma[nomagic] - private predicate hasLoadSummary( - I::SummarizedCallable callable, I::TypeTrackerContent contents, I::SummaryComponentStack input, - I::SummaryComponentStack output - ) { - callable.propagatesFlow(I::push(I::content(contents), input), output, true) and - not isNonLocal(input.head()) and - not isNonLocal(output.head()) - } - - pragma[nomagic] - private predicate hasStoreSummary( - I::SummarizedCallable callable, I::TypeTrackerContent contents, I::SummaryComponentStack input, - I::SummaryComponentStack output - ) { - not isNonLocal(input.head()) and - not isNonLocal(output.head()) and - ( - callable.propagatesFlow(input, I::push(I::content(contents), output), true) - or - // Allow the input to start with an arbitrary WithoutContent[X]. - // Since type-tracking only tracks one content deep, and we're about to store into another content, - // we're already preventing the input from being in a content. - callable - .propagatesFlow(I::push(I::withoutContent(_), input), - I::push(I::content(contents), output), true) - ) - } - - pragma[nomagic] - private predicate hasLoadStoreSummary( - I::SummarizedCallable callable, I::TypeTrackerContent loadContents, - I::TypeTrackerContent storeContents, I::SummaryComponentStack input, - I::SummaryComponentStack output - ) { - callable - .propagatesFlow(I::push(I::content(loadContents), input), - I::push(I::content(storeContents), output), true) and - not isNonLocal(input.head()) and - not isNonLocal(output.head()) - } - - pragma[nomagic] - private predicate hasWithoutContentSummary( - I::SummarizedCallable callable, I::TypeTrackerContentFilter filter, - I::SummaryComponentStack input, I::SummaryComponentStack output - ) { - exists(I::TypeTrackerContent content | - callable.propagatesFlow(I::push(I::withoutContent(content), input), output, true) and - filter = I::getFilterFromWithoutContentStep(content) and - not isNonLocal(input.head()) and - not isNonLocal(output.head()) and - input != output - ) - } - - pragma[nomagic] - private predicate hasWithContentSummary( - I::SummarizedCallable callable, I::TypeTrackerContentFilter filter, - I::SummaryComponentStack input, I::SummaryComponentStack output - ) { - exists(I::TypeTrackerContent content | - callable.propagatesFlow(I::push(I::withContent(content), input), output, true) and - filter = I::getFilterFromWithContentStep(content) and - not isNonLocal(input.head()) and - not isNonLocal(output.head()) and - input != output - ) - } - - private predicate componentLevelStep(I::SummaryComponent component) { - exists(I::TypeTrackerContent content | - component = I::withoutContent(content) and - not exists(I::getFilterFromWithoutContentStep(content)) - ) - } - - /** - * Gets a data flow `I::Node` corresponding an argument or return value of `call`, - * as specified by `component`. `isOutput` indicates whether the node represents - * an output node or an input node. - */ - bindingset[call, component] - private I::Node evaluateSummaryComponentLocal( - I::Node call, I::SummaryComponent component, boolean isOutput - ) { - result = I::argumentOf(call, component, isOutput) - or - component = I::return() and - result = call and - isOutput = true - } - - /** - * Holds if `callable` is relevant for type-tracking and we therefore want `stack` to - * be evaluated locally at its call sites. - */ - pragma[nomagic] - private predicate dependsOnSummaryComponentStack( - I::SummarizedCallable callable, I::SummaryComponentStack stack - ) { - exists(I::callTo(callable)) and - ( - callable.propagatesFlow(stack, _, true) - or - callable.propagatesFlow(_, stack, true) - or - // include store summaries as they may skip an initial step at the input - hasStoreSummary(callable, _, stack, _) - ) - or - dependsOnSummaryComponentStackCons(callable, _, stack) - } - - pragma[nomagic] - private predicate dependsOnSummaryComponentStackCons( - I::SummarizedCallable callable, I::SummaryComponent head, I::SummaryComponentStack tail - ) { - dependsOnSummaryComponentStack(callable, I::push(head, tail)) - } - - pragma[nomagic] - private predicate dependsOnSummaryComponentStackConsLocal( - I::SummarizedCallable callable, I::SummaryComponent head, I::SummaryComponentStack tail - ) { - dependsOnSummaryComponentStackCons(callable, head, tail) and - not isNonLocal(head) - } - - pragma[nomagic] - private predicate dependsOnSummaryComponentStackLeaf( - I::SummarizedCallable callable, I::SummaryComponent leaf - ) { - dependsOnSummaryComponentStack(callable, I::singleton(leaf)) - } - - /** - * Gets a data flow I::Node corresponding to the local input or output of `call` - * identified by `stack`, if possible. - */ - pragma[nomagic] - private I::Node evaluateSummaryComponentStackLocal( - I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack stack, boolean isOutput - ) { - exists(I::SummaryComponent component | - dependsOnSummaryComponentStackLeaf(callable, component) and - stack = I::singleton(component) and - call = I::callTo(callable) and - result = evaluateSummaryComponentLocal(call, component, isOutput) - ) - or - exists( - I::Node prev, I::SummaryComponent head, I::SummaryComponentStack tail, boolean isOutput0 - | - prev = evaluateSummaryComponentStackLocal(callable, call, tail, isOutput0) and - dependsOnSummaryComponentStackConsLocal(callable, pragma[only_bind_into](head), - pragma[only_bind_out](tail)) and - stack = I::push(pragma[only_bind_out](head), pragma[only_bind_out](tail)) - | - // `Parameter[X]` is only allowed in the output of flow summaries (hence `isOutput = true`), - // however the target of the parameter (e.g. `Argument[Y].Parameter[X]`) should be fetched - // not from a post-update argument node (hence `isOutput0 = false`) - result = I::parameterOf(prev, head) and - isOutput0 = false and - isOutput = true - or - // `ReturnValue` is only allowed in the input of flow summaries (hence `isOutput = false`), - // and the target of the return value (e.g. `Argument[X].ReturnValue`) should be fetched not - // from a post-update argument node (hence `isOutput0 = false`) - result = I::returnOf(prev, head) and - isOutput0 = false and - isOutput = false - or - componentLevelStep(head) and - result = prev and - isOutput = isOutput0 - ) - } - - // Implement Output - predicate levelStepNoCall(I::Node nodeFrom, I::Node nodeTo) { - exists( - I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, - I::SummaryComponentStack output - | - callable.propagatesFlow(input, output, true) and - call = I::callTo(callable) and - nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and - nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true) - ) - } - - predicate basicLoadStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content) { - exists( - I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, - I::SummaryComponentStack output - | - hasLoadSummary(callable, content, pragma[only_bind_into](input), - pragma[only_bind_into](output)) and - call = I::callTo(callable) and - nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and - nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true) - ) - } - - predicate basicStoreStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content) { - exists( - I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, - I::SummaryComponentStack output - | - hasStoreSummary(callable, content, pragma[only_bind_into](input), - pragma[only_bind_into](output)) and - call = I::callTo(callable) and - nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and - nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true) - ) - } - - predicate basicLoadStoreStep( - I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent loadContent, - I::TypeTrackerContent storeContent - ) { - exists( - I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, - I::SummaryComponentStack output - | - hasLoadStoreSummary(callable, loadContent, storeContent, pragma[only_bind_into](input), - pragma[only_bind_into](output)) and - call = I::callTo(callable) and - nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and - nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true) - ) - } - - predicate basicWithoutContentStep( - I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter - ) { - exists( - I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, - I::SummaryComponentStack output - | - hasWithoutContentSummary(callable, filter, pragma[only_bind_into](input), - pragma[only_bind_into](output)) and - call = I::callTo(callable) and - nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and - nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true) - ) - } - - predicate basicWithContentStep( - I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter - ) { - exists( - I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, - I::SummaryComponentStack output - | - hasWithContentSummary(callable, filter, pragma[only_bind_into](input), - pragma[only_bind_into](output)) and - call = I::callTo(callable) and - nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and - nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true) - ) - } -} diff --git a/ruby/ql/lib/codeql/ruby/typetracking/internal/TypeTrackingImpl.qll b/ruby/ql/lib/codeql/ruby/typetracking/internal/TypeTrackingImpl.qll index 13f6c1de149..750c96ef723 100644 --- a/ruby/ql/lib/codeql/ruby/typetracking/internal/TypeTrackingImpl.qll +++ b/ruby/ql/lib/codeql/ruby/typetracking/internal/TypeTrackingImpl.qll @@ -3,7 +3,7 @@ import codeql.typetracking.internal.TypeTrackingImpl as SharedImpl private import codeql.ruby.AST private import codeql.ruby.CFG as Cfg private import Cfg::CfgNodes -private import SummaryTypeTracker as SummaryTypeTracker +private import codeql.typetracking.internal.SummaryTypeTracker as SummaryTypeTracker private import codeql.ruby.DataFlow private import codeql.ruby.dataflow.FlowSummary as FlowSummary private import codeql.ruby.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon @@ -133,11 +133,11 @@ private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input { class Node = DataFlow::Node; // Content - class TypeTrackerContent = DataFlowPublic::ContentSet; + class Content = DataFlowPublic::ContentSet; - class TypeTrackerContentFilter = TypeTrackingInput::ContentFilter; + class ContentFilter = TypeTrackingInput::ContentFilter; - TypeTrackerContentFilter getFilterFromWithoutContentStep(TypeTrackerContent content) { + ContentFilter getFilterFromWithoutContentStep(Content content) { ( content.isAnyElement() or @@ -150,7 +150,7 @@ private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input { result = MkElementFilter() } - TypeTrackerContentFilter getFilterFromWithContentStep(TypeTrackerContent content) { + ContentFilter getFilterFromWithContentStep(Content content) { ( content.isAnyElement() or diff --git a/shared/typetracking/codeql/typetracking/internal/SummaryTypeTracker.qll b/shared/typetracking/codeql/typetracking/internal/SummaryTypeTracker.qll index efe7629fffc..b942446d43b 100644 --- a/shared/typetracking/codeql/typetracking/internal/SummaryTypeTracker.qll +++ b/shared/typetracking/codeql/typetracking/internal/SummaryTypeTracker.qll @@ -10,9 +10,9 @@ signature module Input { class Node; // Content - class TypeTrackerContent; + class Content; - class TypeTrackerContentFilter; + class ContentFilter; // Relating content and filters /** @@ -24,7 +24,7 @@ signature module Input { * type-tracking this is rarely beneficial and quite expensive. However, `WithoutContent` can be quite useful * for restricting the type of an object, and in these cases we translate it to a filter. */ - TypeTrackerContentFilter getFilterFromWithoutContentStep(TypeTrackerContent content); + ContentFilter getFilterFromWithoutContentStep(Content content); /** * Gets a content filter to use for a `WithContent[content]` step, (data must be stored in `content`) @@ -35,7 +35,7 @@ signature module Input { * to preserve those that didn't get updated). But for type-tracking this is rarely beneficial and quite expensive. * However, `WithContent` can be quite useful for restricting the type of an object, and in these cases we translate it to a filter. */ - TypeTrackerContentFilter getFilterFromWithContentStep(TypeTrackerContent content); + ContentFilter getFilterFromWithContentStep(Content content); // Summaries and their stacks class SummaryComponent; @@ -57,13 +57,13 @@ signature module Input { // Relating content to summaries /** Gets a summary component for content `c`. */ - SummaryComponent content(TypeTrackerContent contents); + SummaryComponent content(Content contents); /** Gets a summary component where data is not allowed to be stored in `contents`. */ - SummaryComponent withoutContent(TypeTrackerContent contents); + SummaryComponent withoutContent(Content contents); /** Gets a summary component where data must be stored in `contents`. */ - SummaryComponent withContent(TypeTrackerContent contents); + SummaryComponent withContent(Content contents); // Callables class SummarizedCallable { @@ -105,34 +105,29 @@ signature module Output { /** * Holds if `nodeTo` is the result of accessing the `content` content of `nodeFrom`. */ - predicate basicLoadStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content); + predicate basicLoadStep(I::Node nodeFrom, I::Node nodeTo, I::Content content); /** * Holds if `nodeFrom` is being written to the `content` content of the object in `nodeTo`. */ - predicate basicStoreStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content); + predicate basicStoreStep(I::Node nodeFrom, I::Node nodeTo, I::Content content); /** * Holds if the `loadContent` of `nodeFrom` is stored in the `storeContent` of `nodeTo`. */ predicate basicLoadStoreStep( - I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent loadContent, - I::TypeTrackerContent storeContent + I::Node nodeFrom, I::Node nodeTo, I::Content loadContent, I::Content storeContent ); /** * Holds if type-tracking should step from `nodeFrom` to `nodeTo` but block flow of contents matched by `filter` through here. */ - predicate basicWithoutContentStep( - I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter - ); + predicate basicWithoutContentStep(I::Node nodeFrom, I::Node nodeTo, I::ContentFilter filter); /** * Holds if type-tracking should step from `nodeFrom` to `nodeTo` if inside a content matched by `filter`. */ - predicate basicWithContentStep( - I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter - ); + predicate basicWithContentStep(I::Node nodeFrom, I::Node nodeTo, I::ContentFilter filter); } /** @@ -148,7 +143,7 @@ module SummaryFlow implements Output { pragma[nomagic] private predicate hasLoadSummary( - I::SummarizedCallable callable, I::TypeTrackerContent contents, I::SummaryComponentStack input, + I::SummarizedCallable callable, I::Content contents, I::SummaryComponentStack input, I::SummaryComponentStack output ) { callable.propagatesFlow(I::push(I::content(contents), input), output, true) and @@ -158,7 +153,7 @@ module SummaryFlow implements Output { pragma[nomagic] private predicate hasStoreSummary( - I::SummarizedCallable callable, I::TypeTrackerContent contents, I::SummaryComponentStack input, + I::SummarizedCallable callable, I::Content contents, I::SummaryComponentStack input, I::SummaryComponentStack output ) { not isNonLocal(input.head()) and @@ -177,9 +172,8 @@ module SummaryFlow implements Output { pragma[nomagic] private predicate hasLoadStoreSummary( - I::SummarizedCallable callable, I::TypeTrackerContent loadContents, - I::TypeTrackerContent storeContents, I::SummaryComponentStack input, - I::SummaryComponentStack output + I::SummarizedCallable callable, I::Content loadContents, I::Content storeContents, + I::SummaryComponentStack input, I::SummaryComponentStack output ) { callable .propagatesFlow(I::push(I::content(loadContents), input), @@ -190,10 +184,10 @@ module SummaryFlow implements Output { pragma[nomagic] private predicate hasWithoutContentSummary( - I::SummarizedCallable callable, I::TypeTrackerContentFilter filter, - I::SummaryComponentStack input, I::SummaryComponentStack output + I::SummarizedCallable callable, I::ContentFilter filter, I::SummaryComponentStack input, + I::SummaryComponentStack output ) { - exists(I::TypeTrackerContent content | + exists(I::Content content | callable.propagatesFlow(I::push(I::withoutContent(content), input), output, true) and filter = I::getFilterFromWithoutContentStep(content) and not isNonLocal(input.head()) and @@ -204,10 +198,10 @@ module SummaryFlow implements Output { pragma[nomagic] private predicate hasWithContentSummary( - I::SummarizedCallable callable, I::TypeTrackerContentFilter filter, - I::SummaryComponentStack input, I::SummaryComponentStack output + I::SummarizedCallable callable, I::ContentFilter filter, I::SummaryComponentStack input, + I::SummaryComponentStack output ) { - exists(I::TypeTrackerContent content | + exists(I::Content content | callable.propagatesFlow(I::push(I::withContent(content), input), output, true) and filter = I::getFilterFromWithContentStep(content) and not isNonLocal(input.head()) and @@ -217,7 +211,7 @@ module SummaryFlow implements Output { } private predicate componentLevelStep(I::SummaryComponent component) { - exists(I::TypeTrackerContent content | + exists(I::Content content | component = I::withoutContent(content) and not exists(I::getFilterFromWithoutContentStep(content)) ) @@ -338,7 +332,7 @@ module SummaryFlow implements Output { ) } - predicate basicLoadStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content) { + predicate basicLoadStep(I::Node nodeFrom, I::Node nodeTo, I::Content content) { exists( I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, I::SummaryComponentStack output @@ -351,7 +345,7 @@ module SummaryFlow implements Output { ) } - predicate basicStoreStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content) { + predicate basicStoreStep(I::Node nodeFrom, I::Node nodeTo, I::Content content) { exists( I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, I::SummaryComponentStack output @@ -365,8 +359,7 @@ module SummaryFlow implements Output { } predicate basicLoadStoreStep( - I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent loadContent, - I::TypeTrackerContent storeContent + I::Node nodeFrom, I::Node nodeTo, I::Content loadContent, I::Content storeContent ) { exists( I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, @@ -380,9 +373,7 @@ module SummaryFlow implements Output { ) } - predicate basicWithoutContentStep( - I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter - ) { + predicate basicWithoutContentStep(I::Node nodeFrom, I::Node nodeTo, I::ContentFilter filter) { exists( I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, I::SummaryComponentStack output @@ -395,9 +386,7 @@ module SummaryFlow implements Output { ) } - predicate basicWithContentStep( - I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter - ) { + predicate basicWithContentStep(I::Node nodeFrom, I::Node nodeTo, I::ContentFilter filter) { exists( I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, I::SummaryComponentStack output