CodeQL C++ 查询集变更日志深度解读:从 0.0.4 到 1.9.0 的安全分析与查询演进
CodeQL C++ 查询集变更日志深度解读:从 0.0.4 到 1.9.0 的安全分析与查询演进
本指南以仓库中 cpp/ql/src/CHANGELOG.md 为骨架,系统梳理 CodeQL C/C++ 查询集自 0.0.4 至 1.9.0 的完整演进脉络,覆盖新查询的引入、查询精度升降级、CWE 标签维护、数据流模型扩充与误报(False Positive)修复等核心内容,并结合仓库内的查询实现源码、测试用例与查询套件配置给出可验证的佐证,帮助安全研究者与 CodeQL 使用者理解每个版本行为变化背后的实现逻辑与升级路径。
一、文件定位与阅读方式
该文件位于仓库根目录下的 cpp/ql/src/CHANGELOG.md,是 C/C++ 查询集(qlpack)面向用户的版本变更记录,按版本号从新到旧排列,每个版本条目按以下分类组织:
- New Queries:新增查询,标注查询 ID(如
cpp/ambiguous-assignment-of-comparison)与检测目标; - Breaking Changes:破坏性变更,如查询删除、查询迁移、包版本策略调整;
- Deprecated Queries / Predicates / Classes:弃用的查询、谓词与类及其替代方案;
- Query Metadata Changes:查询元数据(精度、CWE 标签、安全等级)调整;
- Major / Minor Analysis Improvements:误报修复、真阳性提升、数据流模型扩充等分析质量改进。
与之一一对应的源码文件位于同目录下的 cpp/ql/src 中,例如 Best Practices/GuardedFree.ql、Likely Bugs/Likely Typos/AmbiguousAssignmentOfComparison.ql 等,可用于核对查询 ID、精度元数据与检测逻辑。
二、新查询引入:检测能力的持续扩充
版本演进中最直观的变化是新增查询。以下按版本整理 CHANGELOG 明确记录的新查询及其仓库实现位置。
2.1 比较赋值歧义检测(1.9.0)
cpp/ambiguous-assignment-of-comparison 用于检测"将比较结果赋值给变量、且该赋值结果又被用作真值"的潜在歧义表达式。其实现位于 Likely Bugs/Likely Typos/AmbiguousAssignmentOfComparison.ql,核心逻辑为:
- 通过
isUsedAsTruthValue判定赋值结果是否被直接用作条件、逻辑运算操作数,或经由逗号表达式、条件表达式、比较表达式间接参与真值判断; - 通过
isExplicitlyGrouped排除已经用括号或显式转换明确分组的比较; - 通过
not assignment.getLValue().getUnspecifiedType() instanceof BoolType排除左值为bool类型的情形(此时赋值比较结果类型恰当,通常是有意为之); - 同时排除未求值上下文(
isUnevaluated)与未实例化模板(isFromUninstantiatedTemplate)。
该查询元数据声明 @precision high,标签包含 external/cwe/cwe-783(CWE-783:运算符优先级逻辑错误),并可在 change-notes/released/1.9.0.md 中找到对应发布说明。
2.2 生命周期过期访问类查询(0.9.x)
CHANGELOG 在 0.9.x 系列集中引入了一批"对象生命周期结束后被访问"类查询:
cpp/use-of-string-after-lifetime-ends(0.9.0):检测对即将销毁的字符串调用c_str并继续使用其指针的场景;cpp/use-of-unique-pointer-after-lifetime-ends(0.9.2):检测临时unique_ptr拥有的内容在指针即将被销毁时仍被使用,实现见 Security/CWE/CWE-416/UseOfUniquePointerAfterLifetimeEnds.ql;cpp/iterator-to-expired-container(0.9.12):检测由即将销毁的临时容器所拥有的迭代器被继续使用,实现见 Security/CWE/CWE-416/IteratorToExpiredContainer.ql。
其中 cpp/use-of-unique-pointer-after-lifetime-ends 在 1.0.0 中收窄了告警范围:当指针被转换为布尔值(如 if (ptr) 这类判空写法)时不再告警,change-notes/released/1.0.0.md 记录了该调整。
2.3 内存安全与缓冲区溢出类(0.6.x–0.7.x)
cpp/double-free(0.6.1):检测同一指针被两次释放的可能场景,初始精度为medium;cpp/use-after-free(0.6.1):检测指针在内存被释放后仍被解引用,初始精度为medium;cpp/overrun-write(0.6.3):检测 C 风格缓冲区操作函数中的缓冲区越界写;cpp/invalid-pointer-deref(0.7.4):检测越界指针读写;cpp/guarded-free(1.3.0):检测调用free前多余的 NULL 检查——因为向free传入 NULL 本身是 no-op。该查询实现见 Best Practices/GuardedFree.ql,其@precision very-high为质量类(quality)查询中的最高档。实现核心是利用控制流库semmle.code.cpp.controlflow.Guards的ensuresEq谓词判断守卫条件,并通过guardConditionGuardsUniqueBlock确保该守卫只保护唯一一个基本块,从而避免误报。
CHANGELOG 还注明,cpp/guarded-free 的一个变体最初由外部贡献者以实验查询形式提交,随后被纳入主仓库,体现了社区贡献进入正式查询集的流程。
2.4 格式化函数与 scanf 族(0.4.x–0.9.x)
cpp/missing-check-scanf(0.4.0):检测scanf输出变量在未检查返回值的情况下被使用(无法确认其确实被写入)的场景;cpp/incorrectly-checked-scanf(0.9.2):检测scanf返回值被错误检查的场景,部分原本由cpp/missing-check-scanf报告的告警迁移至此查询;cpp/too-few-arguments与cpp/wrong-number-format-arguments:格式化函数实参数量不足的检测。
2.5 安全与加密类(0.0.x–0.9.x)
cpp/non-https-url(0.0.4):标记使用httpURL 的场合;cpp/certificate-not-checked与cpp/certificate-result-conflation(0.0.5):标记 OpenSSL 等库的不安全用法;cpp/external-entity-expansion(0.1.1):检测易受 XXE(外部实体扩展)攻击的 XML 对象,精度后续提升至high;cpp/open-call-with-mode-argument(0.0.9):检测以O_CREAT或O_TMPFILE标志调用open/openat但省略mode参数的场景;cpp/type-confusion(0.9.9):检测向无效类型的强制转换。
三、精度升降级与默认查询套件联动
查询元数据中的 @precision 直接影响查询是否进入默认 Code Scanning 查询套件(通常 high 及以上精度才会进入默认套件,medium 进入 security-extended 套件)。CHANGELOG 记录了多次精度调整及其套件联动:
3.1 精度提升(进入默认套件)
| 版本 | 查询 | 调整 |
|---|---|---|
| 1.1.0 | cpp/iterator-to-expired-container、cpp/unsafe-strncat |
精度提升至 high,进入默认 Code Scanning 套件 |
| 1.2.0 | cpp/unsigned-difference-expression-compared-zero |
精度提升至 high,进入默认套件 |
| 1.6.1 | cpp/comparison-with-wider-type、cpp/integer-multiplication-cast-to-long、cpp/suspicious-add-sizeof、cpp/wrong-type-format-argument、cpp/implicit-function-declaration |
精度提升至 high,进入默认套件;其中 cpp/implicit-function-declaration 对 build-mode: none 数据库不再产生结果(该模式下结果噪声过大、本质不精确) |
套件配置可参见 cpp/config/suites/default/cpp,默认套件由 correctness、maintainability、readability、useless-code、metrics、metric-defects 等子套件组成。
3.2 精度降低与套件移出(1.5.0)
1.5.0 是一次反向调整:cpp/wrong-type-format-argument、cpp/comparison-with-wider-type、cpp/integer-multiplication-cast-to-long、cpp/implicit-function-declaration、cpp/suspicious-add-sizeof 的精度由 high 降为 medium,且前四个不再包含在 code-scanning 套件中;同时这些查询开始对 build-mode: none 构建的工程给出告警。对比 1.6.1 的再次提升,可见精度评估会根据误报率数据动态往复调整。
3.3 其余精度调整
cpp/cleartext-transmission:在 0.0.9 由medium提升至high,1.6.0 中其@security-severity从 6.1(medium)上调至 7.8(high);cpp/system-data-exposure:0.0.12 由medium提升至high;cpp/external-entity-expansion:0.1.3 精度提升至high;cpp/double-free、cpp/use-after-free:0.8.0 由medium提升至high(同时进一步减少误报);cpp/non-https-url:0.0.11 精度提升至high并减少误报;cpp/uncontrolled-arithmetic:0.0.8 精度提升至high。
四、查询元数据与 CWE 标签维护
CWE 标签(external/cwe/cwe-xxx)用于将查询关联到通用弱点枚举,便于安全团队按 CWE 归类告警。CHANGELOG 记录了多起标签增删:
- 1.8.0:
cpp/new-free-mismatch新增标签external/cwe/cwe-762,移除external/cwe/cwe-401,使标签更贴合查询实际检测行为;实现文件位于 Critical/NewFreeMismatch.ql; - 1.7.0:
cpp/uncontrolled-process-operation新增external/cwe/cwe-073与external/cwe/cwe-078,实现见 Security/CWE/CWE-114/UncontrolledProcessOperation.ql; - 1.4.0:一批查询的 CWE 标签由两位数格式(
cwe-14、cwe-20)统一为三位数格式(cwe-014、cwe-020),涉及cpp/memset-may-be-deleted、cpp/count-untrusted-data-external-api、cpp/count-untrusted-data-external-api-ir、cpp/untrusted-data-to-external-api-ir、cpp/untrusted-data-to-external-api、cpp/late-check-of-function-argument; - 0.9.9:
cpp/boost/tls-settings-misconfiguration与cpp/boost/use-of-deprecated-hardcoded-security-protocol新增@precision medium元数据并纳入 security-extended 套件,同时更新了@name元数据; - 0.0.8:
security标签被添加到cpp/return-stack-allocated-memory,使其结果默认可见。
此类标签维护体现了 CWE 编号规范化和查询语义对齐的长期治理过程。
五、数据流模型扩充:跨库污点分析的覆盖面提升
污点分析(Taint Tracking)能力高度依赖内置的库函数模型(即"哪些函数是源、汇聚点、净化器或全覆盖写入")。CHANGELOG 记录了多次模型扩充:
- 1.4.4:为 Win32 API 的
CreateThread、CreateRemoteThread、CreateRemoteThreadEx增加流模型;为pthread_create、std::thread增加流模型;新增 GNU C Library 流模型;cpp/sql-injection开始支持通过sql-injection的 Models as Data(MaD)汇聚点类型进行扩展;cpp/path-injection、cpp/sql-injection、cpp/tainted-format-string、cpp/command-line-injection的主告警位置可能因FunctionWithWrappers库(semmle.code.cpp.security.FunctionWithWrappers)的变更而变化; - 1.4.3:新增
madler/zlib、google/brotli、libidn/libidn2、libssh2/libssh2、nghttp2/nghttp2、libuv/libuv、curl/curl的流模型; - 1.4.1:新增 SQLite 与 OpenSSL 的流模型;
- 1.3.2 / 1.3.1:新增
SysAllocString及 Microsoft Active Template Library(ATL)的数据流模型与流源; - 0.9.5:新增
gettext函数变体的数据流模型; - 0.9.9:新增 GLib 分配与释放函数模型;
- 0.6.0:
cpp/tainted-arithmetic开始标记算术赋值操作中的潜在溢出。
这类模型的每一次扩充都意味着在这些库之上运行污点类查询(如 cpp/path-injection、cpp/sql-injection、cpp/cleartext-transmission)时会产生更多告警,CHANGELOG 对此均有明确说明。
六、核心库行为变更与迁移指引
6.1 数据流库的"全覆盖写入"语义(0.9.6)
0.9.6 引入了对 C/C++ 新数据流与污点追踪库(semmle.code.cpp.dataflow.new.DataFlow 与 semmle.code.cpp.dataflow.new.TaintTracking)的重要行为约定:通过 DataFlowFunction 与 TaintFunction 建模的数据流/污点现在隐式假定模型总是完全覆写其缓冲区,从而充当流屏障(flow barrier),因此许多数据流与污点查询的误报显著减少。若需恢复旧行为,可在具体模型中覆写新增的 isPartialWrite 谓词。
6.2 弃用的谓词与类(0.5.5、1.5.0、0.0.10、0.0.11)
- 1.5.0:
MagicConstants文件模块中的occurenceCount谓词弃用,改用occurrenceCount;LeapYear文件模块中的additionalAdditionOrSubstractionCheckForLeapYear弃用,改用additionalAdditionOrSubtractionCheckForLeapYear(修复拼写); - 0.5.5:数据流配置类
NetworkToBufferSizeConfiguration、UntrustedDataToExternalApiConfig弃用,改用NetworkToBufferSizeFlow、UntrustedDataToExternalApiFlow;LeapYearCheckConfiguration、FiletimeYearArithmeticOperationCheckConfiguration、PossibleYearArithmeticOperationCheckConfiguration弃用,改用对应的*Flow变体; - 0.0.10:
CodeDuplication.Copy、CodeDuplication.DuplicateBlock、CodeDuplication.SimilarBlock类弃用; - 0.0.11:
CodeDuplication库中的谓词与类弃用。
6.3 查询删除与迁移(0.9.0、0.0.11)
- 0.9.0:
cpp/tainted-format-string-through-global被删除,因为其告警是cpp/tainted-format-string告警的子集,删除不会导致相关告警丢失; - 0.0.11:删除一批重复检测类旧查询:
cpp/duplicate-block、cpp/duplicate-function、cpp/duplicate-class、cpp/duplicate-file、cpp/mostly-duplicate-function、cpp/similar-file、cpp/duplicated-lines-in-files。
6.4 包版本策略(1.0.0)
1.0.0 标志着 CodeQL 包管理正式可用(Generally Available),所有 GitHub 生产的 CodeQL 包版本号统一提升至 1.0.0;同时上下文查询及其依赖的查询库被迁移到 codeql/cpp-all 包(0.3.0 记录)。
七、典型误报修复:洞察查询精度的迭代逻辑
CHANGELOG 中数量最多的一类是误报(False Positive)修复,以下选取代表性案例:
- 宏展开与预处理器分支:
cpp/ambiguous-assignment-of-comparison排除宏展开中的匹配(not fc.isInMacroExpansion());cpp/compare-where-assign-meant(0.7.4)不再报告宏展开中的比较;cpp/inconsistent-nullness-testing(0.9.11)不再在守卫检查位于宏展开中时告警; __PRETTY_FUNCTION__特殊变量:1.8.3 中cpp/leap-year/unsafe-array-for-days-of-the-year在包围函数签名恰好为 364 个字符时不再对__PRETTY_FUNCTION__(及相关变量)告警——这是针对特殊字符串长度触发的边界性误报修复;build-mode: none数据库:1.6.0 集中修复了cpp/suspicious-add-sizeof、cpp/wrong-type-format-argument、cpp/integer-multiplication-cast-to-long在该模式下的误报,并使诊断查询cpp/diagnostics/extraction-warnings不再产出ExtractionRecoverableWarning(此前会显著增大 SARIF 文件体积导致不可处理);- 提取错误(Extraction Error)抑制:1.3.1 起,多个查询在表达式或类型存在提取错误时不再产生结果,包括
cpp/return-stack-allocated-memory、cpp/badly-bounded-write、cpp/wrong-number-format-arguments、cpp/wrong-type-format-argument,因为此时结果不可靠; - 不可达代码:
cpp/incorrect-string-type-conversion(1.4.4)不再对不可达代码中的类型转换告警;1.2.3 移除了不可达代码中的缓冲区访问与不一致类型检查导致的误报; - CMake 构建测试文件:1.3.2 起,
cpp/badly-bounded-write、cpp/equality-on-floats、cpp/short-global-name、cpp/static-buffer-overflow、cpp/too-few-arguments、cpp/useless-expression、cpp/world-writable-file-creation不再对 CMake 用于测试构建配置而生成的文件告警; - 模板与结构体边界:
cpp/suspicious-allocation-size(1.2.0)不再对"可变大小"(variable size)struct误报;1.3.5 中cpp/static-buffer-overflow不再报告单次memset写多个结构体/类字段的情形;cpp/overflow-buffer(1.4.6)在目标缓冲区类型为类/结构体引用时不再误报。
此外,0.8.0 记录了两项全局性改进:cpp/double-free 与 cpp/use-after-free 在调用非返回函数(如 exit、abort)时误报减少;各查询报告的重复数据流路径数量被显著压缩。
八、查询从"实验"到"默认套件"的完整生命周期
结合 CHANGELOG 与仓库结构可以完整还原一个查询的生命周期:
- 实验引入:如
cpp/missing-check-scanf、cpp/guarded-free均由外部贡献者的实验查询演化而来(change-notes/released/0.4.0.md、change-notes/released/1.3.0.md); - 正式加入查询集:CHANGELOG 以 "New Queries" 形式记录;
- 精度评估与误报治理:多轮 "Minor Analysis Improvements" 修复误报、提升真阳性;
- 精度升级进入默认套件:如 1.1.0、1.2.0、1.6.1 的多次升级;
- (可选)降级或移出套件:如 1.5.0 的反向调整,随后 1.6.1 再次回升;
- (可选)删除或合并:如 0.9.0 删除重复查询、0.9.2 将部分告警从
cpp/missing-check-scanf迁移至cpp/incorrectly-checked-scanf。
九、使用建议与升级注意事项
- 升级 CodeQL C++ 查询包时,优先阅读本文档中当前目标版本相对上一版本的所有条目,特别是 Breaking Changes(查询删除、数据流库语义变更)与 Query Metadata Changes(影响默认套件构成与 CWE 归类);
- 若你的 CI 告警基线依赖
build-mode: none数据库的结果,注意 1.5.0/1.6.0/1.6.1 中相关查询在该模式下的行为反复调整,建议以 1.6.1 之后的行为为准; - 若使用
occurenceCount、additionalAdditionOrSubstractionCheckForLeapYear等被弃用谓词编写自定义查询,应迁移到修正拼写后的新谓词(见 Best Practices/Magic Constants/MagicConstants.qll 所在模块)——这也提示了依赖内部 QLL 库 API 时需跟踪此类拼写修复型破坏性变更; - 若你的代码库大量使用 SQLite、OpenSSL、curl、zlib、libuv、nghttp2、libssh2、brotli、libidn2 等库,1.4.x 系列新增的流模型会使污点类查询告警增加,需提前规划结果去重与分流;
- 为保持与官方行为一致,建议同步查看 cpp/ql/src/change-notes/released 目录下与 CHANGELOG 对应的逐版本发布说明文件,两者互为印证。
结语
cpp/ql/src/CHANGELOG.md 表面上是一份变更记录,实质上是 CodeQL C/C++ 静态分析能力演进的浓缩档案:它记录了检测能力从内存安全(double-free、use-after-free、缓冲区溢出)向生命周期安全(string/unique_ptr/iterator 过期访问)、加密安全(证书校验、密钥强度、XXE)、格式化函数正确性(scanf 族、format 参数)持续扩张的过程,也展示了精度治理、CWE 标签规范化、数据流模型按库补齐的工程化节奏。结合仓库中对应的查询源码(如 GuardedFree.ql、AmbiguousAssignmentOfComparison.ql)与套件配置(cpp/config/suites/default/cpp),读者可以追踪每一个变更背后的实现细节,为自己的查询定制与升级策略提供依据。