From c2f439a38f789967bd910f53b0a5f8d4ffa43e58 Mon Sep 17 00:00:00 2001 From: yoff Date: Thu, 2 Jul 2026 15:15:28 +0000 Subject: [PATCH] Python: preserve bindingset on new-CFG node dominance wrappers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `Cfg::ControlFlowNode` facade re-exports the shared CFG library's `dominates`/`strictlyDominates` predicates, which are declared `bindingset[this, that]` + `pragma[inline_late]` and are meant to be used as bound-pair membership checks. The facade wrappers dropped these annotations (using plain `pragma[inline]`), so even though the only callers — the `with` / `async with` taint steps in DataFlowPrivate.qll and TaintTrackingPrivate.qll — bind both endpoints, the optimizer was free to materialise `Cfg::ControlFlowNode.strictlyDominates/1` as a full O(nodes^2) relation over the (larger) shared-CFG node set. On some projects this dominated analysis time entirely (DCA showed e.g. ICTU/quality-time and biosimulations regressing ~75-160x). Restoring `bindingset[this, other]` + `pragma[inline_late]` on the wrappers turns the predicate back into a bound-pair check and is result-preserving (only binding annotations change, the predicate body is unchanged). Reproduced on ICTU/quality-time: full python-security-extended suite went from stalling >20min on `strictlyDominates` to completing in ~6min; all ControlFlow and dataflow/coverage library tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- python/ql/lib/semmle/python/controlflow/internal/Cfg.qll | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/python/ql/lib/semmle/python/controlflow/internal/Cfg.qll b/python/ql/lib/semmle/python/controlflow/internal/Cfg.qll index 2d39ae8450e..e04449495f7 100644 --- a/python/ql/lib/semmle/python/controlflow/internal/Cfg.qll +++ b/python/ql/lib/semmle/python/controlflow/internal/Cfg.qll @@ -110,11 +110,13 @@ class ControlFlowNode extends CfgImpl::ControlFlowNode { } /** Holds if this strictly dominates `other`. */ - pragma[inline] + bindingset[this, other] + pragma[inline_late] predicate strictlyDominates(ControlFlowNode other) { super.strictlyDominates(other) } /** Holds if this dominates `other` (reflexively). */ - pragma[inline] + bindingset[this, other] + pragma[inline_late] predicate dominates(ControlFlowNode other) { super.dominates(other) } /** Holds if this is the first node in its enclosing scope. */