Add FlowBarrierGuard to FlowBarrier.qll

This commit is contained in:
Owen Mansel-Chan
2026-03-20 11:08:33 +00:00
parent 77cb35380c
commit 7d65baccb2
4 changed files with 164 additions and 3 deletions

View File

@@ -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;

View File

@@ -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<TKindModelPair, barrierGuardChecks/4>::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<ParamSig P> {
/**
* 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<ParamSig P, WithParam<P>::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<P, guardChecks/4>::getABarrierNode(param)
}
}

View File

@@ -305,6 +305,31 @@ private module Cached {
predicate getABarrierNode = getABarrierNodeImpl/0;
}
bindingset[this]
private signature class ParamSig;
private module WithParam<ParamSig P> {
signature predicate guardChecksSig(AstNode g, Expr e, boolean branch, P param);
}
overlay[global]
cached // nothing is actually cached
module ParameterizedBarrierGuard<ParamSig P, WithParam<P>::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<P, guardChecksAdjTypes/4>::getABarrierNode(param)
}
predicate getABarrierNode = getABarrierNodeImpl/1;
}
}
}

View File

@@ -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);