llama.cpp 越界写入漏洞CVE-2026-21869
llama.cpp是Georgi Gerganov主导的C++开源 LLM 推理框架,以纯 C++ 无外部依赖、CPU 优先 + 多硬件加速、GGUF 量化生态为核心。
一、基本情况
llama.cpp是在 C/C++ 中实现多个大型语言模型(LLM)代码文件,通过 GGUF 量化与分层加速让大模型在低资源设备本地运行成为可能。

llama.cpp实现资源受限设备(PC/边缘/嵌入式)的本地高效推理,支持 2–8bit 多精度量化与多模型热切换,适配个人到企业服务全场景。
栋科技漏洞库关注到 llama.cpp在提交 55d4206c8 及之前的版本中存在的安全漏洞,追踪为CVE-2026-21869,漏洞CVSS 3.1评分为8.8。
二、漏洞分析CVE-2026-21869 漏洞是存在于 llama.cpp 在 llama-server 中存在的越界写入漏洞,
该漏洞源于 llama.cpp 服务器在解析完成端点的 JSON 输入时,直接从输入中解析 n_discard 参数,但没有进行验证以确保其值非负。
当提供负值时,上下文会填满,导致 `llama_memory_seq_rm/add` 函数接收到反向范围和负偏移量。
这会在令牌评估循环中导致越界内存写入。这种确定的内存损坏可能会使进程崩溃或允许远程代码执行(RCE)。
上下文转移信任客户端的n_discard(注:n_discard可能为特定上下文中的变量或参数)。
远程攻击者可通过公共补全功能提供负值 端点,在切换过程中损坏KV缓存/文本缓冲区,并导致进程崩溃或转向远程代码执行(RCE)。
入口点:/completions、/chat/completions、/slots/(resume)
——任何通过 server_task::params_from_json_cmpl() 函数处理的入口点。
tools/server/server.cpp:326:n_discard直接从JSON解析到slot_params::n_discard中,没有进行非负数检查。
当上下文已满时,
server_context::update_slots()会消耗slot.task->params.n_discard来移位标记(tools/server/server.cpp:3613 ff.)。
负值会导致:
llama_memory_seq_rm/add 接收一个反向范围和负偏移量,使键值(KV)缓存不同步。
在tools/server/server.cpp:3626-3630的循环中,
将new_tokens[i - n_discard]评估为new_tokens[i + |n_discard|],并写入 超出std::vector的末尾。
使用ASan或内置保护机制时,会出现异常终止(pos_min == -1),
但移除这些保护机制后,会出现确定性的越界写入—— 攻击者执行代码所需的确切条件。
三、POC概念验证
先决条件:启动服务器时启用上下文切换(--context-shift)。适用于CPU和GPU构建;以下使用 CUDA+ASan二进制文件。
1、按照标准说明,使用CUDA和ASan构建llama-server。
2、启动它:
CUDA_LAUNCH_BLOCKING=1
ASAN_OPTIONS=abort_on_error=1:detect_leaks=0
LSAN_OPTIONS=detect_leaks=0
LD_LIpARY_PATH=/usr/local/cuda/lib64:$LD_LIpARY_PATH
build-cuda-asan/bin/llama-server
--model /models/DeepSeek-R1-Distill-Qwen-32B-Q2_K.gguf
--ctx-size 128
--port 8080
--context-shift
3、发出恶意请求(无需授权):
管理员已设置登录后刷新可查看4、服务器日志显示插槽上下文发生偏移... n_discard = -32,随后出现KV不一致或ASan OOB报告。在未进行清理的情况下
构建相同的路径会导致内存无声崩溃,并为远程代码执行(RCE)敞开大门。
5、影响
远程未经身份验证的越界写入会导致崩溃或任意代码执行。
每个运行带有--context-shift(CPU或GPU)的HTTP服务器的llama.cpp部署都会受到影响。
攻击者仅需网络访问权限;无需凭证或用户交互。
输出:
This GDB supports auto-downloading debuginfo from the following URLs:
<https://debuginfod.ubuntu.com>
Enable debuginfod for this session? (y or [n]) [answered N; input not from terminal]
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
warning: could not find '.gnu_debugaltlink' file for /lib/x86_64-linux-gnu/liblber.so.2
warning: could not find '.gnu_debugaltlink' file for /lib/x86_64-linux-gnu/libpotlidec.so.1
warning: could not find '.gnu_debugaltlink' file for /lib/x86_64-linux-gnu/libpotlicommon.so.1
[Thread debugging using libthread_db enabled]
Using host libthread_db lipary "/lib/x86_64-linux-gnu/libthread_db.so.1".
0x00007f0c1219d813 in wait4 () from /lib/x86_64-linux-gnu/libc.so.6
#0 0x00007f0c1219d813 in wait4 () from /lib/x86_64-linux-gnu/libc.so.6
#1 0x0000556d78816cd6 in __interceptor_waitpid ()
#2 0x00007f0c12670177 in ggml_print_backtrace () at /workspace/ggml/src/ggml.c:196
196 waitpid(child_pid, NULL, 0);
#3 0x00007f0c1267067a in ggml_abort (file=<optimized out>, line=<optimized out>, fmt=<optimized out>) at /workspace/ggml/src/ggml.c:230
230 ggml_print_backtrace();
#4 0x0000556d78a71d73 in server_context::update_slots (this=0x7f0c0fa01900) at /workspace/tools/server/server.cpp:3835
3835 GGML_ABORT("pos_min == -1, but n_past > 0 - should not happen: https://github.com/ggml-org/llama.cpp/pull/13833#discussion_r2116181237");
#5 0x0000556d7891c8ca in std::function<void ()>::operator()() const (this=0x7f0c0fa03078) at /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/std_function.h:591
591 return _M_invoker(_M_functor, std::forward<_ArgTypes>(__args)...);
#6 server_queue::start_loop (this=0x7f0c0fa02f58) at /workspace/tools/server/server.cpp:2152
2152 callback_update_slots();
#7 0x0000556d788c97c4 in main (argc=<optimized out>, argv=<optimized out>) at /workspace/tools/server/server.cpp:5753
5753 ctx_server.queue_tasks.start_loop();
[Inferior 1 (process 32778) detached]
[1] 32778 IOT instruction (core dumped) ASAN_OPTIONS=abort_on_error=1:detect_leaks=0 LSAN_OPTIONS=detect_leaks=0
四、影响范围
未知
五、修复建议
未知
六、参考链接
管理员已设置登录后刷新可查看