GGUF 模型文件是不可信输入:llama.cpp 解析器反复出现的 RCE
CVE-2026-33298(2026 年 3 月)与 2026 年 5 月 15 日的一份 oss-sec 披露表明,llama.cpp 的 GGUF 解析器接连出现整数溢出导致的堆破坏:加载一个被构造的模型文件就可能执行代码。
摘要 驱动本地大模型的文件格式 —— GGUF —— 在 llama.cpp 及其
ggml内核中由内存不安全的 C/C++ 代码解析。CVE-2026-33298 于 2026 年 3 月 18 日公开(CVSS 7.8),是张量尺寸计算中的整数溢出:一个被构造的模型文件即可破坏堆,并可能执行代码。这并非首次:CVE-2025-53630(2025 年 7 月)属于同一类问题,而一份 日期为 2026 年 5 月 15 日 的完全披露公告又列出了 GGUF 解析器的另外六处弱点。教训是结构性的:模型文件是不可信输入,而加载器是一个攻击面。
这是什么?
GGUF 是在本地运行的大语言模型事实上的容器格式。当您从 Hugging Face 拉取一个量化模型,并用 llama.cpp、Ollama、LM Studio 或任何基于 ggml 库构建的工具运行它时,这个 .gguf 文件会在任何推理开始之前,先由您机器上的 C/C++ 代码解析。
CVE-2026-33298 在 GitHub 安全公告 GHSA-96jg-mvhq-q7q7 中于 2026 年 3 月 18 日披露(CWE-122 / CWE-190,CVSS 7.8,AV:L/AC:L/PR:N/UI:R),是 ggml_nbytes 函数中的整数溢出 —— 该函数负责计算一个张量需要多少字节。带有伪造张量维度的文件会使这个计算发生回绕,于是解析器分配的缓冲区远小于张量实际所需的大小。看似一个算术 bug,实则变成了内存破坏。
真正值得关注的是它的反复出现。同一类溢出在一年前就影响过 gguf_init_from_file_impl,即 CVE-2025-53630(2025 年 7 月 10 日公开);而在 2026 年 5 月 15 日,一位独立研究者在 oss-sec 上发布了一份完全披露公告,描述了 gguf.cpp 和 Python 参考实现 gguf_reader.py 中另外六处弱点 —— 从对齐值缺少上界,到未经校验的枚举字段和字符串长度字段。
工作原理
所有这些缺陷的模式都一致。某个大小或偏移量直接由攻击者控制的文件中读取的值计算得出。在 64 位主机上,将较大的张量维度相乘会使 size_t 溢出并回绕到一个很小的数值;随后加载器便依据这个小数值来确定堆分配的大小。当代码之后按张量真实(大得多)的范围去复制或索引它 —— 或顺着一个指向欠分配缓冲区之外的张量 offset 访问 —— 就会越界读写。这一堆上的越界写入正是攻击者据以破坏相邻结构、并在最坏情况下劫持执行流的原语。
CVSS 向量对分级很重要:该攻击是本地的,且需要用户交互(UI:R)。没有人能远程触及您的进程 —— 触发条件正是您去加载该文件。但在本地大模型的世界里,下载陌生人的 GGUF 量化文件并加载它是一种日常操作,往往只需一行 pull 即可自动完成。文件被当作惰性数据;而解析器却对其中的字段照单全收。
为何重要
ggml 和 llama.cpp 位于本地 AI 生态相当大一部分的底层 —— Ollama、LM Studio、GPT4All 以及许多更小的工具都内嵌了相同的解析代码。因此,GGUF 解析中的弱点并非某个产品的 bug,而是一个共享依赖的 bug,被其下游的一切所继承。又因为社区模型平台让发布和分享 GGUF 文件变得轻而易举,一个恶意模型的分发渠道早已建好,并被用户广泛信任。
这与 Python 中基于 pickle 的检查点长期以来的教训如出一辙:模型工件与代码相邻,并非被动数据。 「格式上安全」的努力(例如避免使用 pickle)解决的是代码反序列化的问题,却并不能让二进制解析器本身在内存上安全。CVE-2026-33298 及其同类表明,即便是一个「纯数据」格式,一旦它的解析器算错了字节数,也会变得危险。
防御措施
- 更新。 CVE-2026-33298 已在 llama.cpp 构建 b7824 中修复。如果您分发或内嵌
ggml/llama.cpp,请基于较新版本,并跟踪其安全公告 —— 这一类问题会反复出现,因此更应固定版本并持续监视,而非一修了之。 - 将 GGUF 视为不可信。 仅从您信任的发布者处加载模型;在加载前校验校验和与来源。来自未知上传者的社区量化文件,理应像对待未签名的二进制文件一样加以警惕。
- 隔离加载器。 在一个最小化容器中运行模型加载与推理,不授予对机密或敏感数据存储的访问权限,并限制出站流量。施加内存与 CPU 限额,使溢出或内存耗尽退化为崩溃,而非横向移动的跳板。
- 尽可能在加载前校验。 优先选择经过维护、会针对文件实际大小校验张量尺寸、偏移量、对齐和字符串长度边界的加载器。如果您在
ggml之上构建工具,请对 GGUF 解析路径做 fuzzing,并在每一处尺寸计算中使用带溢出检查的算术(例如__builtin_mul_overflow)。 - 监控。 模型加载过程中出现的段错误、sanitizer 中止或异常的内存激增,都是有用的狩猎信号:某个文件也许是想破坏解析器,而非被它正常加载。
状态
| 项目 | 参考 | 日期 | 备注 |
|---|---|---|---|
| CVE-2026-33298 | GHSA-96jg-mvhq-q7q7 | 2026-03-18 | ggml_nbytes 整数溢出,堆溢出,CVSS 7.8,已在 b7824 修复 |
| oss-sec GGUF 公告 | seclists.org(V-01…V-06) | 2026-05-15 | gguf.cpp / gguf_reader.py 解析器另外六处弱点,完全披露 |
| CVE-2025-53630 | GHSA-vgg9-87g3-85w8 | 2025-07-10 | gguf_init_from_file_impl 中同类溢出,堆越界读写 |
正确的框定不是「修补某一个 CVE」。而是 ggml/llama.cpp 中的 GGUF 解析已反复产生同一类「整数溢出 → 堆破坏」问题 —— 因此任何会从其信任边界之外加载模型文件的技术栈,都应加入不依赖解析器把每一处尺寸都算对的来源校验与隔离措施。