diff --git a/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll b/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll index fef69bec7fd..b969400c0c5 100644 --- a/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll +++ b/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll @@ -57,6 +57,22 @@ abstract class FluentMethod extends ValuePreservingMethod { override predicate returnsValue(int arg) { arg = -1 } } +/** + * A unit class for adding additional data flow nodes. + * + * Extend this class to add additional data flow nodes for use in globally + * applicable additional steps. + */ +class AdditionalDataFlowNode extends Unit { + /** + * Holds if an additional node is needed in relation to `e`. The pair `(e,id)` + * must uniquely identify the node. + * The added node can be selected for use in a predicate by the corresponding + * `DataFlow::AdditionalNode.nodeAt(Expr e, string id)` predicate. + */ + abstract predicate nodeAt(Expr e, string id); +} + /** * A unit class for adding additional taint steps. * @@ -85,6 +101,36 @@ class AdditionalValueStep extends Unit { abstract predicate step(DataFlow::Node node1, DataFlow::Node node2); } +/** + * A unit class for adding additional store steps. + * + * Extend this class to add additional store steps that should apply to all + * data flow configurations. A store step must be local, so non-local steps are + * ignored. + */ +class AdditionalStoreStep extends Unit { + /** + * Holds if the step from `node1` to `node2` is a store step of `c` and should + * apply to all data flow configurations. + */ + abstract predicate step(DataFlow::Node node1, DataFlow::Content c, DataFlow::Node node2); +} + +/** + * A unit class for adding additional read steps. + * + * Extend this class to add additional read steps that should apply to all + * data flow configurations. A read step must be local, so non-local steps are + * ignored. + */ +class AdditionalReadStep extends Unit { + /** + * Holds if the step from `node1` to `node2` is a read step of `c` and should + * apply to all data flow configurations. + */ + abstract predicate step(DataFlow::Node node1, DataFlow::Content c, DataFlow::Node node2); +} + /** * A method or constructor that preserves taint. * diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll index 7ee703808e2..44061e1cd00 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll @@ -3,6 +3,7 @@ private import semmle.code.java.dataflow.InstanceAccess private import semmle.code.java.dataflow.ExternalFlow private import semmle.code.java.dataflow.FlowSummary private import semmle.code.java.dataflow.TypeFlow +private import semmle.code.java.dataflow.FlowSteps private import DataFlowPrivate private import DataFlowUtil private import FlowSummaryImpl as FlowSummaryImpl @@ -56,7 +57,8 @@ private module Cached { } or TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or TFieldValueNode(Field f) or - TCaptureNode(CaptureFlow::SynthesizedCaptureNode cn) + TCaptureNode(CaptureFlow::SynthesizedCaptureNode cn) or + TAdditionalNode(Expr e, string id) { any(AdditionalDataFlowNode adfn).nodeAt(e, id) } cached newtype TContent = @@ -133,6 +135,8 @@ module Public { result = this.(CaptureNode).getTypeImpl() or result = this.(FieldValueNode).getField().getType() + or + result instanceof TypeObject and this instanceof AdditionalNode } /** Gets the callable in which this node occurs. */ @@ -335,6 +339,21 @@ module Public { /** Holds if this is an access to an object's own instance. */ predicate isOwnInstanceAccess() { this.getInstanceAccess().isOwnInstanceAccess() } } + + /** A node introduced by an extension of `AdditionalDataFlowNode`. */ + class AdditionalNode extends Node, TAdditionalNode { + Expr e_; + string id_; + + AdditionalNode() { this = TAdditionalNode(e_, id_) } + + override string toString() { result = e_.toString() + " (" + id_ + ")" } + + override Location getLocation() { result = e_.getLocation() } + + /** Holds if this node was introduced by `AdditionalDataFlowNode.nodeAt(e, id)`. */ + predicate nodeAt(Expr e, string id) { e = e_ and id = id_ } + } } private import Public @@ -378,7 +397,8 @@ module Private { result = nodeGetEnclosingCallable(n.(ImplicitPostUpdateNode).getPreUpdateNode()) or result.asSummarizedCallable() = n.(FlowSummaryNode).getSummarizedCallable() or result.asCallable() = n.(CaptureNode).getSynthesizedCaptureNode().getEnclosingCallable() or - result.asFieldScope() = n.(FieldValueNode).getField() + result.asFieldScope() = n.(FieldValueNode).getField() or + result.asCallable() = any(Expr e | n.(AdditionalNode).nodeAt(e, _)).getEnclosingCallable() } /** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */ diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll index 6223c9eae7a..bac40a951d8 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll @@ -228,6 +228,10 @@ predicate storeStep(Node node1, ContentSet f, Node node2) { node2.(FlowSummaryNode).getSummaryNode()) or captureStoreStep(node1, f, node2) + or + any(AdditionalStoreStep a).step(node1, f, node2) and + pragma[only_bind_out](node1.getEnclosingCallable()) = + pragma[only_bind_out](node2.getEnclosingCallable()) } /** @@ -262,6 +266,10 @@ predicate readStep(Node node1, ContentSet f, Node node2) { node2.(FlowSummaryNode).getSummaryNode()) or captureReadStep(node1, f, node2) + or + any(AdditionalReadStep a).step(node1, f, node2) and + pragma[only_bind_out](node1.getEnclosingCallable()) = + pragma[only_bind_out](node2.getEnclosingCallable()) } /**