diff --git a/cpp/ql/lib/change-notes/2026-02-24-barrier-guards.md b/cpp/ql/lib/change-notes/2026-02-24-barrier-guards.md new file mode 100644 index 000000000000..8ebd788f9f8f --- /dev/null +++ b/cpp/ql/lib/change-notes/2026-02-24-barrier-guards.md @@ -0,0 +1,4 @@ +--- +category: breaking +--- +* CodeQL version 2.24.2 accidentially introduced a syntactical breaking change to `BarrierGuard<...>::getAnIndirectBarrierNode` and `InstructionBarrierGuard<...>::getAnIndirectBarrierNode`. These breaking changes have now been reverted so that the original code compiles again. \ No newline at end of file diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll index 9bc3a80e3e0c..d704c7d56d65 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll @@ -2641,7 +2641,54 @@ module BarrierGuard { exists(unit) } - import ParameterizedBarrierGuard + private module P = ParameterizedBarrierGuard; + + predicate getABarrierNode = P::getABarrierNode/0; + + /** + * Gets an indirect expression node with indirection index `indirectionIndex` that is + * safely guarded by the given guard check. + * + * For example, given the following code: + * ```cpp + * int* p; + * // ... + * *p = source(); + * if(is_safe_pointer(p)) { + * sink(*p); + * } + * ``` + * and the following barrier guard check: + * ```ql + * predicate myGuardChecks(IRGuardCondition g, Expr e, boolean branch) { + * exists(Call call | + * g.getUnconvertedResultExpression() = call and + * call.getTarget().hasName("is_safe_pointer") and + * e = call.getAnArgument() and + * branch = true + * ) + * } + * ``` + * implementing `isBarrier` as: + * ```ql + * predicate isBarrier(DataFlow::Node barrier) { + * barrier = DataFlow::BarrierGuard::getAnIndirectBarrierNode(1) + * } + * ``` + * will block flow from `x = source()` to `sink(x)`. + * + * NOTE: If a non-indirect expression is tracked, use `getABarrierNode` instead. + */ + Node getAnIndirectBarrierNode(int indirectionIndex) { + result = P::getAnIndirectBarrierNode(indirectionIndex, _) + } + + /** + * Gets an indirect expression node that is safely guarded by the given guard check. + * + * See `getAnIndirectBarrierNode/1` for examples. + */ + Node getAnIndirectBarrierNode() { result = getAnIndirectBarrierNode(_) } } private module InstrWithParam { @@ -2752,7 +2799,20 @@ module InstructionBarrierGuard + private module P = ParameterizedInstructionBarrierGuard; + + predicate getABarrierNode = P::getABarrierNode/0; + + /** + * Gets an indirect node with indirection index `indirectionIndex` that is + * safely guarded by the given guard check. + */ + Node getAnIndirectBarrierNode(int indirectionIndex) { + result = P::getAnIndirectBarrierNode(indirectionIndex, _) + } + + /** Gets an indirect node that is safely guarded by the given guard check. */ + Node getAnIndirectBarrierNode() { result = getAnIndirectBarrierNode(_) } } /** diff --git a/cpp/ql/test/library-tests/dataflow/ir-barrier-guards/test.ql b/cpp/ql/test/library-tests/dataflow/ir-barrier-guards/test.ql index 8948c65e33c0..ce7b79991020 100644 --- a/cpp/ql/test/library-tests/dataflow/ir-barrier-guards/test.ql +++ b/cpp/ql/test/library-tests/dataflow/ir-barrier-guards/test.ql @@ -15,7 +15,10 @@ predicate instructionGuardChecks(IRGuardCondition gc, Instruction checked, boole module BarrierGuard = DataFlow::InstructionBarrierGuard; predicate indirectBarrierGuard(DataFlow::Node node, string s) { - node = BarrierGuard::getAnIndirectBarrierNode(_) and + // This any(...) could technically be removed, but it helps us verify that we don't + // accidentially change the API of this predicate (for instance, by having + // the column be a unit parameter). + node = BarrierGuard::getAnIndirectBarrierNode(any(int indirectionIndex)) and if node.isGLValue() then s = "glval<" + node.getType().toString().replaceAll(" ", "") + ">" else s = node.getType().toString().replaceAll(" ", "")