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