GGUF解析器溢出漏洞的静态检测方案

GGUF解析器反复因文件头整数溢出导致越界,可在加载前静态拦截。
GGUF作为主流推理框架的模型容器格式本身没有代码执行面,但其手写的C/C++解析器反复因文件头中的计数值、长度、维度或偏移量触发整数溢出,最终演变为堆越界读写。已公开的漏洞集群涵盖llama.cpp与PowerInfer的7个CVE,触发值全部位于文件头,因此可在加载前静态检测。作者MV2发布了gguf-scan工具、CVE映射表与畸形头部测试语料生成器,形成一套低成本、可复现的头部校验方案。工具通过GG003、GG004、GG009、GG011等规则与漏洞对应,但存在明确边界:PowerInfer数据偏移仅部分覆盖,且扫描通过不代表文件安全,运行时补丁仍需持续跟进。
GGUF解析器溢出漏洞的静态检测方案
GGUF作为llama.cpp、Ollama、LM Studio等主流推理框架的模型容器格式,本身并无代码执行面——它不含pickle序列化,也没有嵌入式脚本,打开文件并不会"运行"任何东西。但问题出在解析器层:手写的C/C++ ggml读取代码及其衍生实现,一次次因畸形文件头中的计数值、长度值、维度值或偏移量触发整数溢出,最终演变为堆越界读写。

一个已经公开的漏洞集群
这些漏洞有一个共同特征:触发值全部位于文件头中。这意味着一个加载前的静态检查,完全可以在解析器接触文件之前就读取文件头并标记出危险信号。
据开发者MV2(Munim, Inc.)整理的公开数据显示,目前已披露的相关漏洞构成一个可辨识的集群。按CWE分类,这些漏洞几乎全部集中在整数溢出引发的越界访问上:
- CVE-2024-21825:llama.cpp GGUF数组/字符串解析中的元数据数组长度溢出;
- CVE-2024-23496:llama.cpp
gguf_fread_str中的元数据字符串长度溢出; - CVE-2024-23605:llama.cpp
n_kv处理中的元数据KV计数溢出; - CVE-2025-53630:llama.cpp
gguf_init_from_file_impl中的张量计数(尺寸累加)溢出; - CVE-2026-27940:llama.cpp
mem_size中绕过上述53630修复的张量计数溢出; - CVE-2026-33298:llama.cpp
ggml_nbytes()中的张量维度溢出; - CVE-2026-108955:PowerInfer
gguf_init_from_file中的张量数据偏移溢出。
这些漏洞的共同点值得注意:它们不是随机分布的,而是反复出现在同一类代码路径上——文件头字段的解析与累加计算。换句话说,整个攻击面可以被归结为"文件头中的整数"。
GGUF文件头结构简述:GGUF文件以固定魔数(0x46554747,即"GGUF")开头,随后依次是版本号(uint32)、张量数量(uint64)、元数据KV对数量(uint64),再跟一段连续的KV元数据区,然后是张量信息区(每项包含名称、维度数、各维长度、类型、数据偏移),最后才是张量数据区。整个攻击面集中在这些头部字段上:n_kv、n_tensors、字符串长度、维度数组长度、各维尺寸以及张量偏移量,全部是整数。当解析器对这些值做累加、乘法或用作分配/拷贝长度时,若缺少溢出检查,就会算出错误的小值或负值,导致后续按错误尺寸访问缓冲区。理解这个布局,就能理解为什么"只读头部"的扫描器可以覆盖这整类漏洞。
静态扫描工具的设计思路
针对上述模式,MV2发布了名为gguf_scan.py(命令行命令为gguf-scan)的头部校验工具,采用Apache-2.0许可,仅依赖标准库。它的核心设计原则是:只解析GGUF文件头,绝不加载或执行模型。
配套发布的还有两份材料。gguf_cve_map.json与GGUF_CVE_MAP.md(CC BY 4.0许可)将每一条公告映射到具体溢出的头部字段、对应的CWE编号,以及能检测该漏洞的扫描规则编号。据作者称,这是该集群首个整合且可复现的映射表。gguf_cve_corpus.py(Apache-2.0)则负责生成仅包含畸形文件头的测试文件。
在CI中集成的成本极低,只需一行:
gguf-scan ./my-model.gguf --fail-on HIGH
如果要对自己的GGUF解析器做回归测试:
python gguf_cve_corpus.py --out-dir DIR
该命令会为映射表中的每一行生成一个极小的、仅含文件头的.gguf文件,外加一个零对齐的纵深防御用例。任何实现了正确边界检查的读取器都应拒绝这些文件。
整数溢出如何演变为堆越界:以CWE-190(整数溢出/回绕)为例,解析器常用uint64_t total = n * size来累加张量所需内存。若n与size都来自文件头且乘积超过UINT64_MAX,结果会回绕成一个很小的数;解析器据此分配一小块堆内存,随后按n个元素写入,即发生堆缓冲区溢出(CWE-787)或越界读(CWE-125)。另一种常见情形是有符号整数溢出:int型长度字段被赋一个接近INT_MAX的值,加上偏移后变为负数,绕过长度校验后被作为size_t传入memcpy,同样越界。CWE-787与CWE-125正是这类漏洞的常见归类,其根因则是缺少前置的溢出检查。
映射规则与实际覆盖情况
工具通过若干规则(GG003、GG004、GG006、GG009、GG011等)与漏洞一一对应。例如GG003对应张量计数/尺寸累加类溢出,GG004对应元数据字符串长度溢出,GG009对应张量维度溢出。
你可能没注意到,PowerInfer那个案例只被部分覆盖:GG011仅能标记未对齐的数据偏移,而对数据区域的完整边界检查仍是一个已知缺口。这一点作者在文档中如实标注,没有掩饰。
部分覆盖的含义:所谓"部分覆盖",指扫描器只能检测某一类可静态判定的条件,而无法证明对应代码路径在运行时是安全的。以PowerInfer的gguf_init_from_file为例,GG011可以标记出数据偏移未按张量类型所需对齐方式对齐的情况,从而提示潜在风险;但真正是否越界,还取决于后续mmap/fread的长度计算与实际文件长度是否匹配。如果偏移恰好落在合法范围内却指向错误的数据区,或与其他字段组合后才越界,静态头部扫描无法单独判定。这就解释了为什么作者将某些CVE标为部分覆盖,而未夸大工具的检测能力。
工具的诚实边界
作者对工具有着清晰的自我限定,这几点对使用者来说同样重要:
- 所有列出的漏洞均为已公开信息,不涉及任何零日漏洞,也没有披露任何新的漏洞细节;
- 构造的测试文件仅有文件头,不含张量数据,也不含利用载荷,只是复现了畸形的头部取值;
- 扫描通过不代表文件安全。它仅意味着文件头通过了这些特定检查,并不能证明文件本身或其权重是安全的,也不能证明你的加载器没有其他缺陷。运行时补丁仍需持续跟进;
- 覆盖范围仅限于文件头层级,且PowerInfer的数据偏移案例只是部分覆盖。
这套工具的价值不在于"消灭"所有GGUF安全风险,而在于把一类反复出现的、可被精确建模的漏洞,在企业或个人的模型加载流水线中前置拦截。在模型文件来源日益多样化的当下,这种"加载前拦截"的思路显然有其现实意义。
代码仓库与映射表可在GitHub上的munimtechnologies/keras-model-scan项目获取。
相关推荐

Meta与出版商的AI数据之争:服务条款修改引爆互联网未来之战
Meta修改商业账户服务条款,允许用关联网站数据训练AI模型,引发出版商和创作者强烈反弹。媒体行业组织联名致信扎克伯格,Meta被迫删除"训练"措辞并推迟条款生效。这场争议揭示了AI时代数据授权与互联网未来的深层博弈。

当Outside Xbox遇上血染钟楼:Minecraft里的军团对局
Outside Xbox嘉宾Ellen和Mike做客Minecraft版《血染钟楼》,迎战军团恶魔局。三名好人如何在信息噪音中对抗多数邪恶?本文解析军团机制、信息角色博弈与终局逆转。

Kimi K3.1实测:开源模型逼近Opus,视频生成意外抢镜
Kimi K3.1在直播实测中完成运动图形、3D渲染、小游戏与视频生成等复合任务。本文分析其2.8万亿参数MoE架构的真实表现、与Opus/GPT的体感差距,以及被官方忽略的视频生成能力,并讨论多模型并用时代的工作流变化。