diff --git a/rust/ql/lib/codeql/rust/dataflow/FlowBarrier.qll b/rust/ql/lib/codeql/rust/dataflow/FlowBarrier.qll index 1d5e3957112..7ca64bedb5f 100644 --- a/rust/ql/lib/codeql/rust/dataflow/FlowBarrier.qll +++ b/rust/ql/lib/codeql/rust/dataflow/FlowBarrier.qll @@ -51,4 +51,27 @@ module FlowBarrier { final class FlowBarrier = FlowBarrier::Range; +/** Provides the `Range` class used to define the extent of `FlowBarrierGuard`. */ +module FlowBarrierGuard { + /** A flow barrier guard. */ + abstract class Range extends Impl::Public::BarrierGuardElement { + bindingset[this] + Range() { any() } + + override predicate isBarrierGuard( + string input, string branch, string kind, Impl::Public::Provenance provenance, string model + ) { + this.isBarrierGuard(input, branch, kind) and provenance = "manual" and model = "" + } + + /** + * Holds if this element is a flow barrier guard of kind `kind`, for data + * flowing in as described by `input`, when `this` evaluates to `branch`. + */ + predicate isBarrierGuard(string input, string branch, string kind) { none() } + } +} + +final class FlowBarrierGuard = FlowBarrierGuard::Range; + predicate barrierNode = DataFlowImpl::barrierNode/2; diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll index f416c2a20e5..27773758fc4 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll @@ -1157,14 +1157,50 @@ private module Cached { cached predicate sinkNode(Node n, string kind) { n.(FlowSummaryNode).isSink(kind, _) } - /** Holds if `n` is a flow barrier of kind `kind`. */ + private newtype TKindModelPair = + TMkPair(string kind, string model) { + FlowSummaryImpl::Private::barrierGuardSpec(_, _, _, kind, model) + } + + private boolean convertAcceptingValue(FlowSummaryImpl::Public::AcceptingValue av) { + av.isTrue() and result = true + or + av.isFalse() and result = false + // Remaining cases are not supported yet, they depend on the shared Guards library. + // or + // av.isNoException() and result.getDualValue().isThrowsException() + // or + // av.isZero() and result.asIntValue() = 0 + // or + // av.isNotZero() and result.getDualValue().asIntValue() = 0 + // or + // av.isNull() and result.isNullValue() + // or + // av.isNotNull() and result.isNonNullValue() + } + + private predicate barrierGuardChecks(AstNode g, Expr e, boolean gv, TKindModelPair kmp) { + exists( + FlowSummaryImpl::Public::BarrierGuardElement b, + FlowSummaryImpl::Private::SummaryComponentStack stack, + FlowSummaryImpl::Public::AcceptingValue acceptingvalue, string kind, string model + | + FlowSummaryImpl::Private::barrierGuardSpec(b, stack, acceptingvalue, kind, model) and + e = FlowSummaryImpl::StepsInput::getSinkNode(b, stack.headOfSingleton()).asExpr() and + kmp = TMkPair(kind, model) and + gv = convertAcceptingValue(acceptingvalue) and + g = b.getCall() + ) + } + + /** Holds if `n` is a flow barrier of kind `kind` and model `model`. */ cached - predicate barrierNode(Node n, string kind) { + predicate barrierNode(Node n, string kind, string model) { exists( FlowSummaryImpl::Public::BarrierElement b, FlowSummaryImpl::Private::SummaryComponentStack stack | - FlowSummaryImpl::Private::barrierSpec(b, stack, kind, _) + FlowSummaryImpl::Private::barrierSpec(b, stack, kind, model) | n = FlowSummaryImpl::StepsInput::getSourceNode(b, stack, false) or @@ -1174,6 +1210,9 @@ private module Cached { .(PostUpdateNode) .getPreUpdateNode() ) + or + ParameterizedBarrierGuard::getABarrierNode(TMkPair(kind, + model)) = n } /** @@ -1199,3 +1238,34 @@ private module Cached { } import Cached + +/** Holds if `n` is a flow barrier of kind `kind`. */ +predicate barrierNode(Node n, string kind) { barrierNode(n, kind, _) } + +bindingset[this] +private signature class ParamSig; + +private module WithParam { + /** + * Holds if the guard `g` validates the expression `e` upon evaluating to `gv`. + * + * The expression `e` is expected to be a syntactic part of the guard `g`. + * For example, the guard `g` might be a call `isSafe(x)` and the expression `e` + * the argument `x`. + */ + signature predicate guardChecksSig(AstNode g, Expr e, boolean branch, P param); +} + +/** + * Provides a set of barrier nodes for a guard that validates an expression. + * + * This is expected to be used in `isBarrier`/`isSanitizer` definitions + * in data flow and taint tracking. + */ +module ParameterizedBarrierGuard::guardChecksSig/4 guardChecks> { + /** Gets a node that is safely guarded by the given guard check. */ + Node getABarrierNode(P param) { + SsaFlow::asNode(result) = + SsaImpl::DataFlowIntegration::ParameterizedBarrierGuard::getABarrierNode(param) + } +} diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll index 03db7c35b4d..874126f6a08 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll @@ -305,6 +305,31 @@ private module Cached { predicate getABarrierNode = getABarrierNodeImpl/0; } + + bindingset[this] + private signature class ParamSig; + + private module WithParam { + signature predicate guardChecksSig(AstNode g, Expr e, boolean branch, P param); + } + + overlay[global] + cached // nothing is actually cached + module ParameterizedBarrierGuard::guardChecksSig/4 guardChecks> { + private predicate guardChecksAdjTypes( + DataFlowIntegrationInput::Guard g, DataFlowIntegrationInput::Expr e, + DataFlowIntegrationInput::GuardValue branch, P param + ) { + guardChecks(g, e, branch, param) + } + + private Node getABarrierNodeImpl(P param) { + result = + DataFlowIntegrationImpl::BarrierGuardWithState::getABarrierNode(param) + } + + predicate getABarrierNode = getABarrierNodeImpl/1; + } } } diff --git a/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll b/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll index fa20405f788..8b25c54bfa0 100644 --- a/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll +++ b/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll @@ -381,6 +381,21 @@ module Make< abstract predicate isBarrier(string output, string kind, Provenance provenance, string model); } + /** A barrier guard element. */ + abstract class BarrierGuardElement extends SinkBaseFinal { + bindingset[this] + BarrierGuardElement() { any() } + + /** + * Holds if this element is a flow barrier guard of kind `kind`, for data + * flowing in as described by `input`, when `this` evaluates to `branch`. + */ + pragma[nomagic] + abstract predicate isBarrierGuard( + string input, string branch, string kind, Provenance provenance, string model + ); + } + private signature predicate hasKindSig(string kind); signature class NeutralCallableSig extends SummarizedCallableBaseFinal { @@ -748,6 +763,19 @@ module Make< ) } + private predicate isRelevantBarrierGuard( + BarrierGuardElement e, string input, string branch, string kind, Provenance provenance, + string model + ) { + e.isBarrierGuard(input, branch, kind, provenance, model) and + ( + provenance.isManual() + or + provenance.isGenerated() and + not exists(Provenance p | p.isManual() and e.isBarrierGuard(_, _, kind, p, _)) + ) + } + private predicate flowSpec(string spec) { exists(SummarizedCallable c | c.propagatesFlow(spec, _, _, _, _, _) @@ -759,6 +787,8 @@ module Make< or isRelevantBarrier(_, spec, _, _, _) or + isRelevantBarrierGuard(_, spec, _, _, _, _) + or isRelevantSink(_, spec, _, _, _) } @@ -1554,6 +1584,19 @@ module Make< ) } + /** + * Holds if `barrierGuard` is a relevant barrier guard element with input specification `inSpec`. + */ + predicate barrierGuardSpec( + BarrierGuardElement barrierGuard, SummaryComponentStack inSpec, string branch, string kind, + string model + ) { + exists(string input | + isRelevantBarrierGuard(barrierGuard, input, branch, kind, _, model) and + External::interpretSpec(input, inSpec) + ) + } + signature module TypesInputSig { /** Gets the type of content `c`. */ DataFlowType getContentType(ContentSet c);