- 静态分析
- SAST
- 应用安全
- 漏洞扫描
- 代码质量
【免费下载链接】codeql
CodeQL: the libraries and queries that power security researchers around the world, as well as code scanning in GitHub Advanced Security
CodeQL C++ 查询库(cppQL pack)的 4.3.1 版本是一个补丁版本,其发布说明记录了一项重要的缺陷修复:semmle.code.cpp.rangeanalysis.new.RangeAnalysis模块在计算“非常大且复杂”的函数体中的数值范围时不再陷入无限循环。本文以该发布说明为骨架,结合当前仓库中的范围分析模块源码,深入讲解这一修复涉及的模块职责、终止性问题的成因,以及该模块在 CodeQL C++ 安全查询体系中的实际地位,帮助读者理解这一补丁修复的底层原理与其对查询性能、结果完备性的影响。
一、4.3.1 发布说明原文
本次发布的变更记录位于 cpp/ql/lib/change-notes/released/4.3.1.md,全文如下:
4.3.1
Bug Fixes
- Fixed an infinite loop in
semmle.code.cpp.rangeanalysis.new.RangeAnalysiswhen computing ranges in very large and complex function bodies.
这条变更记录属于 CodeQL 仓库标准的“change notes”体系:每个已发布的查询库版本对应cpp/ql/lib/change-notes/released/目录下的一个独立 Markdown 文件(如 0.0.4.md、4.3.0.md、12.1.1.md 等),按“新增功能 / Bug 修复 / 弃用”等分类组织。仓库根目录的 docs/change-notes.md 描述了这套变更记录的编写约定。4.3.1作为4.3.0之后的补丁号,只包含缺陷修复,不引入新功能,因此本次记录仅有一条 Bug Fixes 条目。
虽然记录只有一句话,但它指向了一个贯穿多类安全查询的核心基础设施模块——这正是值得深入展开的技术点。
二、被修复的模块:semmle.code.cpp.rangeanalysis.new.RangeAnalysis
2.1 模块定位:C++ 的相对范围分析
发布说明中被修复的模块位于 cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/RangeAnalysis.qll,其文件头注释给出了精确定义:
Provides an AST-based interface to the relative range analysis, which tracks bounds of the form
a <= b + deltafor expressionsaandband an integer offsetdelta.
也就是说,这是一个面向 AST 的“相对范围分析”接口:它并不只推断某个表达式是常数范围内的值,而是追踪形如表达式 a <= 表达式 b + 整数偏移 delta的相对边界。这种形式比单纯的常数范围分析表达能力更强——例如它可以表达“y <= arr.length - 2”这类依赖其他运行时值的边界。
2.2 核心公共谓词
该模块向查询作者暴露了以下关键入口(对应文件 cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/RangeAnalysis.qll):
bounded(Expr e, Bound b, float delta, boolean upper, Reason reason):当upper = true时表示e <= b + delta,且该边界可以追溯到一个 guard 条件(由reason表示);convertedBounded(...):与bounded语义相同,但允许e是一个转换(conversion)表达式,即带隐式类型转换后依然成立的范围边界。
配套的类型还包括:
Bound抽象类:表示可为表达式推断出的“边界基值”,其子类ZeroBound(整数 0,用于表示所有常数边界)与ValueNumberBound(对应某个全局值编号GVN,即某个程序值的抽象代表)分别对应“相对零的常数边界”与“相对某个程序值的边界”;Reason抽象类:CondReason表示边界来自某个具体条件(guard),NoReason表示边界是直接推导得到、不经过任何边界条件。
2.3 底层语义实现:IR 层的两阶段分析
AST 接口只是薄薄的一层“翻译”,真正的计算发生在语义层。RangeAnalysis.qll将 AST 表达式映射为语义表达式后,委托给 cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/analysis/RangeAnalysis.qll 与 cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/analysis/RangeAnalysisImpl.qll。
从RangeAnalysisImpl.qll的源码结构可以看到,分析被组织为两个可实例化的阶段(RangeStage):
ConstantStage:常数阶段,推断零基(ZeroBound)边界,即e <= 0 + delta形式的常数范围;RelativeStage:相对阶段,推断相对边界,即e <= b + delta且b是某个程序值。
公共谓词semBounded将两阶段的结果取并集(见 RangeAnalysisImpl.qll),并通过FloatDelta支持浮点偏移量。此外还组合了符号分析(SignAnalysis)与模运算分析(ModulusAnalysisInstantiated,来自codeql.rangeanalysis.ModulusAnalysis),用于处理取模、位与、位或等运算的边界传递。
因此,4.3.1 中“在计算范围时出现无限循环”的修复,作用于的正是这一整套跨阶段、跨 IR 指令的递归推导过程。
三、无限循环为什么会发生:终止性问题的理论背景
发布说明没有描述修复的具体手法,但仓库中同族模块的头部注释对这类分析的非终止风险给出了权威解释。参见实验版模块 cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll,其开篇完整描述了这一分析的基本模型:
- 步进关系(step relation):分析把范围推断建模为流问题。以
len = arr.length; if (x < len) { ... y = x - 1; ... }为例,目标是推断y <= arr.length - 2,通过跟踪一条推导链完成:arr.length --> len = .. --> x < len --> x-1 --> y = .. --> y。 - 带偏移的步进:为处理加减常数与严格比较,步进关系被泛化为
I1 --(delta)--> I2,表示“若I1 <= B则I2 <= B + delta”,对应谓词boundFlowStep。 - 传递闭包:完整范围分析即步进关系的传递闭包,沿途累加
delta,对应谓词boundedInstruction。
关键在于,该注释还明确指出(RangeAnalysis.qll):
As all inferred bounds can be related directly to a path in the source code the only source of non-termination is if successive redundant (and thereby increasingly worse) bounds are calculated along a loop in the source code. We prevent this by weakening the bound to a small finite set of bounds when a path follows a second back-edge.
即:由于所有推断出的边界都能直接关联到源码中的一条路径,非终止的唯一来源是:沿着源码中的循环反复计算“冗余且越来越差”的边界(例如每次迭代都让delta增大/减小一个单位,永不收敛)。防御手段是当路径经过第二条回边(back-edge,即循环反向边)时,把边界弱化到一个小而有限的集合(weakenDelta将偏移弱化到[-1..1]区间,见 RangeAnalysis.qll 的weakenDelta)。之所以推迟到第二条回边才弱化,是因为精确边界有时需要完整遍历一次循环。
结合这一背景可以推断:4.3.1 修复的“在非常大且复杂的函数体中计算范围时出现无限循环”,属于同一类终止性缺陷在该“new”模块上的具体表现——当函数体规模极大、控制流复杂(多重嵌套循环、大量 phi 节点与回边)时,原有的弱化策略可能在某条推导路径上未能及时将边界限制到有限集合,导致推导链不收敛。修复的目标就是保证即使面对极端复杂的函数体,范围分析也必然终止。
3.1 phi 节点与回边:复杂度的重要来源
为什么复杂函数体容易触发这类问题?从实验版模块的注释可以看到,phi 节点(SSA 合并点)需要特殊处理:对x0 = phi(x1, x2),当x1 <= B + d1且x2 <= B + d2时,x0 <= B + max(d1, d2);而当x2来自回边且能证明变量在循环迭代中“非增”时,可以用“循环前成立的上界在循环内依然成立”这一规则。对 n 输入的 phi 节点,规则进一步泛化(RangeAnalysis.qll)。
在语义实现中,对应逻辑分散于boundedPhiOperand、boundedPhiInp、selfBoundedPhiInp、boundedPhiCandValidForEdge等谓词(见 RangeAnalysisImpl.qll)。函数体越大、循环与分支越复杂,phi 节点与回边就越多,边界推导的递归深度与分支组合数呈指数增长,非终止与性能恶化的风险随之显著上升——这正是发布说明强调“very large and complex function bodies”的原因。
四、修复的实际影响:范围分析被哪些查询使用
RangeAnalysis不是孤立模块,它支撑着 C++ 查询库中多条与“越界”“溢出”直接相关的安全查询。以下是仓库中确认的消费方(均为private import或import该模块的文件):
- cpp/ql/lib/semmle/code/cpp/security/Overflow.qll:溢出检测的核心库,导入
SimpleRangeAnalysis与RangeAnalysisUtils; - cpp/ql/lib/semmle/code/cpp/security/InvalidPointerDereference/AllocationToInvalidPointer.qll 与 InvalidPointerToDereference.qll:支撑
cpp/invalid-pointer-deref查询; - cpp/ql/lib/semmle/code/cpp/security/ProductFlowUtils/ProductFlowUtils.qll:乘积类数据流工具;
- cpp/ql/lib/semmle/code/cpp/commons/Printf.qll:格式化字符串相关检查。
其中,面向查询作者常用的入口是 cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/SimpleRangeAnalysis.qll(提供upperBound/lowerBound等简化接口),以及供上述安全查询使用的 cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/RangeAnalysisUtil.qll。后者头部注释明确写道:
This file contains the range-analysis specific parts of the
cpp/invalid-pointer-derefandcpp/overrun-writequery.
其bounded1/bounded2/bounded谓词通过bindingset与inline_late等编译指令强制 join 顺序,以保证在已绑定的指令上高效求值(见 RangeAnalysisUtil.qll)。
因此,4.3.1 的修复不只是“某个内部模块不卡死”这么简单:它保证依赖范围分析的溢出检测(如整数溢出、无效指针解引用、缓冲区越界写等)在遇到极端庞大的函数体时依然能够正常返回结果,而不是让整个查询评估在该函数上挂起——这对 CodeQL 在大型真实代码库上的可用性与结果完备性都至关重要。
五、如何在当前仓库中验证与跟进
- 查看本次变更记录:直接阅读 cpp/ql/lib/change-notes/released/4.3.1.md,对比相邻版本 4.3.0.md 可观察补丁版本只含 Bug Fixes 的发布节奏;
- 通读被修复模块:cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/RangeAnalysis.qll 全文约 100 行,是理解“相对范围分析”公共接口的最佳起点;
- 追踪语义实现:RangeAnalysisImpl.qll 展示了常数阶段与相对阶段如何组合、符号分析与模运算分析如何接入;
- 学习终止性理论:实验版模块 cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll 的头部注释是理解步进关系、回边与弱化策略的最佳材料。
对于使用 CodeQL 的开发者而言,本修复随cpp查询库 4.3.1 版本发布,升级到包含该版本或更高版本的 CodeQL 查询包后即可自动受益,无需修改任何查询代码——影响体现在:原本在超大函数体上可能导致范围分析不终止的查询场景,现在能够顺利评估并产出结果。
小结
cpp查询库 4.3.1 用一条简洁的 Bug Fixes 记录,修复了semmle.code.cpp.rangeanalysis.new.RangeAnalysis在非常大且复杂的函数体中计算范围时的无限循环问题。透过这条记录,我们得以审视 CodeQL C++ 中相对范围分析的设计:以a <= b + delta为基本形式、以步进关系与传递闭包为计算模型、以回边弱化策略保证终止性。该模块作为溢出、无效指针解引用、越界写等核心安全查询的公共依赖,其终止性与性能直接决定了这些查询在大型代码库上的可用性——这正是本次补丁修复的深层价值所在。
- 静态分析
- SAST
- 应用安全
- 漏洞扫描
- 代码质量
【免费下载链接】codeql
CodeQL: the libraries and queries that power security researchers around the world, as well as code scanning in GitHub Advanced Security
相关推荐
CodeQL C++ 安全查询解读:循环条件中的宽类型比较检测(cpp/comparison-with-wider-type)
CodeQL C++ 安全查询解读:循环条件中的宽类型比较检测(cpp/comparison with wider type) 在 C/C++ 代码中,当循环条
静态分析SAST应用安全漏洞扫描代码质量json_repair库中的无限循环问题分析与修复
json_repair库中的无限循环问题分析与修复 在解析特定格式的JSON字符串时,json_repair库0.46.0版本存在一个可能导致无限循环的严重缺陷
开发工具AI 应用CodeQL C/C++ 查询库 0.4.3 变更解读:FormatLiteral 精度估算缺陷修复与 printf 缓冲区分析原理
CodeQL C/C++ 查询库 0.4.3 变更解读:FormatLiteral 精度估算缺陷修复与 printf 缓冲区分析原理 本篇技术指南以 CodeQ
静态分析SAST应用安全漏洞扫描代码质量
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考