- 静态分析
- SAST
- 应用安全
- 漏洞扫描
- 代码质量
【免费下载链接】codeql
CodeQL: the libraries and queries that power security researchers around the world, as well as code scanning in GitHub Advanced Security
本篇文章围绕 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 安全查询发布流程中的两个关键机制:
- 标签
lgtm,codescanning:表示该变更同时影响 LGTM.com 与 GitHub Code Scanning 两个平台。 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并非单一规则,而是由三个谓词共同构成的三类检测场景:
| 场景 | 对应谓词 | 典型触发代码 |
|---|---|---|
| 循环中数组下标越界 | overflowOffsetInLoop | for (i = 0; i < N; i++) buf[i]且N超过buf大小 |
| 传入错误缓冲区大小的库函数调用 | wrongBufferSize | strncpy(buf, s, SIZE)而SIZE > sizeof(buf) |
| 常量下标直接越界访问 | outOfBounds | buf[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),精确记录"哪个参数是缓冲区、哪个参数是大小":
| 函数 | 缓冲区参数索引 | 大小参数索引 |
|---|---|---|
read | 1 | 2 |
fgets | 0 | 1 |
strncpy | 0 | 2 |
strncat | 0 | 2 |
memcpy | 0 | 2 |
memmove | 0 | 2 |
snprintf | 0 | 1 |
vsnprintf | 0 | 1 |
检测逻辑为:目标缓冲区是静态数组(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检测边界与防误报设计
从源码可以总结出该查询刻意控制的几个边界,理解这些边界有助于准确解读告警:
- 只针对字符数组:
staticBufferBase要求数组基类型为CharType,避免对数值数组的误报;同时要求访问必须被求值(not access.isUnevaluated()),排除sizeof等不求值语境。 - 排除
strcmp宏展开:BufferAccess构造器显式排除strcmp宏实现中"看起来危险但实际受控"的访问。 - 排除死代码:
reachable(this)保证只分析可达路径。 - 排除变长结构成员:
memberMayBeVarSize排除 flexible array 等场景(这也是 2021-09-13 变更的落点)。 - 排除 CMake 配置测试文件:
ConfigurationTestFile机制(2021 年 1.3.2 版落实)。 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已随默认安全查询集运行。若需人工核对告警,建议按以下顺序排查:
- 循环越界类告警:检查循环上限与数组声明大小是否一致,注意
#define SIZE 30与char buf[20]这类"宏尺寸与局部声明脱节"的典型模式(参见 OverflowStatic.cpp)。 - 错误大小参数类告警:核对
strncpy、memcpy、snprintf等调用传入的 size 实参是否超过目标缓冲区实际大小。 - 常量下标类告警:检查
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
相关推荐
CodeQL C/C++ 静态缓冲区溢出查询优化:`memberMayBeVarSize` 变长结构体判定与误报治理
CodeQL C/C++ 静态缓冲区溢出查询优化: memberMayBeVarSize 变长结构体判定与误报治理 导读 本文以 CodeQL 仓库中 C/C+
静态分析SAST应用安全漏洞扫描代码质量CodeQL C++ 命令注入检测深度解析:`cpp/command-line-injection` 查询的高精度化演进
CodeQL C++ 命令注入检测深度解析: cpp/command line injection 查询的高精度化演进 本文围绕 CodeQL C++ 查询库中
静态分析SAST应用安全漏洞扫描代码质量CodeQL C++ 安全查询实战:`cpp/toctou-race-condition` 时间检查与使用竞态检测及其改进
CodeQL C++ 安全查询实战: cpp/toctou race condition 时间检查与使用竞态检测及其改进 本文围绕 CodeQL C++ 查询库
静态分析SAST应用安全漏洞扫描代码质量
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考