☰
CodeQL C/C++ 静态缓冲区溢出检测:从 `cpp/static-buffer-overflow` 的精度升级看安全查询工程化
2026/9/27 7:04:49 网站建设 项目流程
  • 静态分析
  • 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 仓库cpp/old-change-notes/2021-09-27-overflow-static.md这条变更记录展开,系统讲解 C/C++ 静态数组越界问题(cpp/static-buffer-overflow)的检测原理、检测范围、精调度机制,以及该查询从 2021 年 5 月到 9 月经历的三轮演进(降误报 → 扩大变长字段识别 → 精度升级为 high 并默认启用)。读完本文,你将掌握:该查询在源码中的定位与实现要点、precision与security-severity元数据如何驱动 CodeQL 的默认启用策略,以及如何通过测试套件与ConfigurationTestFile机制理解其检测边界。

变更记录解读:一次"默认开启"的里程碑

cpp/old-change-notes/2021-09-27-overflow-static.md是 2021 年 9 月 27 日发布的 CodeQL 变更说明,全文如下:

lgtm,codescanning * Increase precision to high for the "Static buffer overflow" query (`cpp/static-buffer-overflow`). This means the query is run and displayed by default on Code Scanning and LGTM.

短短三行内容,蕴含了 CodeQL 安全查询发布流程中的两个关键机制:

  1. 标签lgtm,codescanning:表示该变更同时影响 LGTM.com 与 GitHub Code Scanning 两个平台。
  2. precision从既有等级提升到high:触发 CodeQL 的默认查询集包含逻辑,使该查询无需用户显式配置即可在 Code Scanning 和 LGTM 上运行并展示结果。

在仓库中,该查询的实体文件为 cpp/ql/src/Critical/OverflowStatic.ql,其头部元数据正体现了这次升级的最终状态:

/** * @name Static array access may cause overflow * @description Exceeding the size of a static array during write or access operations * may result in a buffer overflow. * @kind problem * @problem.severity warning * @security-severity 9.3 * @precision high * @id cpp/static-buffer-overflow * @tags reliability * security * external/cwe/cwe-119 * external/cwe/cwe-131 */

其中:

  • @precision high:对应 2021-09-27 这次变更的落点。在 CodeQL 中,precision反映查询的误报率水平,是查询能否进入"默认启用"列表的核心依据。
  • @problem.severity warning与@security-severity 9.3:表示该问题按安全严重性评级高达 9.3,属于高危告警,对应 CWE-119(内存缓冲区边界内操作限制不当)与 CWE-131(缓冲区大小计算错误)。
  • @kind problem:表示这是一条问题类查询,会生成告警条目而非路径类结果。

查询是什么:静态数组越界的三类检测场景

结合 OverflowStatic.ql 的源码,cpp/static-buffer-overflow并非单一规则,而是由三个谓词共同构成的三类检测场景:

场景对应谓词典型触发代码
循环中数组下标越界overflowOffsetInLoopfor (i = 0; i < N; i++) buf[i]且N超过buf大小
传入错误缓冲区大小的库函数调用wrongBufferSizestrncpy(buf, s, SIZE)而SIZE > sizeof(buf)
常量下标直接越界访问outOfBoundsbuf[5]而buf只有 5 个元素

场景一:循环计数器驱动越界

overflowOffsetInLoop通过ClassicForLoop检查经典三段式for循环:

  • 循环上限loop.limit() >= bufaccess.bufferSize();
  • 循环计数器loop.counter()恰好是数组下标bufaccess.getArrayOffset();
  • 区间分析(SimpleRangeAnalysis)未给出更小的上界,且上限未被widened(not upperBoundMayBeWidened)。

命中时输出形如:

Potential buffer-overflow: counter 'i' <= 3 but 'buffer1' has 3 elements.

其实际测试期望见 cpp/ql/test/query-tests/Critical/OverflowStatic/OverflowStatic.expected:

|| test.cpp:19:3:19:12 | access to array | Potential buffer-overflow: counter 'i' <= 3 but 'buffer1' has 3 elements. || test.cpp:20:3:20:12 | access to array | Potential buffer-overflow: counter 'i' <= 3 but 'buffer2' has 3 elements.

场景二:缓冲区大小参数与声明不符

wrongBufferSize维护了一张常见 C 库函数签名表(谓词bufferAndSizeFunction),精确记录"哪个参数是缓冲区、哪个参数是大小":

函数缓冲区参数索引大小参数索引
read12
fgets01
strncpy02
strncat02
memcpy02
memmove02
snprintf01
vsnprintf01

检测逻辑为:目标缓冲区是静态数组(staticBuffer(call.buffer(), buf, bufsize))且声明大小bufsize小于调用中实际传入的大小statedSize。大小实参的取值同时来自数据流(DataFlow::localExprFlow)与区间分析的上界upperBound,并取两者最小值以保证结果可靠。典型误报示例(来自查询自带的示例文件 OverflowStatic.cpp):

#define SIZE 30 int f(char * s) { char buf[20]; //buf not set to use SIZE macro strncpy(buf, s, SIZE); //wrong: copy may exceed size of buf for (int i = 0; i < SIZE; i++) { //wrong: upper limit that is higher than array size cout << array[i]; } }

场景三:常量下标直接越界

outOfBounds直接比对数组访问的常量下标与数组大小,并做了两个严谨的边界处理:

  • 当access == size时,如果该访问是&buf[size](取地址)或offsetof的一部分,则不告警——因为取越界元素地址(取地址后不解引用)与offsetof是合法惯用法;
  • 只有access > size才无条件告警。

对应测试期望:

|| test.c:14:9:14:13 | access to array | Potential buffer-overflow: 'xs' has size 5 but 'xs[5]' may be accessed here. || test.c:15:9:15:13 | access to array | Potential buffer-overflow: 'xs' has size 5 but 'xs[6]' may be accessed here.

精调度:precision如何决定"默认启用"

CodeQL 查询元数据中的@precision是对查询"可信度"的官方评级,等级从低到高通常为low/medium/high/very-high。high表示该查询具有较低的误报率,足以进入默认启用的安全查询集。

本次 2021-09-27 的变更正是将 OverflowStatic.ql 的@precision提升为high,其直接后果写在了变更说明中:该查询会在 Code Scanning 和 LGTM 上默认运行并展示结果。换言之,在精度升级前,cpp/static-buffer-overflow属于"需要用户主动加入查询集"的查询;升级后,C/C++ 用户只要开启 CodeQL 默认安全查询,即可自动获得该检查。

在仓库中,C/C++ 默认启用查询集的配置位于 cpp/config/suites 目录下的查询套件文件(无扩展名的 suite 文件),CodeQL 会依据@precision、@security-severity等元数据筛选纳入默认集的问题类查询。

演进路径:2021 年内的三次迭代

cpp/static-buffer-overflow在 2021 年内经历了一个典型的"从可用到可信"演进过程,旧变更记录忠实记录了每一步:

第一步(2021-05-18):减少误报。变更 2021-05-18-static-buffer-overflow.md 记载该查询"已被改进以产生更少的误报",标签仅为lgtm,说明当时主要在 LGTM 平台验证。

第二步(2021-09-13):扩展变长字段识别。变更 2021-09-13-overflow-static.md 说明memberMayBeVarSize谓词现在会考虑更多"变长"字段(如 flexible array member 等),从而进一步减少误报。在 OverflowStatic.ql 中,staticBufferBase通过not memberMayBeVarSize(_, v)将可能属于变长结构的变量排除在静态缓冲区判定之外。

第三步(2021-09-27):精度升级为 high 并默认启用。即本篇文章的主题文档,标签升级为lgtm,codescanning,两个平台同步生效。

此外,在 cpp/ql/src/change-notes/released/1.3.2.md 与 cpp/ql/src/CHANGELOG.md 中还能看到后续工程化细节:cpp/static-buffer-overflow等查询不再对"CMake 构建配置测试生成的临时文件"产生告警,这类文件由ConfigurationTestFile谓词识别并在查询末尾统一排除:

not error.getFile() instanceof ConfigurationTestFile // elements in files generated during configuration are likely false positives

检测边界与防误报设计

从源码可以总结出该查询刻意控制的几个边界,理解这些边界有助于准确解读告警:

  1. 只针对字符数组:staticBufferBase要求数组基类型为CharType,避免对数值数组的误报;同时要求访问必须被求值(not access.isUnevaluated()),排除sizeof等不求值语境。
  2. 排除strcmp宏展开:BufferAccess构造器显式排除strcmp宏实现中"看起来危险但实际受控"的访问。
  3. 排除死代码:reachable(this)保证只分析可达路径。
  4. 排除变长结构成员:memberMayBeVarSize排除 flexible array 等场景(这也是 2021-09-13 变更的落点)。
  5. 排除 CMake 配置测试文件:ConfigurationTestFile机制(2021 年 1.3.2 版落实)。
  6. offsetof与取地址例外:access == size时不告警,除非访问被解引用。

测试方面,该查询拥有完整的回归测试体系:

  • 查询引用文件 cpp/ql/test/query-tests/Critical/OverflowStatic/OverflowStatic.qlref 指向Critical/OverflowStatic.ql,并使用InlineExpectationsTestQuery.ql做内联期望校验;
  • 期望文件 cpp/ql/test/query-tests/Critical/OverflowStatic/OverflowStatic.expected 覆盖了test.c(常量越界)、test2.c(大小实参大于缓冲区)与test.cpp(循环越界 + 错误大小参数)三类用例;
  • 安全套件路径下还挂有 SAMATE 参考用例与semmle/tests两套独立测试:cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverflowStatic.qlref、cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowStatic.qlref。

实践建议:如何用这条查询排查静态缓冲区溢出

在 C/C++ 项目中启用 CodeQL 后,cpp/static-buffer-overflow已随默认安全查询集运行。若需人工核对告警,建议按以下顺序排查:

  1. 循环越界类告警:检查循环上限与数组声明大小是否一致,注意#define SIZE 30与char buf[20]这类"宏尺寸与局部声明脱节"的典型模式(参见 OverflowStatic.cpp)。
  2. 错误大小参数类告警:核对strncpy、memcpy、snprintf等调用传入的 size 实参是否超过目标缓冲区实际大小。
  3. 常量下标类告警:检查buf[N]中N是否等于数组长度(有效下标应为0..N-1)。

同时注意该查询的适用范围:它针对静态声明的、大小可静态确定的字符数组;对于堆分配缓冲区、std::vector等动态容器,以及变长结构成员,需要配合 CodeQL 的 CWE-119 套件中其他查询(如 cpp/ql/src/Security 下的相关数据流查询)一起使用。

小结

从 2021 年 5 月的降误报,到 9 月 13 日的变长字段识别扩展,再到 9 月 27 日将cpp/static-buffer-overflow的precision提升至high并默认启用,这条查询的演进完整展示了 CodeQL 安全查询的发布准则:先通过测试与误报控制证明可信度,再以precision元数据进入默认查询集。理解这一机制,既能帮你更准确地解读 C/C++ 项目的 CodeQL 扫描结果,也能为你在 CodeQL 中自研安全查询的"转正"流程提供直接参考。

  • 静态分析
  • 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
点击查看免费下载

相关推荐

上一篇:Polar Python SDK 快速接入指南:用官方客户端连接 Polar 计费平台
下一篇:G-Helper终极指南:让你的华硕笔记本性能飙升的免费神器

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

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

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

立即咨询