☰
CodeQL C++ 库 1.4.0 变更详解:模板推导指南、explicit 检测与 using enum 建模增强
2026/9/28 12:33:22 网站建设 项目流程
  • 静态分析
  • SAST
  • 应用安全
  • 漏洞扫描
  • 代码质量

【免费下载链接】codeql

CodeQL: the libraries and queries that power security researchers around the world, as well as code scanning in GitHub Advanced Security

项目地址:https://gitcode.com/gh_mirrors/co/codeql
点击查看免费下载

本篇文章系统梳理 CodeQL 仓库中 C++ 分析库(cpp/ql/lib)1.4.0 版本发布的新特性与静态分析改进,涵盖DeductionGuide、Function、NewOrNewArrayExpr/DeleteOrDeleteArrayExpr等核心 QL 类新增的谓词,以及constexpr if未求值语义、别名分析性能与using enum声明建模的调整。读者将掌握这些新增 QL API 的调用方式、底层数据表支撑与典型查询场景,可直接用于编写或扩展 C++ 安全与质量分析查询。

一、版本概览与适用范围

CodeQL C++ 分析库的 1.4.0 版本变更记录位于 cpp/ql/lib/change-notes/released/1.4.0.md,该版本主要面向 C++ 语言建模层的能力增强,包含四类新增 QL 谓词与三项语义/性能改进:

  • New Features(新增特性):为 4 个核心 QL 类补充了 4 个新谓词,覆盖模板推导指南、explicit 声明、destroying delete 释放函数识别;
  • Minor Analysis Improvements(次要分析改进):修正constexpr if的未求值语义、优化大函数体别名分析性能、新增UsingEnumDeclarationEntry类并调整using enum的条目合成行为。

这些 API 定义在 cpp/ql/lib/semmle/code/cpp/ 目录下的 QLL 库模块中,与其对应的 dbscheme 数据表(cpp/ql/lib/semmlecode.cpp.dbscheme)共同构成分析器与查询之间的接口层。

二、DeductionGuide 类新增 getTemplateClass 谓词

2.1 谓词语义

DeductionGuide类表示 C++ 中的推导指南(deduction guide),即 C++17 起用于指导类模板参数推导的声明。1.4.0 版本为其新增了getTemplateClass谓词,用于获取该推导指南所服务的类模板:

TemplateClass getTemplateClass() { deduction_guide_for_class(underlyingElement(this), unresolveElement(result)) }

该实现位于 cpp/ql/lib/semmle/code/cpp/Function.qll#L944-L953,底层直接依赖 dbscheme 中的deduction_guide_for_class二元关系:第一个参数是推导指南元素本身,第二个参数是它为之服务的类模板。

2.2 使用场景示例

考虑如下 C++17 代码:

template <typename T> struct Box { Box(T value); }; // 用户自定义推导指南 Box(int) -> Box<int>;

对应查询可写成:

import cpp from DeductionGuide g, TemplateClass tc where tc = g.getTemplateClass() select g, tc, "该推导指南服务于类模板 " + tc.getName()

在 1.4.0 之前,从DeductionGuide反向找到其目标类模板需要手工遍历声明上下文;现在该关联关系被直接建模为一条谓词,使围绕模板推导的分析(如推导结果类型推断、隐式推导指南 vs 显式推导指南的区分)更加直接。

三、Function 类新增 explicit 检测谓词

3.1 isExplicit:判断是否声明为 explicit

Function类新增的isExplicit谓词用于判断函数是否被explicit关键字修饰:

predicate isExplicit() { this.hasSpecifier("explicit") }

实现位于 cpp/ql/lib/semmle/code/cpp/Function.qll#L121-L124,通过复用既有的说明符检查机制hasSpecifier实现。它适用于构造函数、转换运算符等所有可被声明为explicit的函数,此前查询需要自行检查说明符集合,现在可直接调用。

3.2 getExplicitExpr:获取条件 explicit 的常量表达式

C++20 引入了条件式explicit(expr)写法,其布尔常量表达式在编译期决定构造函数或转换函数是否为 explicit。getExplicitExpr谓词用于取出这个表达式:

Expr getExplicitExpr() { explicit_specifier_exprs(underlyingElement(this), unresolveElement(result)) }

实现位于 cpp/ql/lib/semmle/code/cpp/Function.qll#L126-L139,库文档给出了典型示例:

template<typename T> struct C { explicit(sizeof(T) == 1) C(const T); };

对上述代码,getExplicitExpr返回表达式sizeof(T) == 1。借助该谓词,分析器既可以判定“存在显式说明符”,也可以进一步求值或分析决定显式性的常量表达式,例如检查该表达式是否恒为真/假、是否依赖模板参数等。

两个谓词可组合使用:isExplicit()覆盖传统写法与explicit(expr)写法;对于后者,getExplicitExpr()提供表达式级别的可编程访问。

四、destroying delete 释放函数识别

4.1 背景:destroying delete

C++20 允许将类专属的operator delete声明为 destroying delete(形式为operator delete(T*, std::destroying_delete_t)),此时delete表达式会先调用析构函数,再由该运算符负责销毁对象并释放内存。1.4.0 版本在NewOrNewArrayExpr与DeleteOrDeleteArrayExpr两个类上同时新增了isDestroyingDeleteDeallocation谓词。

4.2 实现与底层数据表

两个类上的实现一致,例如 cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll#L969-L977(NewOrNewArrayExpr)与 cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll#L1144-L1152(DeleteOrDeleteArrayExpr):

predicate isDestroyingDeleteDeallocation() { exists(int form | expr_deallocator(underlyingElement(this), _, form) and form.bitAnd(4) != 0 // Bit two is the "destroying delete" bit ) }

该实现基于 dbscheme 中的expr_deallocator关系(定义于 cpp/ql/lib/semmlecode.cpp.dbscheme#L1609-L1613)。该关系携带一个form整数列,以位标志编码释放调用携带的参数信息:

位掩码含义
bit 01释放函数带 size 参数(sized deallocation)
bit 12释放函数带 alignment 参数(aligned deallocation)
bit 24释放函数是 destroying delete

同一文件中的hasSizedDeallocation()(掩码1)与hasAlignedDeallocation()(掩码2)早在之前版本就已存在,1.4.0 新增的 bit 2 使三个特性得以统一在同一个位标志机制下建模。

4.3 使用示例

import cpp from DeleteExpr d, Function f where f = d.getDeallocator() and d.isDestroyingDeleteDeallocation() select d, f, "该 delete 表达式使用的是 destroying delete 释放函数"

该谓词对于资源生命周期相关查询(例如 double-delete、自定义删除器与析构顺序分析)很有价值,能够精确区分普通operator delete与 C++20 destroying delete 两种释放路径。

五、constexpr if 控制表达式识别为未求值

1.4.0 修正了一个语义细节:constexpr if(即if constexpr)的控制表达式现在总是被识别为未求值表达式(unevaluated expression)。

Expr类中的isUnevaluated谓词定义于 cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll#L289-L304,它说明“表达式因其所在上下文而不会被求值,例如sizeof内部的表达式”,其实现覆盖了SizeofOperator、非多态类型的typeid操作数等经典未求值上下文。本次变更将if constexpr的判定条件纳入同一语义范畴:

template <typename T> void f(T x) { if constexpr (sizeof(T) > 4) { // 分支 A } else { // 分支 B } }

其中sizeof(T) > 4不会被运行时求值。此修正对依赖isUnevaluated的查询影响直接:例如数据流分析不再认为该表达式中的操作数产生实际读取/写入,避免在if constexpr分支中产生误报或漏报;依赖表达式求值属性的别名分析与污点分析也会获得更一致的语义边界。

六、大函数体别名分析性能改进

6.1 变更内容

1.4.0 改进了对大函数体的别名分析(alias analysis)性能。变更记录同时提示了一个潜在影响:在极少数情况下,依赖大函数体别名分析结果的告警(alerts)可能因结果集变化而受影响。

6.2 影响范围与注意事项

该改进位于分析引擎的别名分析实现层,查询作者无需改动代码即可受益,但需要注意:

  • 别名分析是 C++ 数据流与指针分析的基础组件,其性能直接影响大型函数(数千行、大量局部变量与指针操作)的查询耗时;
  • “极少数情况下告警可能受影响”意味着本次优化可能伴随求解顺序或保守近似策略的微调,导致个别边界用例的结果发生变化。若团队基于旧版本建立了告警基线,升级后建议对高度依赖别名分析的查询(如悬垂指针、use-after-free、double-free 相关)做一次回归验证。

七、using enum 声明建模:新增 UsingEnumDeclarationEntry 类

7.1 新类定义

C++20 引入了using enum声明(如using enum Foo;),用于将枚举成员引入当前作用域。1.4.0 为此新增了UsingEnumDeclarationEntry类,定义于 cpp/ql/lib/semmle/code/cpp/Namespace.qll#L223-L240:

class UsingEnumDeclarationEntry extends UsingEntry { UsingEnumDeclarationEntry() { usings(underlyingElement(this), _, _, 3) } Enum getEnum() { usings(underlyingElement(this), unresolveElement(result), _, _) } override string toString() { result = "using enum " + this.getEnum().getQualifiedName() } }

usings关系以 kind 字段区分不同种类的 using 条目:kind2对应UsingDirectiveEntry(using namespace std;,见 Namespace.qll#L211-L221),kind3则对应本版本新增的UsingEnumDeclarationEntry。新类提供:

  • getEnum():获取被引用的枚举类型;
  • toString():输出using enum Foo形式的限定名描述,便于在结果中直接呈现。

7.2 行为调整:不再为单个枚举器合成 UsingDeclarationEntry

与新增类配套,1.4.0 还调整了条目的合成(synthesized)行为:不再为using enum所引用枚举的每一个枚举器单独合成UsingDeclarationEntry。

此前,using enum Foo;会被展开为一组针对Foo::a、Foo::b等单个枚举器的 using 条目,导致名称查找相关查询需要处理大量合成条目。本次变更将using enum作为整体建模为单一入口,使:

  • 基于UsingDeclarationEntry的名称解析/作用域查询结果更加简洁;
  • 查询仍可通过UsingEnumDeclarationEntry.getEnum()拿到完整枚举,再按需枚举其成员(例如通过Enum.getEnumerator())。

对于依赖旧行为的查询,升级 1.4.0 后需要注意:若查询遍历UsingDeclarationEntry并期望覆盖到using enum引用的枚举器,需要显式追加对UsingEnumDeclarationEntry的处理分支。

八、升级与查询迁移建议

  1. 在查询模块头部import cpp后即可直接使用上述新谓词,无需额外引入专用库文件;
  2. isDestroyingDeleteDeallocation同时存在于 new 与 delete 两类表达式上,编写资源生命周期查询时注意二者语义差异(前者关注分配初始化异常时的释放路径,后者关注普通delete路径);
  3. 若历史查询依赖if constexpr控制表达式的“已求值”行为,或依赖using enum展开出的枚举器级UsingDeclarationEntry,请按第六、七节说明调整逻辑并回归测试;
  4. 版本变更的完整历史可参考 cpp/ql/lib/CHANGELOG.md,其中同步记录了 1.4.0 的上述全部条目,便于在多版本升级时对照检索。
  • 静态分析
  • SAST
  • 应用安全
  • 漏洞扫描
  • 代码质量

【免费下载链接】codeql

CodeQL: the libraries and queries that power security researchers around the world, as well as code scanning in GitHub Advanced Security

项目地址:https://gitcode.com/gh_mirrors/co/codeql
点击查看免费下载

相关推荐

上一篇:终极指南|3分钟用AI读懂千万行代码(附实战案例)
下一篇:Obsidian终极性能优化实战:从界面美化到效率革命的完整指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询