news 2026/9/25 5:44:42

CodeQL C++ 静态分析:解析 allocation-too-small 与 suspicious-allocation-size 两条尺寸检查查询

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CodeQL C++ 静态分析:解析 allocation-too-small 与 suspicious-allocation-size 两条尺寸检查查询
  • 静态分析
  • 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/allocation-too-small(为指针类型分配的内存不足)与cpp/suspicious-allocation-size(为指针数组分配的内存不足)——展开。你将了解这两条查询各自的判定逻辑、二者如何做到"同一分配点不再重复告警",以及其背后可模型化的分配函数体系(AllocationFunction)是如何扩展识别范围的。读完后,你可以结合仓库中的源码与测试用例,理解这两条 CWE-131/CWE-122 检查从建模到执行的完整机制,并学会用同样的方式为自己的库函数建立分配模型。

一、变更背景:一次针对重叠告警与覆盖面的改进

该改进对应仓库中的变更记录 size-check-queries.md,原文包含两点:

  • cpp/allocation-too-small(Not enough memory allocated for pointer type)与cpp/suspicious-allocation-size(Not enough memory allocated for array of pointer type)两条查询得到改进。此前同一处内存分配会同时被两条查询报告,改进后不再发生;
  • 两条查询现在能理解更多的分配函数(more allocation functions are now understood by both queries)。

这两点分别对应"报告去重"与"模型覆盖"两个问题。下文结合仓库中的查询源码与模型库,逐一给出实现层面的证据。

二、查询一:cpp/allocation-too-small 的判定逻辑

查询源码位于 SizeCheck.ql,其元数据声明了问题定位与严重度:

@name Not enough memory allocated for pointer type @description Calling 'malloc', 'calloc' or 'realloc' without allocating enough memory to contain an instance of the type of the pointer may result in a buffer overflow @kind problem @problem.severity warning @security-severity 8.1 @precision medium @id cpp/allocation-too-small @tags reliability security external/cwe/cwe-131 external/cwe/cwe-122

其核心判定只有三条:

predicate baseType(AllocationExpr alloc, Type base) { exists(PointerType pointer | pointer.getBaseType() = base and ( exists(AssignExpr assign | assign.getRValue() = alloc and assign.getLValue().getType() = pointer ) or exists(Variable v | v.getInitializer().getExpr() = alloc and v.getType() = pointer) ) ) } from AllocationExpr alloc, Type base, int basesize, int allocated where baseType(alloc, base) and allocated = alloc.getSizeBytes() and decideOnSize(base, basesize) and alloc.(FunctionCall).getTarget() instanceof AllocationFunction and // exclude `new` and similar basesize > allocated select alloc, "Type '" + base.getName() + "' is " + basesize.toString() + " bytes, but only " + allocated.toString() + " bytes are allocated."

可以拆出四个关键点:

  1. 类型关联:baseType通过"分配表达式的返回值被赋给(或用于初始化)某个指针变量"这条路径,把void *分配结果与它实际承载的类型base关联起来;
  2. 尺寸取最小值:decideOnSize用min(t.getSize())处理同名类型可能有多尺寸的情况——如果同一名字在不同翻译单元/重载场景下存在多个尺寸,用最小的作为比较基准,避免漏报;
  3. 排除new:alloc.(FunctionCall).getTarget() instanceof AllocationFunction只保留函数调用形态的分配(malloc/calloc/realloc等),因为new表达式由编译器保证分配尺寸正确,无需检查;
  4. 报告条件:basesize > allocated,即分配字节数小于目标类型大小,直接提示"X 需要 N 字节,但只分配了 M 字节"。

三、查询二:cpp/suspicious-allocation-size 的"整数倍"检查与去重条款

第二条查询源码位于 SizeCheck2.ql,元数据同样为@security-severity 8.1、CWE-131/122,但报告条件不同:

from AllocationExpr alloc, Type base, int basesize, int allocated where baseType(alloc, base) and allocated = alloc.getSizeBytes() and decideOnSize(base, basesize) and alloc.(FunctionCall).getTarget() instanceof AllocationFunction and // exclude `new` and similar // If the codebase has more than one type with the same name, check if any matches not exists(int size | base.getSize() = size | size = 0 or (allocated / size) * size = allocated ) and not basesize > allocated and // covered by SizeCheck.ql not memberMayBeVarSize(base.getUnspecifiedType(), _) // exclude variable size types select alloc, "Allocated memory (" + allocated.toString() + " bytes) is not a multiple of the size of '" + base.getName() + "' (" + basesize.toString() + " bytes)."

它的语义是:分配字节数不是目标类型大小的整数倍((allocated / size) * size != allocated,同时容忍同名类型存在多个尺寸的情况,只要有任何一个尺寸匹配即视为合法)。

其中一行正是变更记录中"不再重复告警"这一点的直接实现证据:

not basesize > allocated and // covered by SizeCheck.ql

这行注释点明了两条查询的分工:

  • basesize > allocated(分配量不足一个元素)的情况归SizeCheck.ql报告;
  • SizeCheck2.ql通过not basesize > allocated显式排除了这个区间,只负责"分配量 ≥ 一个元素但不是整数倍"的数组型误分配。

由此,任意一个分配点至多只会命中两条查询中的一条,从查询逻辑层面保证了同一分配不会收到两份告警。此外还有一个边界处理:not memberMayBeVarSize(base.getUnspecifiedType(), _)排除了含可变长度成员(如 C99 柔性数组)的变长类型,避免对尺寸本身不确定的类型误报。

四、"更多分配函数被理解"背后的模型体系

变更记录第二点——两条查询现在能理解更多分配函数——对应的是 CodeQL C++ 模型库中统一的分配模型:两条查询都import semmle.code.cpp.models.Models,并都以AllocationExpr/AllocationFunction为分析入口。该模型由接口层与实现层两部分组成。

4.1 接口层:可继承、可扩展的抽象

interfaces/Allocation.qll 定义了四个核心抽象:

  • AllocationExpr:一次分配表达式(malloc调用、new表达式等),提供getSizeBytes()(固定字节数,若可确定)、getSizeExpr()+getSizeMult()(长度表达式与常量乘数)、getAllocatedElementType()、requiresDealloc()等钩子;
  • AllocationFunction:分配函数(如malloc),提供getSizeArg()(尺寸参数下标)、getSizeMult()(乘数参数下标)、getReallocPtrArg()(realloc 的原指针参数)等钩子;
  • HeuristicAllocationExpr/HeuristicAllocationFunction:基于启发式(函数名、参数形态)识别"可能"的分配,用于模型外的兜底;
  • extensible predicate allocationFunctionModel(...):外部可扩展模型谓词,允许使用者按"命名空间 + 类型 + 函数名"追加自己的分配函数模型,并指定尺寸参数、乘数参数、realloc 参数下标以及是否需要释放。

4.2 实现层:内建覆盖的分配函数清单

implementations/Allocation.qll 展示了内建模型的覆盖面,这正是"更多分配函数被理解"的落地位置:

  • realloc 家族(ReallocAllocationFunction):realloc(ptr, size),以及 Windows 传统 APILocalReAlloc/GlobalReAlloc/HeapReAlloc、COM 的CoTaskMemRealloc、OpenSSL 的CRYPTO_realloc、GLib 的g_realloc/g_try_realloc;
  • 无尺寸参数的分配(SizelessAllocationFunction):Windows 驱动内存管理中的ExAllocateFromLookasideListEx、MmMapLockedPages系列,NetBSD 的pool_get等——它们没有显式尺寸参数,因此不会进入尺寸比较;
  • new/new[]表达式(NewAllocationExpr/NewArrayAllocationExpr):直接由getAllocatedType().getSize()得出字节数,尺寸天然正确;
  • 外部模型(AllocationFunctionFromModel):把allocationFunctionModel谓词中的声明翻译为AllocationFunction子类;
  • 启发式兜底(HeuristicAllocationFunctionByName):函数名匹配"%alloc%"或"%Alloc%"、返回指针类型、且存在唯一无符号整型参数(视为尺寸参数);名字匹配%realloc%且存在唯一指针参数时,该指针参数被视为 realloc 输入。

实现层还有两个对查询结果准确性很关键的细节:

  1. realloc(ptr, 0)不算分配:CallAllocationExprImpl的构造条件中显式排除"realloc 目标且尺寸参数为 0"的调用,因为该调用语义上是释放而非分配;
  2. sizeof表达式的拆解:deconstructSizeExpr把malloc(a * 2 * sizeof(char32_t))这样的实参拆成"长度表达式a * 2+ 乘数 4",使getSizeBytes()能还原出真实字节数——这保证了用sizeof计算尺寸的合法写法不会因解析失败而漏报。

五、测试用例:用仓库中的测试验证行为边界

两条查询的测试位于 SizeCheck 测试目录,包含test.c、test2.c及对应的.expected期望输出,测试文件头部注释说明其关联 CWE-131,并约定"每个查询只应报告标记为 BAD 的行"。

test.c覆盖cpp/allocation-too-small的典型场景:

void bad0(void) { float *fptr = malloc(3); // $ Alert[cpp/allocation-too-small] // Too small double *dptr = malloc(5); // $ Alert[cpp/allocation-too-small] // Too small } void bad1(void) { float *fptr = malloc(sizeof(short)); // $ Alert[cpp/allocation-too-small] double *dptr = malloc(sizeof(float)); // $ Alert[cpp/allocation-too-small] }

而test2.c覆盖cpp/suspicious-allocation-size的"整数倍"场景:

long long *lptr = malloc(27); // $ Alert[cpp/suspicious-allocation-size] double *dptr = malloc(33); // $ Alert[cpp/suspicious-allocation-size] long long *lptr2 = malloc(sizeof(long long)*7/2); // $ Alert[cpp/suspicious-allocation-size] float *fptr2 = malloc(24); // GOOD -- An integral multiple

期望输出(SizeCheck.expected 与 SizeCheck2.expected)给出了完整的告警文本,例如:

  • Type 'float' is 4 bytes, but only 3 bytes are allocated.(来自SizeCheck.ql)
  • Allocated memory (27 bytes) is not a multiple of the size of 'long long' (8 bytes).(来自SizeCheck2.ql)

值得注意的是两个测试文件的 BAD 行互不重叠:malloc(3)之于float只被SizeCheck.ql报告,malloc(27)之于long long *只被SizeCheck2.ql报告——这正是"不再重复告警"这一改进在测试层面的体现。

六、实践要点与局限

综合查询源码与模型库,使用这两条查询时可以把握以下几点:

  • 适用前提:分配结果必须通过赋值或变量初始化与某个具名指针类型关联,查询才能把分配与类型对应起来;分配尺寸必须是编译期可确定的固定值(getSizeBytes()有解),运行时动态尺寸不会触发报告;
  • new被排除:new/new[]的尺寸由语言机制保证,两条查询只针对malloc/calloc/realloc类函数调用;
  • 同名类型取最小尺寸:若同一类型名在代码库中尺寸不一致,两条查询都以min(t.getSize())为基准,宁可保守(偏向报告);
  • 变长类型被排除:含 C99 可变长度成员的数组类型不参与整数倍检查;
  • 扩展覆盖:如果你使用的库有自己的分配 API(如posix_memalign、g_malloc之类),可以通过allocationFunctionModel谓词的外部扩展声明其尺寸参数与 realloc 参数,使其纳入AllocationExpr体系;也可以依赖实现层的启发式(名字含alloc/Alloc、返回指针、唯一无符号整型参数)自动覆盖一部分场景。

七、小结

这次 2020-10-21 的改进,从结果上看只是"不重复告警 + 识别更多分配函数"两句话,但在 SizeCheck2.ql 中一行not basesize > allocated的去重条款、在 implementations/Allocation.qll 中分层的分配模型(内建清单 + 外部可扩展谓词 + 启发式兜底)中,都有清晰的实现落点。这套"查询声明分工、模型负责覆盖"的结构,也是 CodeQL C++ 查询套件处理类似可靠性/安全问题的典型范式:查询本身保持简洁的类型-尺寸比较,识别能力的扩展则集中在可继承、可外部扩展的模型层完成。

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

相关推荐

上一篇:html-anything 社区/配对数据墙模板解析:dating-web Skill 的布局设计、frontmatter 约定与加载机制
下一篇:Facebook的PyRE2: 高性能正则表达式处理库

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 5:44:03

HCIP-Storage备考:H13-624练习题拆解与实操验证指南

简介:这份HCIP-Storage(存储)H13-624练习题文档,面向备考华为存储认证的考生及希望系统梳理存储知识点的工程师,围绕融合存储、超融合、RAID2.0、容灾备份等核心考点提供针对性训练。内容涵盖并行快速数据重建、超融合…

作者头像 李华
网站建设 2026/9/25 5:41:43

Atlas 300V 24G加速卡实测:从环境搭建到YOLOv5部署全流程

“atlas 300v 24g 是运算加速卡吗”,这个问题如果只看型号名,答案毫无悬念:是。但实际操作一圈之后你会发现,这个“是”字后面藏着很多前提。我最近在一台服务器上装了Atlas 300V Pro 24G,并且把YOLOv5检测模型从PyTor…

作者头像 李华
网站建设 2026/9/25 5:41:22

Chrome 109:Win7/Win8最后的安全兼容版本

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 5:41:12

精益与六西格玛的本质区别及应用场景解析

1. 为什么我们需要分清精益与六西格玛上周和制造业的老王吃饭时,他提到公司刚花大价钱请了咨询公司做"精益六西格玛"培训,结果发现顾问自己都说不清两者的区别,把改善活动搞得一团糟。这让我想起十年前刚接触这两个方法论时踩过的坑…

作者头像 李华
网站建设 2026/9/25 5:41:11

带补偿与爬坡约束的电力市场混合整数均衡问题精确求解方法

电力市场的出清计算,说白了就是在一个巨大的经济调度问题里找平衡点。这几年我做过不少相关的优化项目,最头疼的往往不是连续量的经济调度,而是那些带 0-1 整数变量的均衡问题。尤其当场景里再加上补偿费用、机组上升爬坡约束,问题…

作者头像 李华