diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll index 424f3651bf88..5ba1db044290 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll @@ -12,8 +12,14 @@ private import semmle.code.cpp.dataflow.ExternalFlow private import semmle.code.cpp.ir.IR module Input implements InputSig { + private import codeql.util.Void + class SummarizedCallableBase = Function; + class SourceBase = Void; + + class SinkBase = Void; + ArgumentPosition callbackSelfParameterPosition() { result = TDirectPosition(-1) } ReturnKind getStandardReturnValueKind() { result.(NormalReturnKind).getIndirectionIndex() = 0 } @@ -93,6 +99,10 @@ private module StepsInput implements Impl::Private::StepsInputSig { DataFlowCall getACall(Public::SummarizedCallable sc) { result.getStaticCallTarget().getUnderlyingCallable() = sc } + + Node getSourceNode(Input::SourceBase source, Impl::Private::SummaryComponent sc) { none() } + + Node getSinkNode(Input::SinkBase sink, Impl::Private::SummaryComponent sc) { none() } } module SourceSinkInterpretationInput implements diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll index d777566a336a..5d861464c8e3 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll @@ -14,8 +14,14 @@ private import semmle.code.csharp.Unification private import semmle.code.csharp.dataflow.internal.ExternalFlow module Input implements InputSig { + private import codeql.util.Void + class SummarizedCallableBase = UnboundCallable; + class SourceBase = Void; + + class SinkBase = Void; + predicate neutralElement(SummarizedCallableBase c, string kind, string provenance, boolean isExact) { interpretNeutral(c, kind, provenance) and // isExact is not needed for C#. @@ -176,12 +182,22 @@ private module TypesInput implements Impl::Private::TypesInputSig { result.asGvnType() = Gvn::getGlobalValueNumber(dt.getDelegateType().getReturnType()) ) } + + DataFlowType getSourceNodeType(Input::SourceBase source, Impl::Private::SummaryComponent sc) { + none() + } + + DataFlowType getSinkNodeType(Input::SinkBase sink, Impl::Private::SummaryComponent sc) { none() } } private module StepsInput implements Impl::Private::StepsInputSig { DataFlowCall getACall(Public::SummarizedCallable sc) { sc = viableCallable(result).asSummarizedCallable() } + + Node getSourceNode(Input::SourceBase source, Impl::Private::SummaryComponent sc) { none() } + + Node getSinkNode(Input::SinkBase sink, Impl::Private::SummaryComponent sc) { none() } } module SourceSinkInterpretationInput implements diff --git a/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll b/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll index 5ae7b6a7f0d8..3228f4248859 100644 --- a/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll +++ b/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll @@ -90,7 +90,7 @@ import internal.ExternalFlowExtensions as FlowExtensions private import FlowSummary as FlowSummary private import internal.DataFlowPrivate private import internal.FlowSummaryImpl -private import internal.FlowSummaryImpl::Public +private import internal.FlowSummaryImpl::Public as Public private import internal.FlowSummaryImpl::Private private import internal.FlowSummaryImpl::Private::External private import codeql.mad.ModelValidation as SharedModelVal @@ -583,13 +583,13 @@ predicate sourceNode(DataFlow::Node node, string kind) { sourceNode(node, kind, predicate sinkNode(DataFlow::Node node, string kind) { sinkNode(node, kind, _) } // adapter class for converting Mad summaries to `SummarizedCallable`s -private class SummarizedCallableAdapter extends SummarizedCallable { +private class SummarizedCallableAdapter extends Public::SummarizedCallable { SummarizedCallableAdapter() { summaryElement(this, _, _, _, _, _) } private predicate relevantSummaryElementManual( string input, string output, string kind, string model ) { - exists(Provenance provenance | + exists(Public::Provenance provenance | summaryElement(this, input, output, kind, provenance, model) and provenance.isManual() ) @@ -598,11 +598,11 @@ private class SummarizedCallableAdapter extends SummarizedCallable { private predicate relevantSummaryElementGenerated( string input, string output, string kind, string model ) { - exists(Provenance provenance | + exists(Public::Provenance provenance | summaryElement(this, input, output, kind, provenance, model) and provenance.isGenerated() ) and - not exists(Provenance provenance | + not exists(Public::Provenance provenance | neutralElement(this, "summary", provenance) and provenance.isManual() ) @@ -621,7 +621,7 @@ private class SummarizedCallableAdapter extends SummarizedCallable { ) } - override predicate hasProvenance(Provenance provenance) { + override predicate hasProvenance(Public::Provenance provenance) { summaryElement(this, _, _, _, provenance, _) } } diff --git a/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll b/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll index 40c68ceb900a..55de73895938 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll @@ -21,8 +21,14 @@ private string positionToString(int pos) { } module Input implements InputSig { + private import codeql.util.Void + class SummarizedCallableBase = Callable; + class SourceBase = Void; + + class SinkBase = Void; + predicate neutralElement( Input::SummarizedCallableBase c, string kind, string provenance, boolean isExact ) { @@ -108,6 +114,10 @@ private module StepsInput implements Impl::Private::StepsInputSig { call.getACalleeIncludingExternals() = sc ) } + + Node getSourceNode(Input::SourceBase source, Impl::Private::SummaryComponent sc) { none() } + + Node getSinkNode(Input::SinkBase sink, Impl::Private::SummaryComponent sc) { none() } } module SourceSinkInterpretationInput implements diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll b/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll index b4a2bad48f35..37201245a58b 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll @@ -27,8 +27,14 @@ private string positionToString(int pos) { } module Input implements InputSig { + private import codeql.util.Void + class SummarizedCallableBase = FlowSummary::SummarizedCallableBase; + class SourceBase = Void; + + class SinkBase = Void; + predicate neutralElement( Input::SummarizedCallableBase c, string kind, string provenance, boolean isExact ) { @@ -123,12 +129,22 @@ private module TypesInput implements Impl::Private::TypesInputSig { result = getErasedRepr(t.(FunctionalInterface).getRunMethod().getReturnType()) and exists(rk) } + + DataFlowType getSourceNodeType(Input::SourceBase source, Impl::Private::SummaryComponent sc) { + none() + } + + DataFlowType getSinkNodeType(Input::SinkBase sink, Impl::Private::SummaryComponent sc) { none() } } private module StepsInput implements Impl::Private::StepsInputSig { DataFlowCall getACall(Public::SummarizedCallable sc) { sc = viableCallable(result).asSummarizedCallable() } + + Node getSourceNode(Input::SourceBase source, Impl::Private::SummaryComponent sc) { none() } + + Node getSinkNode(Input::SinkBase sink, Impl::Private::SummaryComponent sc) { none() } } private predicate relatedArgSpec(Callable c, string spec) { diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll b/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll index a1dd67a008ad..f7fdf84549e6 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll @@ -10,8 +10,14 @@ private import DataFlowImplSpecific::Private private import DataFlowImplSpecific::Public module Input implements InputSig { + private import codeql.util.Void + class SummarizedCallableBase = string; + class SourceBase = Void; + + class SinkBase = Void; + ArgumentPosition callbackSelfParameterPosition() { result.isLambdaSelf() } ReturnKind getStandardReturnValueKind() { any() } @@ -98,6 +104,10 @@ private module StepsInput implements Impl::Private::StepsInputSig { sc.(LibraryCallable).getACallSimple().asCfgNode() ]) } + + Node getSourceNode(Input::SourceBase source, Impl::Private::SummaryComponent sc) { none() } + + Node getSinkNode(Input::SinkBase sink, Impl::Private::SummaryComponent sc) { none() } } module Private { diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll index d0beb44ac7b4..c85d2230b23d 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll @@ -10,8 +10,14 @@ private import DataFlowImplSpecific::Private private import DataFlowImplSpecific::Public module Input implements InputSig { + private import codeql.util.Void + class SummarizedCallableBase = string; + class SourceBase = Void; + + class SinkBase = Void; + ArgumentPosition callbackSelfParameterPosition() { result.isLambdaSelf() } ReturnKind getStandardReturnValueKind() { result instanceof NormalReturnKind } @@ -154,6 +160,10 @@ private module StepsInput implements Impl::Private::StepsInputSig { or result.asCall().getAstNode() = sc.(LibraryCallable).getACallSimple() } + + Node getSourceNode(Input::SourceBase source, Impl::Private::SummaryComponent sc) { none() } + + Node getSinkNode(Input::SinkBase sink, Impl::Private::SummaryComponent sc) { none() } } module Private { diff --git a/rust/ql/lib/codeql/rust/dataflow/FlowSink.qll b/rust/ql/lib/codeql/rust/dataflow/FlowSink.qll new file mode 100644 index 000000000000..70401b1dbc05 --- /dev/null +++ b/rust/ql/lib/codeql/rust/dataflow/FlowSink.qll @@ -0,0 +1,36 @@ +/** Provides classes and predicates for defining flow sinks. */ + +private import rust +private import internal.FlowSummaryImpl as Impl +private import internal.DataFlowImpl as DataFlowImpl + +// import all instances below +private module Sinks { + private import codeql.rust.Frameworks + private import codeql.rust.dataflow.internal.ModelsAsData +} + +/** Provides the `Range` class used to define the extent of `FlowSink`. */ +module FlowSink { + /** A flow source. */ + abstract class Range extends Impl::Public::SinkNode { + bindingset[this] + Range() { any() } + + override predicate isSink( + string input, string kind, Impl::Public::Provenance provenance, string model + ) { + this.isSink(input, kind) and provenance = "manual" and model = "" + } + + /** + * Holds is this element is a flow sink of kind `kind`, where data + * flows in as described by `input`. + */ + predicate isSink(string output, string kind) { none() } + } +} + +final class FlowSink = FlowSink::Range; + +predicate sinkNode = DataFlowImpl::sinkNode/2; diff --git a/rust/ql/lib/codeql/rust/dataflow/FlowSource.qll b/rust/ql/lib/codeql/rust/dataflow/FlowSource.qll new file mode 100644 index 000000000000..59495cd24c56 --- /dev/null +++ b/rust/ql/lib/codeql/rust/dataflow/FlowSource.qll @@ -0,0 +1,36 @@ +/** Provides classes and predicates for defining flow sources. */ + +private import rust +private import internal.FlowSummaryImpl as Impl +private import internal.DataFlowImpl as DataFlowImpl + +// import all instances below +private module Sources { + private import codeql.rust.Frameworks + private import codeql.rust.dataflow.internal.ModelsAsData +} + +/** Provides the `Range` class used to define the extent of `FlowSource`. */ +module FlowSource { + /** A flow source. */ + abstract class Range extends Impl::Public::SourceNode { + bindingset[this] + Range() { any() } + + override predicate isSource( + string output, string kind, Impl::Public::Provenance provenance, string model + ) { + this.isSource(output, kind) and provenance = "manual" and model = "" + } + + /** + * Holds is this element is a flow source of kind `kind`, where data + * flows out as described by `output`. + */ + predicate isSource(string output, string kind) { none() } + } +} + +final class FlowSource = FlowSource::Range; + +predicate sourceNode = DataFlowImpl::sourceNode/2; diff --git a/rust/ql/lib/codeql/rust/dataflow/FlowSummary.qll b/rust/ql/lib/codeql/rust/dataflow/FlowSummary.qll index d1ba69ba22d2..60e76ceff94f 100644 --- a/rust/ql/lib/codeql/rust/dataflow/FlowSummary.qll +++ b/rust/ql/lib/codeql/rust/dataflow/FlowSummary.qll @@ -57,3 +57,5 @@ module SummarizedCallable { } final class SummarizedCallable = SummarizedCallable::Range; + +final class Provenance = Impl::Public::Provenance; diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll index b6f8942320d0..beaaf7086b15 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll @@ -186,18 +186,49 @@ module Node { class FlowSummaryNode extends Node, TFlowSummaryNode { FlowSummaryImpl::Private::SummaryNode getSummaryNode() { this = TFlowSummaryNode(result) } - /** Gets the summarized callable that this node belongs to. */ + /** Gets the summarized callable that this node belongs to, if any. */ FlowSummaryImpl::Public::SummarizedCallable getSummarizedCallable() { result = this.getSummaryNode().getSummarizedCallable() } - override CfgScope getCfgScope() { none() } + /** Gets the source node that this node belongs to, if any */ + FlowSummaryImpl::Public::SourceNode getSourceNode() { + result = this.getSummaryNode().getSourceNode() + } + + /** Gets the sink node that this node belongs to, if any */ + FlowSummaryImpl::Public::SinkNode getSinkNode() { result = this.getSummaryNode().getSinkNode() } + + /** Holds is this node is a source node of kind `kind`. */ + predicate isSource(string kind) { + this.getSummaryNode().(FlowSummaryImpl::Private::SourceOutputNode).isEntry(kind) + } + + /** Holds is this node is a sink node of kind `kind`. */ + predicate isSink(string kind) { + this.getSummaryNode().(FlowSummaryImpl::Private::SinkInputNode).isExit(kind) + } + + override CfgScope getCfgScope() { + result = this.getSummaryNode().getSourceNode().getEnclosingCfgScope() + or + result = this.getSummaryNode().getSinkNode().getEnclosingCfgScope() + } override DataFlowCallable getEnclosingCallable() { result.asLibraryCallable() = this.getSummarizedCallable() + or + result.asCfgScope() = this.getCfgScope() } - override EmptyLocation getLocation() { any() } + override Location getLocation() { + exists(this.getSummarizedCallable()) and + result instanceof EmptyLocation + or + result = this.getSourceNode().getLocation() + or + result = this.getSinkNode().getLocation() + } override string toString() { result = this.getSummaryNode().toString() } } @@ -526,13 +557,20 @@ private ExprCfgNode getALastEvalNode(ExprCfgNode e) { } module LocalFlow { - predicate flowSummaryLocalStep( - Node::FlowSummaryNode nodeFrom, Node::FlowSummaryNode nodeTo, - FlowSummaryImpl::Public::SummarizedCallable c, string model - ) { - FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.getSummaryNode(), - nodeTo.getSummaryNode(), true, model) and - c = nodeFrom.getSummarizedCallable() + predicate flowSummaryLocalStep(Node nodeFrom, Node nodeTo, string model) { + exists(FlowSummaryImpl::Public::SummarizedCallable c | + FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom + .(Node::FlowSummaryNode) + .getSummaryNode(), nodeTo.(Node::FlowSummaryNode).getSummaryNode(), true, model) and + c = nodeFrom.(Node::FlowSummaryNode).getSummarizedCallable() + ) + or + FlowSummaryImpl::Private::Steps::sourceLocalStep(nodeFrom + .(Node::FlowSummaryNode) + .getSummaryNode(), nodeTo, model) + or + FlowSummaryImpl::Private::Steps::sinkLocalStep(nodeFrom, + nodeTo.(Node::FlowSummaryNode).getSummaryNode(), model) } pragma[nomagic] @@ -848,7 +886,7 @@ module RustDataFlow implements InputSig { predicate nodeIsHidden(Node node) { node instanceof Node::SsaNode or - node instanceof Node::FlowSummaryNode + node.(Node::FlowSummaryNode).getSummaryNode().isHidden() or node instanceof Node::CaptureNode or @@ -864,6 +902,10 @@ module RustDataFlow implements InputSig { node.asExpr() = match.getScrutinee() or node.asExpr() = match.getArmPat(_) ) + or + FlowSummaryImpl::Private::Steps::sourceLocalStep(_, node, _) + or + FlowSummaryImpl::Private::Steps::sinkLocalStep(node, _, _) } class DataFlowExpr = ExprCfgNode; @@ -944,7 +986,7 @@ module RustDataFlow implements InputSig { ) and model = "" or - LocalFlow::flowSummaryLocalStep(nodeFrom, nodeTo, _, model) + LocalFlow::flowSummaryLocalStep(nodeFrom, nodeTo, model) } /** @@ -1499,6 +1541,14 @@ private module Cached { cached newtype TContentSet = TSingletonContentSet(Content c) + + /** Holds if `n` is a flow source of kind `kind`. */ + cached + predicate sourceNode(Node n, string kind) { n.(Node::FlowSummaryNode).isSource(kind) } + + /** Holds if `n` is a flow sink of kind `kind`. */ + cached + predicate sinkNode(Node n, string kind) { n.(Node::FlowSummaryNode).isSink(kind) } } import Cached diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll index 492764b3cf54..cfa2cf416fb0 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll @@ -9,8 +9,48 @@ private import codeql.rust.dataflow.internal.DataFlowImpl private import codeql.rust.dataflow.FlowSummary module Input implements InputSig { + private import codeql.rust.elements.internal.CallExprBaseImpl::Impl as CallExprBaseImpl + class SummarizedCallableBase = string; + abstract private class SourceSinkBase extends AstNode { + /** Gets the associated call. */ + abstract CallExprBase getCall(); + + /** Holds if the associated call resolves to `crate, path`. */ + final predicate callResolvesTo(string crate, string path) { + exists(Resolvable r | + r = CallExprBaseImpl::getCallResolvable(this.getCall()) and + path = r.getResolvedPath() + | + crate = r.getResolvedCrateOrigin() + or + not r.hasResolvedCrateOrigin() and + crate = "" + ) + } + } + + abstract class SourceBase extends SourceSinkBase { } + + abstract class SinkBase extends SourceSinkBase { } + + private class CallExprFunction extends SourceBase, SinkBase { + private CallExpr call; + + CallExprFunction() { this = call.getFunction() } + + override CallExpr getCall() { result = call } + } + + private class MethodCallExprNameRef extends SourceBase, SinkBase { + private MethodCallExpr call; + + MethodCallExprNameRef() { this = call.getNameRef() } + + override MethodCallExpr getCall() { result = call } + } + RustDataFlow::ArgumentPosition callbackSelfParameterPosition() { none() } ReturnKind getStandardReturnValueKind() { result = TNormalReturnKind() } @@ -86,12 +126,32 @@ private module StepsInput implements Impl::Private::StepsInputSig { DataFlowCall getACall(Public::SummarizedCallable sc) { result.asCallBaseExprCfgNode().getCallExprBase() = sc.(LibraryCallable).getACall() } + + Node getSourceNode(Input::SourceBase source, Impl::Private::SummaryComponent sc) { + sc = Impl::Private::SummaryComponent::return(_) and + result.asExpr().getExpr() = source.getCall() + } + + Node getSinkNode(Input::SinkBase sink, Impl::Private::SummaryComponent sc) { + exists(CallExprBase call, Expr arg, ParameterPosition pos | + result.asExpr().getExpr() = arg and + sc = Impl::Private::SummaryComponent::argument(pos) and + call = sink.getCall() + | + arg = call.getArgList().getArg(pos.getPosition()) + or + arg = call.(MethodCallExpr).getReceiver() and pos.isSelf() + ) + } } module Private { import Impl::Private module Steps = Impl::Private::Steps; + + private import codeql.rust.dataflow.FlowSource + private import codeql.rust.dataflow.FlowSink } module Public = Impl::Public; diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll b/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll index 244cd6f300f0..47b6f2ef9acc 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll @@ -47,6 +47,8 @@ private import rust private import codeql.rust.dataflow.FlowSummary +private import codeql.rust.dataflow.FlowSource +private import codeql.rust.dataflow.FlowSink /** * Holds if in a call to the function with canonical path `path`, defined in the @@ -138,3 +140,37 @@ private class SummarizedCallableFromModel extends SummarizedCallable::Range { ) } } + +private class FlowSourceFromModel extends FlowSource::Range { + private string crate; + private string path; + + FlowSourceFromModel() { + sourceModel(crate, path, _, _, _, _) and + this.callResolvesTo(crate, path) + } + + override predicate isSource(string output, string kind, Provenance provenance, string model) { + exists(QlBuiltins::ExtensionId madId | + sourceModel(crate, path, output, kind, provenance, madId) and + model = "MaD:" + madId.toString() + ) + } +} + +private class FlowSinkFromModel extends FlowSink::Range { + private string crate; + private string path; + + FlowSinkFromModel() { + sinkModel(crate, path, _, _, _, _) and + this.callResolvesTo(crate, path) + } + + override predicate isSink(string input, string kind, Provenance provenance, string model) { + exists(QlBuiltins::ExtensionId madId | + sinkModel(crate, path, input, kind, provenance, madId) and + model = "MaD:" + madId.toString() + ) + } +} diff --git a/rust/ql/lib/utils/test/InlineFlowTest.qll b/rust/ql/lib/utils/test/InlineFlowTest.qll index cb5b9f72abb2..56a5a2203db4 100644 --- a/rust/ql/lib/utils/test/InlineFlowTest.qll +++ b/rust/ql/lib/utils/test/InlineFlowTest.qll @@ -27,6 +27,12 @@ private module FlowTestImpl implements InputSig { private string getSourceArgString(DataFlow::Node src) { defaultSource(src) and result = src.asExpr().(CallExprCfgNode).getArgument(0).toString() + or + sourceNode(src, _) and + exists(CallExprBase call | + call = src.(Node::FlowSummaryNode).getSourceNode().getCall() and + result = call.getArgList().getArg(0).toString() + ) } bindingset[src, sink] diff --git a/rust/ql/test/library-tests/dataflow/models/main.rs b/rust/ql/test/library-tests/dataflow/models/main.rs index dbff546732a5..b69d8e49d3aa 100644 --- a/rust/ql/test/library-tests/dataflow/models/main.rs +++ b/rust/ql/test/library-tests/dataflow/models/main.rs @@ -175,6 +175,53 @@ fn test_set_tuple_element() { sink(t.1); // $ hasValueFlow=11 } +impl MyFieldEnum { + // has a source model + fn source(&self, i: i64) -> MyFieldEnum { + MyFieldEnum::C { field_c: 0 } + } + + // has a sink model + fn sink(self) {} +} + +// has a source model +fn enum_source(i: i64) -> MyFieldEnum { + MyFieldEnum::C { field_c: 0 } +} + +fn test_enum_source() { + let s = enum_source(12); + match s { + MyFieldEnum::C { field_c: i } => sink(i), + MyFieldEnum::D { field_d: i } => sink(i), // $ hasValueFlow=12 + } +} + +fn test_enum_method_source() { + let e = MyFieldEnum::D { field_d: 0 }; + let s = e.source(13); + match s { + MyFieldEnum::C { field_c: i } => sink(i), // $ hasValueFlow=13 + MyFieldEnum::D { field_d: i } => sink(i), + } +} + +// has a sink model +fn enum_sink(e: MyFieldEnum) {} + +fn test_enum_sink() { + let s = source(14); + enum_sink(MyFieldEnum::C { field_c: s }); // $ hasValueFlow=14 + enum_sink(MyFieldEnum::D { field_d: s }); +} + +fn test_enum_method_sink() { + let s = source(15); + let e = MyFieldEnum::D { field_d: s }; + e.sink(); // $ hasValueFlow=15 +} + fn main() { test_identify(); test_get_var_pos(); @@ -187,5 +234,9 @@ fn main() { test_set_array_element(); test_get_tuple_element(); test_set_tuple_element(); + test_enum_source(); + test_enum_method_source(); + test_enum_sink(); + test_enum_method_sink(); let dummy = Some(0); // ensure that the the `lang:core` crate is extracted } diff --git a/rust/ql/test/library-tests/dataflow/models/models.expected b/rust/ql/test/library-tests/dataflow/models/models.expected index bda991fd7f5d..12ece1b5c9ad 100644 --- a/rust/ql/test/library-tests/dataflow/models/models.expected +++ b/rust/ql/test/library-tests/dataflow/models/models.expected @@ -1,14 +1,18 @@ models -| 1 | Summary: repo::test; crate::coerce; Argument[0]; ReturnValue; taint | -| 2 | Summary: repo::test; crate::get_array_element; Argument[0].ArrayElement; ReturnValue; value | -| 3 | Summary: repo::test; crate::get_struct_field; Argument[0].Struct[crate::MyStruct::field1]; ReturnValue; value | -| 4 | Summary: repo::test; crate::get_tuple_element; Argument[0].Tuple[0]; ReturnValue; value | -| 5 | Summary: repo::test; crate::get_var_field; Argument[0].Variant[crate::MyFieldEnum::C::field_c]; ReturnValue; value | -| 6 | Summary: repo::test; crate::get_var_pos; Argument[0].Variant[crate::MyPosEnum::A(0)]; ReturnValue; value | -| 7 | Summary: repo::test; crate::set_array_element; Argument[0]; ReturnValue.ArrayElement; value | -| 8 | Summary: repo::test; crate::set_tuple_element; Argument[0]; ReturnValue.Tuple[1]; value | -| 9 | Summary: repo::test; crate::set_var_field; Argument[0]; ReturnValue.Variant[crate::MyFieldEnum::D::field_d]; value | -| 10 | Summary: repo::test; crate::set_var_pos; Argument[0]; ReturnValue.Variant[crate::MyPosEnum::B(0)]; value | +| 1 | Sink: repo::test; ::sink; test-sink; Argument[self].Variant[crate::MyFieldEnum::D::field_d] | +| 2 | Sink: repo::test; crate::enum_sink; test-sink; Argument[0].Variant[crate::MyFieldEnum::C::field_c] | +| 3 | Source: repo::test; ::source; test-source; ReturnValue.Variant[crate::MyFieldEnum::C::field_c] | +| 4 | Source: repo::test; crate::enum_source; test-source; ReturnValue.Variant[crate::MyFieldEnum::D::field_d] | +| 5 | Summary: repo::test; crate::coerce; Argument[0]; ReturnValue; taint | +| 6 | Summary: repo::test; crate::get_array_element; Argument[0].ArrayElement; ReturnValue; value | +| 7 | Summary: repo::test; crate::get_struct_field; Argument[0].Struct[crate::MyStruct::field1]; ReturnValue; value | +| 8 | Summary: repo::test; crate::get_tuple_element; Argument[0].Tuple[0]; ReturnValue; value | +| 9 | Summary: repo::test; crate::get_var_field; Argument[0].Variant[crate::MyFieldEnum::C::field_c]; ReturnValue; value | +| 10 | Summary: repo::test; crate::get_var_pos; Argument[0].Variant[crate::MyPosEnum::A(0)]; ReturnValue; value | +| 11 | Summary: repo::test; crate::set_array_element; Argument[0]; ReturnValue.ArrayElement; value | +| 12 | Summary: repo::test; crate::set_tuple_element; Argument[0]; ReturnValue.Tuple[1]; value | +| 13 | Summary: repo::test; crate::set_var_field; Argument[0]; ReturnValue.Variant[crate::MyFieldEnum::D::field_d]; value | +| 14 | Summary: repo::test; crate::set_var_pos; Argument[0]; ReturnValue.Variant[crate::MyPosEnum::B(0)]; value | edges | main.rs:15:9:15:9 | s | main.rs:16:19:16:19 | s | provenance | | | main.rs:15:9:15:9 | s | main.rs:16:19:16:19 | s | provenance | | @@ -18,7 +22,7 @@ edges | main.rs:16:19:16:19 | s | main.rs:16:10:16:20 | identity(...) | provenance | QL | | main.rs:25:9:25:9 | s | main.rs:26:17:26:17 | s | provenance | | | main.rs:25:13:25:22 | source(...) | main.rs:25:9:25:9 | s | provenance | | -| main.rs:26:17:26:17 | s | main.rs:26:10:26:18 | coerce(...) | provenance | MaD:1 | +| main.rs:26:17:26:17 | s | main.rs:26:10:26:18 | coerce(...) | provenance | MaD:5 | | main.rs:40:9:40:9 | s | main.rs:41:27:41:27 | s | provenance | | | main.rs:40:9:40:9 | s | main.rs:41:27:41:27 | s | provenance | | | main.rs:40:13:40:21 | source(...) | main.rs:40:9:40:9 | s | provenance | | @@ -29,8 +33,8 @@ edges | main.rs:41:14:41:28 | ...::A(...) [A] | main.rs:41:9:41:10 | e1 [A] | provenance | | | main.rs:41:27:41:27 | s | main.rs:41:14:41:28 | ...::A(...) [A] | provenance | | | main.rs:41:27:41:27 | s | main.rs:41:14:41:28 | ...::A(...) [A] | provenance | | -| main.rs:42:22:42:23 | e1 [A] | main.rs:42:10:42:24 | get_var_pos(...) | provenance | MaD:6 | -| main.rs:42:22:42:23 | e1 [A] | main.rs:42:10:42:24 | get_var_pos(...) | provenance | MaD:6 | +| main.rs:42:22:42:23 | e1 [A] | main.rs:42:10:42:24 | get_var_pos(...) | provenance | MaD:10 | +| main.rs:42:22:42:23 | e1 [A] | main.rs:42:10:42:24 | get_var_pos(...) | provenance | MaD:10 | | main.rs:53:9:53:9 | s | main.rs:54:26:54:26 | s | provenance | | | main.rs:53:9:53:9 | s | main.rs:54:26:54:26 | s | provenance | | | main.rs:53:13:53:21 | source(...) | main.rs:53:9:53:9 | s | provenance | | @@ -39,8 +43,8 @@ edges | main.rs:54:9:54:10 | e1 [B] | main.rs:55:11:55:12 | e1 [B] | provenance | | | main.rs:54:14:54:27 | set_var_pos(...) [B] | main.rs:54:9:54:10 | e1 [B] | provenance | | | main.rs:54:14:54:27 | set_var_pos(...) [B] | main.rs:54:9:54:10 | e1 [B] | provenance | | -| main.rs:54:26:54:26 | s | main.rs:54:14:54:27 | set_var_pos(...) [B] | provenance | MaD:10 | -| main.rs:54:26:54:26 | s | main.rs:54:14:54:27 | set_var_pos(...) [B] | provenance | MaD:10 | +| main.rs:54:26:54:26 | s | main.rs:54:14:54:27 | set_var_pos(...) [B] | provenance | MaD:14 | +| main.rs:54:26:54:26 | s | main.rs:54:14:54:27 | set_var_pos(...) [B] | provenance | MaD:14 | | main.rs:55:11:55:12 | e1 [B] | main.rs:57:9:57:23 | ...::B(...) [B] | provenance | | | main.rs:55:11:55:12 | e1 [B] | main.rs:57:9:57:23 | ...::B(...) [B] | provenance | | | main.rs:57:9:57:23 | ...::B(...) [B] | main.rs:57:22:57:22 | i | provenance | | @@ -57,8 +61,8 @@ edges | main.rs:73:14:73:42 | ...::C {...} [C] | main.rs:73:9:73:10 | e1 [C] | provenance | | | main.rs:73:40:73:40 | s | main.rs:73:14:73:42 | ...::C {...} [C] | provenance | | | main.rs:73:40:73:40 | s | main.rs:73:14:73:42 | ...::C {...} [C] | provenance | | -| main.rs:74:24:74:25 | e1 [C] | main.rs:74:10:74:26 | get_var_field(...) | provenance | MaD:5 | -| main.rs:74:24:74:25 | e1 [C] | main.rs:74:10:74:26 | get_var_field(...) | provenance | MaD:5 | +| main.rs:74:24:74:25 | e1 [C] | main.rs:74:10:74:26 | get_var_field(...) | provenance | MaD:9 | +| main.rs:74:24:74:25 | e1 [C] | main.rs:74:10:74:26 | get_var_field(...) | provenance | MaD:9 | | main.rs:85:9:85:9 | s | main.rs:86:28:86:28 | s | provenance | | | main.rs:85:9:85:9 | s | main.rs:86:28:86:28 | s | provenance | | | main.rs:85:13:85:21 | source(...) | main.rs:85:9:85:9 | s | provenance | | @@ -67,8 +71,8 @@ edges | main.rs:86:9:86:10 | e1 [D] | main.rs:87:11:87:12 | e1 [D] | provenance | | | main.rs:86:14:86:29 | set_var_field(...) [D] | main.rs:86:9:86:10 | e1 [D] | provenance | | | main.rs:86:14:86:29 | set_var_field(...) [D] | main.rs:86:9:86:10 | e1 [D] | provenance | | -| main.rs:86:28:86:28 | s | main.rs:86:14:86:29 | set_var_field(...) [D] | provenance | MaD:9 | -| main.rs:86:28:86:28 | s | main.rs:86:14:86:29 | set_var_field(...) [D] | provenance | MaD:9 | +| main.rs:86:28:86:28 | s | main.rs:86:14:86:29 | set_var_field(...) [D] | provenance | MaD:13 | +| main.rs:86:28:86:28 | s | main.rs:86:14:86:29 | set_var_field(...) [D] | provenance | MaD:13 | | main.rs:87:11:87:12 | e1 [D] | main.rs:89:9:89:37 | ...::D {...} [D] | provenance | | | main.rs:87:11:87:12 | e1 [D] | main.rs:89:9:89:37 | ...::D {...} [D] | provenance | | | main.rs:89:9:89:37 | ...::D {...} [D] | main.rs:89:35:89:35 | i | provenance | | @@ -85,14 +89,14 @@ edges | main.rs:105:21:108:5 | MyStruct {...} [MyStruct.field1] | main.rs:105:9:105:17 | my_struct [MyStruct.field1] | provenance | | | main.rs:106:17:106:17 | s | main.rs:105:21:108:5 | MyStruct {...} [MyStruct.field1] | provenance | | | main.rs:106:17:106:17 | s | main.rs:105:21:108:5 | MyStruct {...} [MyStruct.field1] | provenance | | -| main.rs:109:27:109:35 | my_struct [MyStruct.field1] | main.rs:109:10:109:36 | get_struct_field(...) | provenance | MaD:3 | -| main.rs:109:27:109:35 | my_struct [MyStruct.field1] | main.rs:109:10:109:36 | get_struct_field(...) | provenance | MaD:3 | +| main.rs:109:27:109:35 | my_struct [MyStruct.field1] | main.rs:109:10:109:36 | get_struct_field(...) | provenance | MaD:7 | +| main.rs:109:27:109:35 | my_struct [MyStruct.field1] | main.rs:109:10:109:36 | get_struct_field(...) | provenance | MaD:7 | | main.rs:138:9:138:9 | s | main.rs:139:29:139:29 | s | provenance | | | main.rs:138:9:138:9 | s | main.rs:139:29:139:29 | s | provenance | | | main.rs:138:13:138:21 | source(...) | main.rs:138:9:138:9 | s | provenance | | | main.rs:138:13:138:21 | source(...) | main.rs:138:9:138:9 | s | provenance | | -| main.rs:139:28:139:30 | [...] [array[]] | main.rs:139:10:139:31 | get_array_element(...) | provenance | MaD:2 | -| main.rs:139:28:139:30 | [...] [array[]] | main.rs:139:10:139:31 | get_array_element(...) | provenance | MaD:2 | +| main.rs:139:28:139:30 | [...] [array[]] | main.rs:139:10:139:31 | get_array_element(...) | provenance | MaD:6 | +| main.rs:139:28:139:30 | [...] [array[]] | main.rs:139:10:139:31 | get_array_element(...) | provenance | MaD:6 | | main.rs:139:29:139:29 | s | main.rs:139:28:139:30 | [...] [array[]] | provenance | | | main.rs:139:29:139:29 | s | main.rs:139:28:139:30 | [...] [array[]] | provenance | | | main.rs:148:9:148:9 | s | main.rs:149:33:149:33 | s | provenance | | @@ -103,8 +107,8 @@ edges | main.rs:149:9:149:11 | arr [array[]] | main.rs:150:10:150:12 | arr [array[]] | provenance | | | main.rs:149:15:149:34 | set_array_element(...) [array[]] | main.rs:149:9:149:11 | arr [array[]] | provenance | | | main.rs:149:15:149:34 | set_array_element(...) [array[]] | main.rs:149:9:149:11 | arr [array[]] | provenance | | -| main.rs:149:33:149:33 | s | main.rs:149:15:149:34 | set_array_element(...) [array[]] | provenance | MaD:7 | -| main.rs:149:33:149:33 | s | main.rs:149:15:149:34 | set_array_element(...) [array[]] | provenance | MaD:7 | +| main.rs:149:33:149:33 | s | main.rs:149:15:149:34 | set_array_element(...) [array[]] | provenance | MaD:11 | +| main.rs:149:33:149:33 | s | main.rs:149:15:149:34 | set_array_element(...) [array[]] | provenance | MaD:11 | | main.rs:150:10:150:12 | arr [array[]] | main.rs:150:10:150:15 | arr[0] | provenance | | | main.rs:150:10:150:12 | arr [array[]] | main.rs:150:10:150:15 | arr[0] | provenance | | | main.rs:159:9:159:9 | s | main.rs:160:14:160:14 | s | provenance | | @@ -117,8 +121,8 @@ edges | main.rs:160:13:160:18 | TupleExpr [tuple.0] | main.rs:160:9:160:9 | t [tuple.0] | provenance | | | main.rs:160:14:160:14 | s | main.rs:160:13:160:18 | TupleExpr [tuple.0] | provenance | | | main.rs:160:14:160:14 | s | main.rs:160:13:160:18 | TupleExpr [tuple.0] | provenance | | -| main.rs:161:28:161:28 | t [tuple.0] | main.rs:161:10:161:29 | get_tuple_element(...) | provenance | MaD:4 | -| main.rs:161:28:161:28 | t [tuple.0] | main.rs:161:10:161:29 | get_tuple_element(...) | provenance | MaD:4 | +| main.rs:161:28:161:28 | t [tuple.0] | main.rs:161:10:161:29 | get_tuple_element(...) | provenance | MaD:8 | +| main.rs:161:28:161:28 | t [tuple.0] | main.rs:161:10:161:29 | get_tuple_element(...) | provenance | MaD:8 | | main.rs:172:9:172:9 | s | main.rs:173:31:173:31 | s | provenance | | | main.rs:172:9:172:9 | s | main.rs:173:31:173:31 | s | provenance | | | main.rs:172:13:172:22 | source(...) | main.rs:172:9:172:9 | s | provenance | | @@ -127,10 +131,54 @@ edges | main.rs:173:9:173:9 | t [tuple.1] | main.rs:175:10:175:10 | t [tuple.1] | provenance | | | main.rs:173:13:173:32 | set_tuple_element(...) [tuple.1] | main.rs:173:9:173:9 | t [tuple.1] | provenance | | | main.rs:173:13:173:32 | set_tuple_element(...) [tuple.1] | main.rs:173:9:173:9 | t [tuple.1] | provenance | | -| main.rs:173:31:173:31 | s | main.rs:173:13:173:32 | set_tuple_element(...) [tuple.1] | provenance | MaD:8 | -| main.rs:173:31:173:31 | s | main.rs:173:13:173:32 | set_tuple_element(...) [tuple.1] | provenance | MaD:8 | +| main.rs:173:31:173:31 | s | main.rs:173:13:173:32 | set_tuple_element(...) [tuple.1] | provenance | MaD:12 | +| main.rs:173:31:173:31 | s | main.rs:173:13:173:32 | set_tuple_element(...) [tuple.1] | provenance | MaD:12 | | main.rs:175:10:175:10 | t [tuple.1] | main.rs:175:10:175:12 | t.1 | provenance | | | main.rs:175:10:175:10 | t [tuple.1] | main.rs:175:10:175:12 | t.1 | provenance | | +| main.rs:194:9:194:9 | s [D] | main.rs:195:11:195:11 | s [D] | provenance | | +| main.rs:194:9:194:9 | s [D] | main.rs:195:11:195:11 | s [D] | provenance | | +| main.rs:194:13:194:23 | enum_source | main.rs:194:13:194:27 | enum_source(...) [D] | provenance | MaD:4 | +| main.rs:194:13:194:23 | enum_source | main.rs:194:13:194:27 | enum_source(...) [D] | provenance | MaD:4 | +| main.rs:194:13:194:27 | enum_source(...) [D] | main.rs:194:9:194:9 | s [D] | provenance | | +| main.rs:194:13:194:27 | enum_source(...) [D] | main.rs:194:9:194:9 | s [D] | provenance | | +| main.rs:195:11:195:11 | s [D] | main.rs:197:9:197:37 | ...::D {...} [D] | provenance | | +| main.rs:195:11:195:11 | s [D] | main.rs:197:9:197:37 | ...::D {...} [D] | provenance | | +| main.rs:197:9:197:37 | ...::D {...} [D] | main.rs:197:35:197:35 | i | provenance | | +| main.rs:197:9:197:37 | ...::D {...} [D] | main.rs:197:35:197:35 | i | provenance | | +| main.rs:197:35:197:35 | i | main.rs:197:47:197:47 | i | provenance | | +| main.rs:197:35:197:35 | i | main.rs:197:47:197:47 | i | provenance | | +| main.rs:203:9:203:9 | s [C] | main.rs:204:11:204:11 | s [C] | provenance | | +| main.rs:203:9:203:9 | s [C] | main.rs:204:11:204:11 | s [C] | provenance | | +| main.rs:203:13:203:24 | e.source(...) [C] | main.rs:203:9:203:9 | s [C] | provenance | | +| main.rs:203:13:203:24 | e.source(...) [C] | main.rs:203:9:203:9 | s [C] | provenance | | +| main.rs:203:15:203:20 | source | main.rs:203:13:203:24 | e.source(...) [C] | provenance | MaD:3 | +| main.rs:203:15:203:20 | source | main.rs:203:13:203:24 | e.source(...) [C] | provenance | MaD:3 | +| main.rs:204:11:204:11 | s [C] | main.rs:205:9:205:37 | ...::C {...} [C] | provenance | | +| main.rs:204:11:204:11 | s [C] | main.rs:205:9:205:37 | ...::C {...} [C] | provenance | | +| main.rs:205:9:205:37 | ...::C {...} [C] | main.rs:205:35:205:35 | i | provenance | | +| main.rs:205:9:205:37 | ...::C {...} [C] | main.rs:205:35:205:35 | i | provenance | | +| main.rs:205:35:205:35 | i | main.rs:205:47:205:47 | i | provenance | | +| main.rs:205:35:205:35 | i | main.rs:205:47:205:47 | i | provenance | | +| main.rs:214:9:214:9 | s | main.rs:215:41:215:41 | s | provenance | | +| main.rs:214:9:214:9 | s | main.rs:215:41:215:41 | s | provenance | | +| main.rs:214:13:214:22 | source(...) | main.rs:214:9:214:9 | s | provenance | | +| main.rs:214:13:214:22 | source(...) | main.rs:214:9:214:9 | s | provenance | | +| main.rs:215:15:215:43 | ...::C {...} [C] | main.rs:215:5:215:13 | enum_sink | provenance | MaD:2 | +| main.rs:215:15:215:43 | ...::C {...} [C] | main.rs:215:5:215:13 | enum_sink | provenance | MaD:2 | +| main.rs:215:41:215:41 | s | main.rs:215:15:215:43 | ...::C {...} [C] | provenance | | +| main.rs:215:41:215:41 | s | main.rs:215:15:215:43 | ...::C {...} [C] | provenance | | +| main.rs:220:9:220:9 | s | main.rs:221:39:221:39 | s | provenance | | +| main.rs:220:9:220:9 | s | main.rs:221:39:221:39 | s | provenance | | +| main.rs:220:13:220:22 | source(...) | main.rs:220:9:220:9 | s | provenance | | +| main.rs:220:13:220:22 | source(...) | main.rs:220:9:220:9 | s | provenance | | +| main.rs:221:9:221:9 | e [D] | main.rs:222:5:222:5 | e [D] | provenance | | +| main.rs:221:9:221:9 | e [D] | main.rs:222:5:222:5 | e [D] | provenance | | +| main.rs:221:13:221:41 | ...::D {...} [D] | main.rs:221:9:221:9 | e [D] | provenance | | +| main.rs:221:13:221:41 | ...::D {...} [D] | main.rs:221:9:221:9 | e [D] | provenance | | +| main.rs:221:39:221:39 | s | main.rs:221:13:221:41 | ...::D {...} [D] | provenance | | +| main.rs:221:39:221:39 | s | main.rs:221:13:221:41 | ...::D {...} [D] | provenance | | +| main.rs:222:5:222:5 | e [D] | main.rs:222:7:222:10 | sink | provenance | MaD:1 | +| main.rs:222:5:222:5 | e [D] | main.rs:222:7:222:10 | sink | provenance | MaD:1 | nodes | main.rs:15:9:15:9 | s | semmle.label | s | | main.rs:15:9:15:9 | s | semmle.label | s | @@ -274,6 +322,58 @@ nodes | main.rs:175:10:175:10 | t [tuple.1] | semmle.label | t [tuple.1] | | main.rs:175:10:175:12 | t.1 | semmle.label | t.1 | | main.rs:175:10:175:12 | t.1 | semmle.label | t.1 | +| main.rs:194:9:194:9 | s [D] | semmle.label | s [D] | +| main.rs:194:9:194:9 | s [D] | semmle.label | s [D] | +| main.rs:194:13:194:23 | enum_source | semmle.label | enum_source | +| main.rs:194:13:194:23 | enum_source | semmle.label | enum_source | +| main.rs:194:13:194:27 | enum_source(...) [D] | semmle.label | enum_source(...) [D] | +| main.rs:194:13:194:27 | enum_source(...) [D] | semmle.label | enum_source(...) [D] | +| main.rs:195:11:195:11 | s [D] | semmle.label | s [D] | +| main.rs:195:11:195:11 | s [D] | semmle.label | s [D] | +| main.rs:197:9:197:37 | ...::D {...} [D] | semmle.label | ...::D {...} [D] | +| main.rs:197:9:197:37 | ...::D {...} [D] | semmle.label | ...::D {...} [D] | +| main.rs:197:35:197:35 | i | semmle.label | i | +| main.rs:197:35:197:35 | i | semmle.label | i | +| main.rs:197:47:197:47 | i | semmle.label | i | +| main.rs:197:47:197:47 | i | semmle.label | i | +| main.rs:203:9:203:9 | s [C] | semmle.label | s [C] | +| main.rs:203:9:203:9 | s [C] | semmle.label | s [C] | +| main.rs:203:13:203:24 | e.source(...) [C] | semmle.label | e.source(...) [C] | +| main.rs:203:13:203:24 | e.source(...) [C] | semmle.label | e.source(...) [C] | +| main.rs:203:15:203:20 | source | semmle.label | source | +| main.rs:203:15:203:20 | source | semmle.label | source | +| main.rs:204:11:204:11 | s [C] | semmle.label | s [C] | +| main.rs:204:11:204:11 | s [C] | semmle.label | s [C] | +| main.rs:205:9:205:37 | ...::C {...} [C] | semmle.label | ...::C {...} [C] | +| main.rs:205:9:205:37 | ...::C {...} [C] | semmle.label | ...::C {...} [C] | +| main.rs:205:35:205:35 | i | semmle.label | i | +| main.rs:205:35:205:35 | i | semmle.label | i | +| main.rs:205:47:205:47 | i | semmle.label | i | +| main.rs:205:47:205:47 | i | semmle.label | i | +| main.rs:214:9:214:9 | s | semmle.label | s | +| main.rs:214:9:214:9 | s | semmle.label | s | +| main.rs:214:13:214:22 | source(...) | semmle.label | source(...) | +| main.rs:214:13:214:22 | source(...) | semmle.label | source(...) | +| main.rs:215:5:215:13 | enum_sink | semmle.label | enum_sink | +| main.rs:215:5:215:13 | enum_sink | semmle.label | enum_sink | +| main.rs:215:15:215:43 | ...::C {...} [C] | semmle.label | ...::C {...} [C] | +| main.rs:215:15:215:43 | ...::C {...} [C] | semmle.label | ...::C {...} [C] | +| main.rs:215:41:215:41 | s | semmle.label | s | +| main.rs:215:41:215:41 | s | semmle.label | s | +| main.rs:220:9:220:9 | s | semmle.label | s | +| main.rs:220:9:220:9 | s | semmle.label | s | +| main.rs:220:13:220:22 | source(...) | semmle.label | source(...) | +| main.rs:220:13:220:22 | source(...) | semmle.label | source(...) | +| main.rs:221:9:221:9 | e [D] | semmle.label | e [D] | +| main.rs:221:9:221:9 | e [D] | semmle.label | e [D] | +| main.rs:221:13:221:41 | ...::D {...} [D] | semmle.label | ...::D {...} [D] | +| main.rs:221:13:221:41 | ...::D {...} [D] | semmle.label | ...::D {...} [D] | +| main.rs:221:39:221:39 | s | semmle.label | s | +| main.rs:221:39:221:39 | s | semmle.label | s | +| main.rs:222:5:222:5 | e [D] | semmle.label | e [D] | +| main.rs:222:5:222:5 | e [D] | semmle.label | e [D] | +| main.rs:222:7:222:10 | sink | semmle.label | sink | +| main.rs:222:7:222:10 | sink | semmle.label | sink | subpaths testFailures invalidSpecComponent @@ -299,3 +399,11 @@ invalidSpecComponent | main.rs:161:10:161:29 | get_tuple_element(...) | main.rs:159:13:159:22 | source(...) | main.rs:161:10:161:29 | get_tuple_element(...) | $@ | main.rs:159:13:159:22 | source(...) | source(...) | | main.rs:175:10:175:12 | t.1 | main.rs:172:13:172:22 | source(...) | main.rs:175:10:175:12 | t.1 | $@ | main.rs:172:13:172:22 | source(...) | source(...) | | main.rs:175:10:175:12 | t.1 | main.rs:172:13:172:22 | source(...) | main.rs:175:10:175:12 | t.1 | $@ | main.rs:172:13:172:22 | source(...) | source(...) | +| main.rs:197:47:197:47 | i | main.rs:194:13:194:23 | enum_source | main.rs:197:47:197:47 | i | $@ | main.rs:194:13:194:23 | enum_source | enum_source | +| main.rs:197:47:197:47 | i | main.rs:194:13:194:23 | enum_source | main.rs:197:47:197:47 | i | $@ | main.rs:194:13:194:23 | enum_source | enum_source | +| main.rs:205:47:205:47 | i | main.rs:203:15:203:20 | source | main.rs:205:47:205:47 | i | $@ | main.rs:203:15:203:20 | source | source | +| main.rs:205:47:205:47 | i | main.rs:203:15:203:20 | source | main.rs:205:47:205:47 | i | $@ | main.rs:203:15:203:20 | source | source | +| main.rs:215:5:215:13 | enum_sink | main.rs:214:13:214:22 | source(...) | main.rs:215:5:215:13 | enum_sink | $@ | main.rs:214:13:214:22 | source(...) | source(...) | +| main.rs:215:5:215:13 | enum_sink | main.rs:214:13:214:22 | source(...) | main.rs:215:5:215:13 | enum_sink | $@ | main.rs:214:13:214:22 | source(...) | source(...) | +| main.rs:222:7:222:10 | sink | main.rs:220:13:220:22 | source(...) | main.rs:222:7:222:10 | sink | $@ | main.rs:220:13:220:22 | source(...) | source(...) | +| main.rs:222:7:222:10 | sink | main.rs:220:13:220:22 | source(...) | main.rs:222:7:222:10 | sink | $@ | main.rs:220:13:220:22 | source(...) | source(...) | diff --git a/rust/ql/test/library-tests/dataflow/models/models.ext.yml b/rust/ql/test/library-tests/dataflow/models/models.ext.yml index d06fc442e2a4..12cababbf73c 100644 --- a/rust/ql/test/library-tests/dataflow/models/models.ext.yml +++ b/rust/ql/test/library-tests/dataflow/models/models.ext.yml @@ -1,4 +1,16 @@ extensions: + - addsTo: + pack: codeql/rust-all + extensible: sourceModel + data: + - ["repo::test", "crate::enum_source", "ReturnValue.Variant[crate::MyFieldEnum::D::field_d]", "test-source", "manual"] + - ["repo::test", "::source", "ReturnValue.Variant[crate::MyFieldEnum::C::field_c]", "test-source", "manual"] + - addsTo: + pack: codeql/rust-all + extensible: sinkModel + data: + - ["repo::test", "crate::enum_sink", "Argument[0].Variant[crate::MyFieldEnum::C::field_c]", "test-sink", "manual"] + - ["repo::test", "::sink", "Argument[self].Variant[crate::MyFieldEnum::D::field_d]", "test-sink", "manual"] - addsTo: pack: codeql/rust-all extensible: summaryModel diff --git a/rust/ql/test/library-tests/dataflow/models/models.ql b/rust/ql/test/library-tests/dataflow/models/models.ql index f419c266862e..b17b45bba82c 100644 --- a/rust/ql/test/library-tests/dataflow/models/models.ql +++ b/rust/ql/test/library-tests/dataflow/models/models.ql @@ -8,6 +8,8 @@ import codeql.rust.dataflow.DataFlow import codeql.rust.dataflow.FlowSummary import codeql.rust.dataflow.TaintTracking import codeql.rust.dataflow.internal.FlowSummaryImpl +import codeql.rust.dataflow.FlowSource +import codeql.rust.dataflow.FlowSink import PathGraph query predicate invalidSpecComponent(SummarizedCallable sc, string s, string c) { @@ -31,9 +33,17 @@ private class SummarizedCallableIdentity extends SummarizedCallable::Range { } module CustomConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { DefaultFlowConfig::isSource(source) } + predicate isSource(DataFlow::Node source) { + DefaultFlowConfig::isSource(source) + or + sourceNode(source, "test-source") + } - predicate isSink(DataFlow::Node sink) { DefaultFlowConfig::isSink(sink) } + predicate isSink(DataFlow::Node sink) { + DefaultFlowConfig::isSink(sink) + or + sinkNode(sink, "test-sink") + } } import FlowTest diff --git a/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll b/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll index e1770ca482a6..ce99f813e31a 100644 --- a/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll +++ b/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll @@ -19,6 +19,24 @@ signature module InputSig Lang> { string toString(); } + /** + * A base class of elements that are candidates for flow source modeling. + */ + bindingset[this] + class SourceBase { + bindingset[this] + string toString(); + } + + /** + * A base class of elements that are candidates for flow sink modeling. + */ + bindingset[this] + class SinkBase { + bindingset[this] + string toString(); + } + /** * Holds if a neutral (MaD) model exists for `c` of kind `kind` * with provenance `provenance` and `isExact` is true if the model @@ -159,6 +177,10 @@ module Make< final private class SummarizedCallableBaseFinal = SummarizedCallableBase; + final private class SourceBaseFinal = SourceBase; + + final private class SinkBaseFinal = SinkBase; + /** Provides classes and predicates for defining flow summaries. */ module Public { private import Private @@ -273,6 +295,32 @@ module Make< predicate hasExactModel() { none() } } + /** A source node. */ + abstract class SourceNode extends SourceBaseFinal { + bindingset[this] + SourceNode() { any() } + + /** + * Holds if this element is a flow source of kind `kind`, where data + * flows out as described by `output`. + */ + pragma[nomagic] + abstract predicate isSource(string output, string kind, Provenance provenance, string model); + } + + /** A sink node. */ + abstract class SinkNode extends SinkBaseFinal { + bindingset[this] + SinkNode() { any() } + + /** + * Holds if this element is a flow sink of kind `kind`, where data + * flows in as described by `input`. + */ + pragma[nomagic] + abstract predicate isSink(string input, string kind, Provenance provenance, string model); + } + private signature predicate hasKindSig(string kind); signature class NeutralCallableSig extends SummarizedCallableBaseFinal { @@ -488,6 +536,10 @@ module Make< or c.propagatesFlow(_, spec, _, _) ) + or + any(SourceNode s).isSource(spec, _, _, _) + or + any(SinkNode s).isSink(spec, _, _, _) } import AccessPathSyntax::AccessPath @@ -837,9 +889,57 @@ module Make< outputState(c, s) and s = SummaryComponentStack::argument(_) } + pragma[nomagic] + private predicate sourceOutputStateEntry( + SourceNode source, SummaryComponentStack s, string kind, string model + ) { + exists(string outSpec | + source.isSource(outSpec, kind, _, model) and + External::interpretSpec(outSpec, s) + ) + } + + pragma[nomagic] + private predicate sourceOutputState( + SourceNode source, SummaryComponentStack s, string kind, string model + ) { + sourceOutputStateEntry(source, s, kind, model) + or + exists(SummaryComponentStack out | + sourceOutputState(source, out, kind, model) and + out.head() = TContentSummaryComponent(_) and + s = out.tail() + ) + } + + pragma[nomagic] + private predicate sinkInputStateExit( + SinkNode sink, SummaryComponentStack s, string kind, string model + ) { + exists(string inSpec | + sink.isSink(inSpec, kind, _, model) and + External::interpretSpec(inSpec, s) + ) + } + + pragma[nomagic] + private predicate sinkInputState( + SinkNode sink, SummaryComponentStack s, string kind, string model + ) { + sinkInputStateExit(sink, s, kind, model) + or + exists(SummaryComponentStack inp | + sinkInputState(sink, inp, kind, model) and + inp.head() = TContentSummaryComponent(_) and + s = inp.tail() + ) + } + private newtype TSummaryNodeState = TSummaryNodeInputState(SummaryComponentStack s) { inputState(_, s) } or - TSummaryNodeOutputState(SummaryComponentStack s) { outputState(_, s) } + TSummaryNodeOutputState(SummaryComponentStack s) { outputState(_, s) } or + TSourceOutputState(SummaryComponentStack s) { sourceOutputState(_, s, _, _) } or + TSinkInputState(SummaryComponentStack s) { sinkInputState(_, s, _, _) } /** * A state used to break up (complex) flow summaries into atomic flow steps. @@ -875,6 +975,22 @@ module Make< outputState(c, s) } + /** Holds if this state is a valid output state for `source`. */ + pragma[nomagic] + predicate isSourceOutputState( + SourceNode source, SummaryComponentStack s, string kind, string model + ) { + sourceOutputState(source, s, kind, model) and + this = TSourceOutputState(s) + } + + /** Holds if this state is a valid input state for `sink`. */ + pragma[nomagic] + predicate isSinkInputState(SinkNode sink, SummaryComponentStack s, string kind, string model) { + sinkInputState(sink, s, kind, model) and + this = TSinkInputState(s) + } + /** Gets a textual representation of this state. */ string toString() { exists(SummaryComponentStack s | @@ -886,6 +1002,16 @@ module Make< this = TSummaryNodeOutputState(s) and result = "to write: " + s ) + or + exists(SummaryComponentStack s | + this = TSourceOutputState(s) and + result = "to write source: " + s + ) + or + exists(SummaryComponentStack s | + this = TSinkInputState(s) and + result = "read sink: " + s + ) } } @@ -895,12 +1021,24 @@ module Make< } or TSummaryParameterNode(SummarizedCallable c, ParameterPosition pos) { summaryParameterNodeRange(c, pos) + } or + TSourceOutputNode(SourceNode source, SummaryNodeState state, string kind, string model) { + state.isSourceOutputState(source, _, kind, model) + } or + TSinkInputNode(SinkNode sink, SummaryNodeState state, string kind, string model) { + state.isSinkInputState(sink, _, kind, model) } abstract class SummaryNode extends TSummaryNode { abstract string toString(); abstract SummarizedCallable getSummarizedCallable(); + + abstract SourceNode getSourceNode(); + + abstract SinkNode getSinkNode(); + + predicate isHidden() { any() } } private class SummaryInternalNode extends SummaryNode, TSummaryInternalNode { @@ -912,6 +1050,10 @@ module Make< override string toString() { result = "[summary] " + state + " in " + c } override SummarizedCallable getSummarizedCallable() { result = c } + + override SourceNode getSourceNode() { none() } + + override SinkNode getSinkNode() { none() } } private class SummaryParamNode extends SummaryNode, TSummaryParameterNode { @@ -923,6 +1065,105 @@ module Make< override string toString() { result = "[summary param] " + pos + " in " + c } override SummarizedCallable getSummarizedCallable() { result = c } + + override SourceNode getSourceNode() { none() } + + override SinkNode getSinkNode() { none() } + } + + class SourceOutputNode extends SummaryNode, TSourceOutputNode { + private SourceNode source_; + private SummaryNodeState state_; + private string kind_; + private string model_; + + SourceOutputNode() { this = TSourceOutputNode(source_, state_, kind_, model_) } + + /** + * Holds if this node is an entry node, i.e. before any stores have been performed. + * + * This node should be used as the actual source node in data flow configurations. + */ + predicate isEntry(string kind) { + exists(SummaryComponentStack out | + sourceOutputStateEntry(source_, out, kind, model_) and + state_.isSourceOutputState(source_, out, kind, model_) + ) + } + + /** + * Holds if this node is an exit node, i.e. after all stores have been performed. + * + * A local flow step should be added from this node to a data flow node representing + * `sc` inside `source`. + */ + predicate isExit(SourceNode source, SummaryComponent sc, string model) { + source = source_ and + model = model_ and + state_.isSourceOutputState(source, TSingletonSummaryComponentStack(sc), _, model) + } + + override predicate isHidden() { not this.isEntry(_) } + + override string toString() { + if this.isEntry(_) + then result = source_.toString() + else result = "[source] " + state_ + " at " + source_ + } + + override SummarizedCallable getSummarizedCallable() { none() } + + override SourceNode getSourceNode() { result = source_ } + + override SinkNode getSinkNode() { none() } + } + + class SinkInputNode extends SummaryNode, TSinkInputNode { + private SinkNode sink_; + private SummaryNodeState state_; + private string kind_; + private string model_; + + SinkInputNode() { this = TSinkInputNode(sink_, state_, kind_, model_) } + + /** + * Holds if this node is an entry node, i.e. before any reads have been performed. + * + * A local flow step should be added to this node from a data flow node representing + * `sc` inside `sink`. + */ + predicate isEntry(SinkNode sink, SummaryComponent sc, string model) { + sink = sink_ and + model = model_ and + state_.isSinkInputState(sink, TSingletonSummaryComponentStack(sc), _, model) + } + + /** + * Holds if this node is an exit node, i.e. after all reads have been performed. + * + * This node should be used as the actual sink node in data flow configurations. + */ + predicate isExit(string kind) { + kind = kind_ and + exists(SummaryComponentStack inp | + sinkInputStateExit(sink_, inp, kind, model_) and + state_.isSinkInputState(sink_, inp, kind, model_) + ) + } + + override predicate isHidden() { not this.isExit(_) } + + override string toString() { + if this.isExit(_) + then result = sink_.toString() + else result = "[sink] " + state_ + " at " + sink_ + } + + override SummarizedCallable getSummarizedCallable() { none() } + + override SourceNode getSourceNode() { none() } + + override SinkNode getSinkNode() { result = sink_ } } /** @@ -967,6 +1208,22 @@ module Make< ) } + pragma[noinline] + private SummaryNode sourceNodeOutputState(SourceNode source, SummaryComponentStack s) { + exists(SummaryNodeState state, string kind, string model | + state.isSourceOutputState(source, s, kind, model) and + result = TSourceOutputNode(source, state, kind, model) + ) + } + + pragma[noinline] + private SummaryNode sinkNodeInputState(SinkNode sink, SummaryComponentStack s) { + exists(SummaryNodeState state, string kind, string model | + state.isSinkInputState(sink, s, kind, model) and + result = TSinkInputNode(sink, state, kind, model) + ) + } + /** * Holds if a write targets `post`, which is a post-update node for a * parameter at position `pos` in `c`. @@ -1090,6 +1347,10 @@ module Make< DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk); DataFlowType getSyntheticGlobalType(SyntheticGlobal sg); + + DataFlowType getSourceNodeType(SourceBase source, SummaryComponent sc); + + DataFlowType getSinkNodeType(SinkBase sink, SummaryComponent sc); } /** @@ -1168,12 +1429,54 @@ module Make< ) ) ) + or + exists(SourceNode source | + exists(SummaryComponent sc | + n.(SourceOutputNode).isExit(source, sc, _) and + result = getSourceNodeType(source, sc) + ) + or + exists(SummaryComponentStack s, ContentSet cont | + n = sourceNodeOutputState(source, s) and + s.head() = TContentSummaryComponent(cont) and + result = getContentType(cont) + ) + ) + or + exists(SinkNode sink | + exists(SummaryComponent sc | + n.(SinkInputNode).isEntry(sink, sc, _) and + result = getSinkNodeType(sink, sc) + ) + or + exists(SummaryComponentStack s, ContentSet cont | + n = sinkNodeInputState(sink, s) and + s.head() = TContentSummaryComponent(cont) and + result = getContentType(cont) + ) + ) } } signature module StepsInputSig { /** Gets a call that targets summarized callable `sc`. */ DataFlowCall getACall(SummarizedCallable sc); + + /** + * Gets a data flow node corresponding to the `sc` part of `source`. + * + * `sc` is typically `ReturnValue` and the result is the node that + * represents the return value of `source`. + */ + Node getSourceNode(SourceBase source, SummaryComponent sc); + + /** + * Gets a data flow node corresponding to the `sc` part of `sink`. + * + * `sc` is typically `Argument[i]` and the result is the node that + * represents the `i`th argument of `sink`. + */ + Node getSinkNode(SinkBase sink, SummaryComponent sc); } /** Provides a compilation of flow summaries to atomic data-flow steps. */ @@ -1207,6 +1510,20 @@ module Make< ) } + predicate sourceLocalStep(SourceOutputNode nodeFrom, Node nodeTo, string model) { + exists(SummaryComponent sc, SourceNode source | + nodeFrom.isExit(source, sc, model) and + nodeTo = StepsInput::getSourceNode(source, sc) + ) + } + + predicate sinkLocalStep(Node nodeFrom, SinkInputNode nodeTo, string model) { + exists(SummaryComponent sc, SinkNode sink | + nodeFrom = StepsInput::getSinkNode(sink, sc) and + nodeTo.isEntry(sink, sc, model) + ) + } + /** Holds if the value of `succ` is uniquely determined by the value of `pred`. */ predicate summaryLocalMustFlowStep(SummaryNode pred, SummaryNode succ) { pred = unique(SummaryNode n1 | summaryLocalStep(n1, succ, true, _)) @@ -1222,6 +1539,12 @@ module Make< succ = summaryNodeInputState(sc, s) and SummaryComponent::content(c) = s.head() ) + or + exists(SinkNode sink, SummaryComponentStack s | + pred = sinkNodeInputState(sink, s.tail()) and + succ = sinkNodeInputState(sink, s) and + SummaryComponent::content(c) = s.head() + ) } /** @@ -1234,6 +1557,12 @@ module Make< succ = summaryNodeOutputState(sc, s.tail()) and SummaryComponent::content(c) = s.head() ) + or + exists(SourceNode source, SummaryComponentStack s | + pred = sourceNodeOutputState(source, s) and + succ = sourceNodeOutputState(source, s.tail()) and + SummaryComponent::content(c) = s.head() + ) } /** diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImpl.qll b/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImpl.qll index 0898f7b5355e..cb889baaebc2 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImpl.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImpl.qll @@ -12,8 +12,14 @@ private import DataFlowImplCommon private import codeql.swift.dataflow.ExternalFlow module Input implements InputSig { + private import codeql.util.Void + class SummarizedCallableBase = Function; + class SourceBase = Void; + + class SinkBase = Void; + ArgumentPosition callbackSelfParameterPosition() { result instanceof ThisArgumentPosition } ReturnKind getStandardReturnValueKind() { result instanceof NormalReturnKind } @@ -106,6 +112,10 @@ private import Make as Imp private module StepsInput implements Impl::Private::StepsInputSig { DataFlowCall getACall(Public::SummarizedCallable sc) { result.asCall().getStaticTarget() = sc } + + Node getSourceNode(Input::SourceBase source, Impl::Private::SummaryComponent sc) { none() } + + Node getSinkNode(Input::SinkBase sink, Impl::Private::SummaryComponent sc) { none() } } module SourceSinkInterpretationInput implements