CodeQL C++ 查询集变更日志深度解读:从 0.0.4 到 1.9.0 的安全分析与查询演进

原创2026-09-28 17:05:441,987 阅读
文章标签:静态分析SAST应用安全漏洞扫描代码质量

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-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):标记使用 http URL 的场合;
  • 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 与仓库结构可以完整还原一个查询的生命周期:

  1. 实验引入:如 cpp/missing-check-scanf、cpp/guarded-free 均由外部贡献者的实验查询演化而来(change-notes/released/0.4.0.md、change-notes/released/1.3.0.md);
  2. 正式加入查询集:CHANGELOG 以 "New Queries" 形式记录;
  3. 精度评估与误报治理:多轮 "Minor Analysis Improvements" 修复误报、提升真阳性;
  4. 精度升级进入默认套件:如 1.1.0、1.2.0、1.6.1 的多次升级;
  5. (可选)降级或移出套件:如 1.5.0 的反向调整,随后 1.6.1 再次回升;
  6. (可选)删除或合并:如 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),读者可以追踪每一个变更背后的实现细节,为自己的查询定制与升级策略提供依据。

登录后查看全文
codeql