尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

CodeQL C++ 新增 cpp/overrun-write 查询:基于 ProductFlow 的缓冲区越界写入检测

CodeQL C++ 新增 cpp/overrun-write 查询:基于 ProductFlow 的缓冲区越界写入检测 静态分析SAST应用安全漏洞扫描代码质量【免费下载链接】codeqlCodeQL: 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 查询包 0.6.3 版本发布说明cpp/ql/src/change-notes/released/0.6.3.md引入的新查询cpp/overrun-write展开深入解析它如何检测 C 风格缓冲区操作函数中的缓冲区溢出buffer overflow。读完本文你将掌握该查询的触发原理、与cpp/overrunning-write系列查询的分工差异、底层依赖的 ProductFlow 与范围分析Range Analysis实现以及如何结合仓库内的示例与测试用例验证查询行为。一、发布说明中的新查询在 cpp/ql/src/change-notes/released/0.6.3.md 中0.6.3 版本的发布说明只有一条核心变更Added a new query,cpp/overrun-write, to detect buffer overflows in C-style functions that manipulate buffers.即新增了cpp/overrun-write查询用于检测“在操作缓冲区的 C 风格函数”中发生的缓冲区溢出。与之配套的正式 CHANGELOG 记录见 cpp/ql/src/CHANGELOG.md。这里有两个值得关注的关键词C 风格函数C-style functions指strcpy、strncpy、memset、sprintf、memcpy等以裸指针/字符数组方式操作缓冲区的 C 库函数而非 C 的std::vector、std::string等自带长度管理的容器。缓冲区溢出buffer overflow写入或访问超出分配范围之外的内存轻则导致段错误segmentation fault重则成为可利用的安全漏洞。二、查询定位cpp/overrun-write是什么2.1 查询元数据该查询的实现文件为 cpp/ql/src/Security/CWE/CWE-119/OverrunWriteProductFlow.ql其头部元数据完整定义了查询的身份信息元数据项值含义nameOverrunning write查询名称descriptionExceeding the size of a static array during write or access operations may result in a buffer overflow问题描述kindpath-problem输出带数据流路径的问题problem.severityerror问题严重级别security-severity9.3安全严重级别CVSS 风格评分precisionmedium检出精度中等意味着存在一定误报/漏报空间idcpp/overrun-write查询唯一标识tagsreliability、security、external/cwe/cwe-119、external/cwe/cwe-131关联 CWE-119缓冲区操作中的内存破坏与 CWE-131缓冲区大小计算错误与常见的problem类查询不同cpp/overrun-write是path-problem查询它不仅报告“哪里溢出”还会呈现从分配源头source到写入点sink的完整数据流路径便于开发者定位根因。2.2 查询套件归属从集成测试的套件清单cpp/ql/integration-tests/query-suite/cpp-security-and-quality.qls.expected 与 cpp/ql/integration-tests/query-suite/cpp-security-extended.qls.expected可以看到OverrunWriteProductFlow.ql同时被cpp-security-and-quality与cpp-security-extended两个套件收录即它默认参与 C/C 代码扫描的安全质量检查。三、工作原理从源码看检测机制3.1 核心依赖OverrunWriteProductFlow.ql顶部导入了以下关键库import cpp import semmle.code.cpp.ir.dataflow.internal.ProductFlow import semmle.code.cpp.ir.IR import semmle.code.cpp.models.interfaces.Allocation import semmle.code.cpp.models.interfaces.ArrayFunction import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeAnalysis import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticExprSpecific import semmle.code.cpp.security.ProductFlowUtils.ProductFlowUtils import semmle.code.cpp.rangeanalysis.new.RangeAnalysisUtil import StringSizeFlow::PathGraph1 import codeql.util.Unit这些依赖揭示了查询的三层机理IR 层基于 CodeQL 的中间表示IR进行分析CallInstruction、LoadInstruction等指令级元素是分析的原子单位。ProductFlow 库这是一个“双投影”数据流框架同时追踪缓冲区指针第一投影与缓冲区大小第二投影两条独立的数据流并将二者关联成“缓冲区 大小”的配对约束。Range Analysis范围分析新式范围分析semmle.code.cpp.rangeanalysis.new用于推导分配大小的数值上下界判断写入长度是否会越界。3.2 源点提取分配大小表达式查询通过hasSize谓词从分配表达式中提取“大小变量”与“偏移量 delta”predicate hasSize(HeuristicAllocationExpr alloc, DataFlow::Node n, int state) { exists(VariableAccess va, Expr size, int delta | size alloc.getSizeExpr() and // Get the unique variable in a size expression like x in malloc(x 1). va unique( | | getAVariableAccess(size)) and // Compute delta as the constant difference between x and x 1. bounded(any(Instruction instr | instr.getUnconvertedResultExpression() size), any(LoadInstruction load | load.getUnconvertedResultExpression() va), delta) and n.asExpr() va and state delta ) }例如对malloc(x 1)查询提取出变量x并计算出常量偏移delta 1作为数据流的状态值state在后续传播中携带。这保证了malloc(size 1)这类“多分配一个元素”的常见防御性写法不会被误判为越界。3.3 汇点识别带大小参数的缓冲区函数isSinkPairImpl0谓词通过ArrayFunction模型识别“缓冲区 大小”成对出现的调用predicate isSinkPairImpl0( CallInstruction c, DataFlow::Node bufSink, DataFlow::Node sizeSink, int delta, Expr eBuf, Instruction sizeBound, Instruction sizeInstr ) { exists(int bufIndex, int sizeIndex, Instruction bufInstr, ArrayFunction func | bufInstr bufSink.asInstruction() and c.getArgument(bufIndex) bufInstr and sizeBound sizeSink.asInstruction() and c.getArgument(sizeIndex) sizeInstr and c.getStaticCallTarget() func and pragmaonly_bind_into .hasArrayWithVariableSize(pragmaonly_bind_into, pragmaonly_bind_into) and bounded(sizeInstr, sizeBound, delta) and eBuf bufInstr.getUnconvertedResultExpression() ) }ArrayFunction是 CodeQL 中为 C 库缓冲区操作函数建立的模型接口定义于 cpp/ql/lib 下的models/interfaces/ArrayFunction.qll它声明了哪些参数是缓冲区、哪些参数是其大小。strncpy(dst, src, n)中dst与n、memset(p, c, n)中p与n均属于这类配对。3.4 状态传播与越界判定查询的核心在于StringSizeConfig这一ProductFlow::StateConfigSig实现。它把“大小”维度设计为一个整数状态FlowState2源端hasSize(bufSource.asExpr(), sizeSource, state2)记录分配时的偏移量。汇点isSinkPair中比较delta state2——即调用处实际传入的大小参数超出分配时携带的大小状态偏移时判定越界。屏障SizeBarrier与isBarrierOut2用于阻断回边back edge避免循环导致的无限数据流与误报。最终的getOverflow谓词计算“越过缓冲区边界访问的元素个数”并输出形如以下的消息This write may overflow$by N elements.从select子句看查询以path-problem形式报告CallInstruction对应的表达式、双源点、双汇点及越界元素数完整呈现从malloc到strncpy/memset的整条路径。四、典型漏洞示例仓库为查询配套了示例文件 cpp/ql/src/Security/CWE/CWE-119/OverrunWriteProductFlow.cppint f(char * s, unsigned size) { char* buf (char*)malloc(size); strncpy(buf, s, size 1); // wrong: copy may exceed size of buf for (int i 0; i size; i) { // wrong: upper limit that is higher than size of buf cout buf[i]; } }两处问题一目了然strncpy(buf, s, size 1)buf只分配了size字节却可能写入size 1字节越界 1 字节。for (int i 0; i size; i)循环访问buf[0..size]共size 1个元素下标i size时越界。官方 qhelp 文档OverrunWriteProductFlow.qhelp给出的修复建议是检查高亮操作中使用的偏移量与大小确保不会发生缓冲区溢出。安全做法是改为strncpy(buf, s, size)并将循环上界改为i size。五、测试用例验证查询行为边界查询的自动化测试位于 cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/对应.qlref为 OverrunWriteProductFlow.qlref测试源码 test.cpp 用注释标注了预期结果$ Alert[cpp/overrun-write]/$ Source[cpp/overrun-write]/GOOD/MISSING是理解查询能力边界的绝佳素材跨函数传递mk_string_t分配缓冲区并保存size到结构体strncpy(str-string, buf, str-size)被判定 GOOD而str-size 1触发 Alert证明查询能跟踪“分配→结构体保存→读取使用”的跨函数流。包装函数strncpy_wrapper场景test2验证流经中间调用层后大小关系仍被保留。条件约束if(anotherSize str-size)保护下的调用是 GOODif(anotherSize str-size 1)则是 BAD——查询对/与1偏移的组合语义判断细致。size与str-size的差异当结构体保存的大小与局部变量size不同源时test3/test4查询对“直接使用局部变量大小”的某些越界如size 1标注MISSING: Alert [NOT DETECTED]如实反映medium精度的漏报边界。分配大小减一test5 中malloc(size - 1)配合str-size size - 1随后strncpy(..., str-size 1)被检出而strncpy(..., str-size)安全。memset与分配语义memset(p, 0, size 1)对malloc(size)的缓冲越界被检出test_flow_through_setter、foorepeated_alerts中循环内size导致的大小变化场景标注NOT DETECTED体现对“大小在循环中可变”这一复杂情况的处理限度。误报抑制test7 中malloc(n)后的memset(p, 0, n)被标注为SPURIOUS: Alert ... GOOD [FALSE POSITIVE]说明当前实现对该场景仍存在已知误报。这些标注让查询的“已知能力”与“已知局限”都以可验证的方式固化在仓库中是评估该查询精度时最重要的第一手资料。六、与同族查询的分工overrun-write 在 CWE-119/CWE-120 家族中的位置cpp/overrun-write并非孤立存在它与 CWE-120缓冲区复制不当下的多个查询构成互补的检测家族查询 ID实现文件特点cpp/overrun-writeSecurity/CWE/CWE-119/OverrunWriteProductFlow.qlpath-problem、ProductFlow 双投影、追踪“分配大小↔写入大小”跨函数流cpp/overrunning-writeSecurity/CWE/CWE-120/OverrunWrite.qlproblem 类基于BufferWrite模型要求getMaxDataLimited估计值超过目标大小cpp/very-likely-overrunning-writeSecurity/CWE/CWE-120/VeryLikelyOverrunWrite.qlproblem 类、precision high仅采用ValueFlowAnalysis得出的高置信估计cpp/overrunning-write-with-floatSecurity/CWE/CWE-120/OverrunWriteFloat.ql专门覆盖%f等浮点格式化导致的极端长度写入从 OverrunWrite.ql 的源码可见cpp/overrunning-write明确排除ValueFlowAnalysis原因的估计“we exclude ValueFlowAnalysis as it is reported in cpp/very-likely-overrunning-write”而 VeryLikelyOverrunWrite.ql 恰恰只接收ValueFlowAnalysis原因——两者互补去重。历史发布说明 cpp/ql/src/change-notes/released/0.0.8.md 也记载了cpp/very-likely-overrunning-write加入默认套件后接管了cpp/overrunning-write的部分结果。相比之下cpp/overrun-write走的是另一条技术路线不依赖BufferWrite模型的“估计字节数”而是通过 ProductFlow 将分配点的大小表达式与调用点的大小参数做符号级关联因此在“结构体保存分配大小、经包装函数间接使用”这类场景下具备更强的跨函数追踪能力。七、如何运行与验证该查询在 CodeQL CLI 环境中可使用查询套件或直接指定查询文件运行# 方式一通过安全套件运行该查询已收录 codeql database analyze database codeql/cpp-queries --formatsarif-latest --outputresult.sarif # 方式二直接指定查询文件 codeql database analyze database \ cpp/ql/src/Security/CWE/CWE-119/OverrunWriteProductFlow.ql \ --formatsarif-latest --outputresult.sarif仓库内验证该查询的推荐方式是 CodeQL 查询测试框架以 OverrunWriteProductFlow.qlref 指向查询文件配合 test.cpp 中的GOOD/BAD标注运行codeql test run即可复现全部预期结果。八、小结cpp/overrun-write是 C 查询包 0.6.3 引入的缓冲区溢出检测利器其独特价值在于path-problem 输出给出从分配到写入的完整路径便于定位根因ProductFlow 双投影同时追踪缓冲区指针与大小值处理malloc(x 1)等带偏移分配、跨结构体/包装函数的大小传播与既有查询家族互补与cpp/overrunning-write、cpp/very-likely-overrunning-write、cpp/overrunning-write-with-float按估计来源与置信度分工降低重复告警。值得注意的边界该查询精度为medium仓库测试明确标注了对“局部变量大小直接 1”与“循环内大小递增”等场景的漏报以及对malloc(n)场景的已知误报。实际部署时建议将其与同族查询尤其是 precision 更高的cpp/very-likely-overrunning-write组合使用并依据测试用例理解告警含义再结合代码评审确认修复方案。赞分享静态分析SAST应用安全漏洞扫描代码质量【免费下载链接】codeqlCodeQL: 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/C 静态缓冲区溢出检测从 cpp/static-buffer-overflow 的精度升级看安全查询工程化CodeQL C/C 静态缓冲区溢出检测从 cpp/static buffer overflow 的精度升级看安全查询工程化 本篇文章围绕 CodeQL静态分析SAST应用安全漏洞扫描代码质量终极窗口管理技巧5分钟掌握跨平台自动化你是否曾经在多个应用窗口间频繁切换却总是找不到想要的那个或者想要自动化管理窗口布局却苦于没有合适的工具今天我要向你介绍一款真正的窗口管理工具——get静态分析SAST应用安全漏洞扫描代码质量CodeQL C 查询 cpp/bad-strncpy-size 0.3.2 更新strxfrm、wcsxfrm、stpncpy 等字符串复制函数纳入缓冲区大小误用检测CodeQL C 查询 cpp/bad strncpy size 0.3.2 更新strxfrm、wcsxfrm、stpncpy 等字符串复制函数纳入缓冲静态分析SAST应用安全漏洞扫描代码质量上一篇helm/charts仓库结构全解stable与incubator的核心区别及Chart发布流程下一篇CopyQ 脚本命令Script Commands完全指南扩展命令行接口与覆盖剪贴板处理函数创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表