Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions rust/ql/lib/codeql/rust/dataflow/FlowBarrier.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Provides classes and predicates for defining barriers and barrier guards.
*
* Flow sinks defined here feed into data flow configurations as follows:
*
* ```text
* data from *.model.yml | QL extensions of FlowSink::Range
* v v
* FlowSink (associated with a models-as-data kind string)
* v
* sinkNode predicate | other QL defined sinks, for example using concepts
* v v
* various Sink classes for specific data flow configurations <- extending QuerySink
* ```
*
* New sinks should be defined using models-as-data, QL extensions of
* `FlowSink::Range`, or concepts. Data flow configurations should use the
* `sinkNode` predicate and/or concepts to define their sinks.
*/

private import rust
private import internal.FlowSummaryImpl as Impl
private import internal.DataFlowImpl as DataFlowImpl

// import all instances below
private module Barriers {
private import codeql.rust.Frameworks
private import codeql.rust.dataflow.internal.ModelsAsData
}

/** Provides the `Range` class used to define the extent of `FlowBarrier`. */
module FlowBarrier {
/** A flow sink. */
abstract class Range extends Impl::Public::BarrierElement {
bindingset[this]
Range() { any() }

override predicate isBarrier(
string output, string kind, Impl::Public::Provenance provenance, string model
) {
this.isBarrier(output, kind) and provenance = "manual" and model = ""
}

/**
* Holds is this element is a flow barrier of kind `kind`, where data
* flows out as described by `output`.
*/
predicate isBarrier(string output, string kind) { none() }
}
}

final class FlowBarrier = FlowBarrier::Range;

predicate barrierNode = DataFlowImpl::barrierNode/2;
19 changes: 19 additions & 0 deletions rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -1157,6 +1157,25 @@ private module Cached {
cached
predicate sinkNode(Node n, string kind) { n.(FlowSummaryNode).isSink(kind, _) }

/** Holds if `n` is a flow barrier of kind `kind`. */
cached
predicate barrierNode(Node n, string kind) {
exists(
FlowSummaryImpl::Public::BarrierElement b,
FlowSummaryImpl::Private::SummaryComponentStack stack
|
FlowSummaryImpl::Private::barrierSpec(b, stack, kind, _)
|
n = FlowSummaryImpl::StepsInput::getSourceNode(b, stack, false)
or
// For barriers like `Argument[0]` we want to target the pre-update node
n =
FlowSummaryImpl::StepsInput::getSourceNode(b, stack, true)
.(PostUpdateNode)
.getPreUpdateNode()
)
}

/**
* A step in a flow summary defined using `OptionalStep[name]`. An `OptionalStep` is "opt-in", which means
* that by default the step is not present in the flow summary and needs to be explicitly enabled by defining
Expand Down
19 changes: 14 additions & 5 deletions rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ module Input implements InputSig<Location, RustDataFlow> {

private import Make<Location, RustDataFlow, Input> as Impl

private module StepsInput implements Impl::Private::StepsInputSig {
module StepsInput implements Impl::Private::StepsInputSig {
DataFlowCall getACall(Public::SummarizedCallable sc) { result.asCall().getStaticTarget() = sc }

/** Gets the argument of `source` described by `sc`, if any. */
Expand Down Expand Up @@ -171,18 +171,27 @@ private module StepsInput implements Impl::Private::StepsInputSig {
result.asCfgScope() = source.getEnclosingCfgScope()
}

RustDataFlow::Node getSourceNode(Input::SourceBase source, Impl::Private::SummaryComponentStack s) {
additional RustDataFlow::Node getSourceNode(
Input::SourceBase source, Impl::Private::SummaryComponentStack s, boolean isArgPostUpdate
) {
s.head() = Impl::Private::SummaryComponent::return(_) and
result.asExpr() = source.getCall()
result.asExpr() = source.getCall() and
isArgPostUpdate = false
or
exists(RustDataFlow::ArgumentPosition pos, Expr arg |
s.head() = Impl::Private::SummaryComponent::parameter(pos) and
arg = getSourceNodeArgument(source, s.tail().headOfSingleton()) and
result.asParameter() = getCallable(arg).getParam(pos.getPosition())
result.asParameter() = getCallable(arg).getParam(pos.getPosition()) and
isArgPostUpdate = false
)
or
result.(RustDataFlow::PostUpdateNode).getPreUpdateNode().asExpr() =
getSourceNodeArgument(source, s.headOfSingleton())
getSourceNodeArgument(source, s.headOfSingleton()) and
isArgPostUpdate = true
}

RustDataFlow::Node getSourceNode(Input::SourceBase source, Impl::Private::SummaryComponentStack s) {
result = getSourceNode(source, s, _)
}

RustDataFlow::Node getSinkNode(Input::SinkBase sink, Impl::Private::SummaryComponent sc) {
Expand Down
51 changes: 51 additions & 0 deletions rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
*/

private import rust
private import codeql.rust.dataflow.FlowBarrier
private import codeql.rust.dataflow.FlowSummary
private import codeql.rust.dataflow.FlowSource
private import codeql.rust.dataflow.FlowSink
Expand Down Expand Up @@ -98,6 +99,29 @@
string path, string kind, string provenance, QlBuiltins::ExtensionId madId
);

/**
* Holds if in a call to the function with canonical path `path`, the value referred
* to by `output` is a barrier of the given `kind` and `madId` is the data
* extension row number.
*/
extensible predicate barrierModel(
string path, string output, string kind, string provenance, QlBuiltins::ExtensionId madId
);

/**
* Holds if in a call to the function with canonical path `path`, the value referred
* to by `input` is a barrier guard of the given `kind` and `madId` is the data
* extension row number.
* the value referred to by `input` is assumed to lead to a parameter of a call
* (possibly `self`), and the call is guarding the parameter.
* `branch` is either `true` or `false`, indicating which branch of the guard
* is protecting the parameter.
*/
extensible predicate barrierGuardModel(

Check warning

Code scanning / CodeQL

Missing QLDoc for parameter Warning

The QLDoc has no documentation for provenance, but the QLDoc mentions self
string path, string input, string branch, string kind, string provenance,
QlBuiltins::ExtensionId madId
);

/**
* Holds if the given extension tuple `madId` should pretty-print as `model`.
*
Expand All @@ -123,6 +147,16 @@
neutralModel(path, kind, _, madId) and
model = "Neutral: " + path + "; " + kind
)
or
exists(string path, string output, string kind |
barrierModel(path, output, kind, _, madId) and
model = "Barrier: " + path + "; " + output + "; " + kind
)
or
exists(string path, string input, string branch, string kind |
barrierGuardModel(path, input, branch, kind, _, madId) and
model = "Barrier guard: " + path + "; " + input + "; " + branch + "; " + kind
)
}

private class SummarizedCallableFromModel extends SummarizedCallable::Range {
Expand Down Expand Up @@ -206,6 +240,23 @@
}
}

private class BarrierFromModel extends FlowBarrier::Range {
private string path;

BarrierFromModel() {
barrierModel(path, _, _, _, _) and
this.callResolvesTo(path)
// path = this.getCall().getResolvedTarget().getCanonicalPath()
}

override predicate isBarrier(string output, string kind, Provenance provenance, string model) {
exists(QlBuiltins::ExtensionId madId |
barrierModel(path, output, kind, provenance, madId) and
model = "MaD:" + madId.toString()
)
}
}

private module Debug {
private import FlowSummaryImpl
private import Private
Expand Down
4 changes: 2 additions & 2 deletions rust/ql/lib/codeql/rust/dataflow/internal/Node.qll
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,12 @@ class FlowSummaryNode extends Node, TFlowSummaryNode {
result = this.getSummaryNode().getSinkElement()
}

/** Holds is this node is a source node of kind `kind`. */
/** Holds if this node is a source node of kind `kind`. */
predicate isSource(string kind, string model) {
this.getSummaryNode().(FlowSummaryImpl::Private::SourceOutputNode).isEntry(kind, model)
}

/** Holds is this node is a sink node of kind `kind`. */
/** Holds if this node is a sink node of kind `kind`. */
predicate isSink(string kind, string model) {
this.getSummaryNode().(FlowSummaryImpl::Private::SinkInputNode).isExit(kind, model)
}
Expand Down
10 changes: 10 additions & 0 deletions rust/ql/lib/codeql/rust/dataflow/internal/empty.model.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,13 @@ extensions:
pack: codeql/rust-all
extensible: summaryModel
data: []

- addsTo:
pack: codeql/rust-all
extensible: barrierModel
data: []

- addsTo:
pack: codeql/rust-all
extensible: barrierGuardModel
data: []
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
extensions:
- addsTo:
pack: codeql/rust-all
extensible: barrierModel
data:
- ["main::sanitize", "ReturnValue", "test", "manual"]
19 changes: 15 additions & 4 deletions rust/ql/test/library-tests/dataflow/barrier/inline-flow.ql
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,21 @@
*/

import rust
import codeql.rust.dataflow.DataFlow
import codeql.rust.dataflow.FlowBarrier
import utils.test.InlineFlowTest
import DefaultFlowTest
import TaintFlow::PathGraph
import PathGraph

from TaintFlow::PathNode source, TaintFlow::PathNode sink
where TaintFlow::flowPath(source, sink)
module CustomConfig implements DataFlow::ConfigSig {
predicate isSource = DefaultFlowConfig::isSource/1;

predicate isSink = DefaultFlowConfig::isSink/1;

predicate isBarrier(DataFlow::Node n) { barrierNode(n, "test") }
}

import FlowTest<CustomConfig, CustomConfig>

from PathNode source, PathNode sink
where flowPath(source, sink)
select sink, source, sink, "$@", source, source.toString()
43 changes: 41 additions & 2 deletions shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,19 @@ module Make<
abstract predicate isSink(string input, string kind, Provenance provenance, string model);
}

/** A barrier element. */
abstract class BarrierElement extends SourceBaseFinal {
bindingset[this]
BarrierElement() { any() }

/**
* Holds if this element is a flow barrier of kind `kind`, where data
* flows out as described by `output`.
*/
pragma[nomagic]
abstract predicate isBarrier(string output, string kind, Provenance provenance, string model);
}

private signature predicate hasKindSig(string kind);

signature class NeutralCallableSig extends SummarizedCallableBaseFinal {
Expand Down Expand Up @@ -723,7 +736,19 @@ module Make<
)
}

private predicate summarySpec(string spec) {
private predicate isRelevantBarrier(
BarrierElement e, string output, string kind, Provenance provenance, string model
) {
e.isBarrier(output, kind, provenance, model) and
(
provenance.isManual()
or
provenance.isGenerated() and
not exists(Provenance p | p.isManual() and e.isBarrier(_, kind, p, _))
)
}

private predicate flowSpec(string spec) {
exists(SummarizedCallable c |
c.propagatesFlow(spec, _, _, _, _, _)
or
Expand All @@ -732,10 +757,12 @@ module Make<
or
isRelevantSource(_, spec, _, _, _)
or
isRelevantBarrier(_, spec, _, _, _)
or
isRelevantSink(_, spec, _, _, _)
}

import AccessPathSyntax::AccessPath<summarySpec/1>
import AccessPathSyntax::AccessPath<flowSpec/1>

/** Holds if specification component `token` parses as parameter `pos`. */
predicate parseParam(AccessPathToken token, ArgumentPosition pos) {
Expand Down Expand Up @@ -1515,6 +1542,18 @@ module Make<
)
}

/**
* Holds if `barrier` is a relevant barrier element with output specification `outSpec`.
*/
predicate barrierSpec(
BarrierElement barrier, SummaryComponentStack outSpec, string kind, string model
) {
exists(string output |
isRelevantBarrier(barrier, output, kind, _, model) and
External::interpretSpec(output, outSpec)
)
}

signature module TypesInputSig {
/** Gets the type of content `c`. */
DataFlowType getContentType(ContentSet c);
Expand Down
Loading