By UNST33R
llama.cpp CVE-2026-116096: the mmproj file is part of your attack surface
An out-of-bounds write in llama.cpp's Pocket-TTS component, triggered by a malicious mmproj file, with a public exploit. Timeline, auxiliary-file attack surface table, local verification script, and what red-team operators should check.
A new llama.cpp vulnerability published as CVE-2026-116096 underscores an operational fact for anyone running open-weight models locally: a downloaded model directory contains multiple binary and structured artifacts that the C++ runtime parses at startup with full process privileges. The bug surfaced on r/LocalLLaMA on October 26 (PSA thread, 73 upvotes within hours) and entered our monitoring queue the same morning.
What the CVE says
Per the NVD entry, the vulnerable function is list_pockettts_state_slots in tools/mtmd/models/pockettts-gen.cpp, part of the Pocket-TTS multimodal/audio component of ggml-org/llama.cpp. Passing a crafted mmproj (multimodal projector) file triggers an out-of-bounds write during slot enumeration. A proof-of-concept exploit is already public, and the trigger path requires only local file loading: pass the runtime a malicious projector file and memory corruption occurs inside your local inference process.
| Field | Detail |
|---|---|
| Identifier | CVE-2026-116096 |
| Component | ggml-org/llama.cpp — Pocket-TTS (tools/mtmd/models/pockettts-gen.cpp) |
| Vulnerable function | list_pockettts_state_slots |
| Bug class | Out-of-bounds write via crafted --mmproj input file |
Mitigation on master | Builds around b11871 abort via GGML_ASSERT on the malformed state-slot path |
| Root-cause fix | Pull request #29360 bounds-checks the slot index before writing |
The timeline as tracked
Reconstructed from the NVD record, repository history, and the community PSA thread:
- NVD publication: the advisory lands with file- and function-level detail (
tools/mtmd/models/pockettts-gen.cpp,list_pockettts_state_slots) and notes that a public exploit exists. - r/LocalLLaMA PSA (October 26): the warning thread highlights that
mmprojfiles ship alongside popular multimodal GGUF quantizations and rarely get hashed or audited separately. - Assert mitigation on
master: recent builds (aroundb11871) convert the out-of-bounds path into a hardGGML_ASSERTabort, turning silent memory corruption into a deterministic crash. Users on older binaries remain exposed to arbitrary out-of-bounds writes. - Root-cause bounds fix: pull request #29360 addresses the underlying write in
list_pockettts_state_slots. Distro-packaged binaries, Homebrew bottles, and pinned Docker base images lag upstreammasteruntil rebuilt.
The practical triage takeaway: exposure depends on both your llama.cpp build revision and whether your workflow passes third-party --mmproj files to llama-mtmd-cli, llama-server, or wrapper tools that bundle libmtmd.
Why mmproj sits inside the trust boundary
A multimodal or speech-capable model directory typically contains a primary .gguf weights file plus an mmproj-*.gguf projector file that maps visual or audio embeddings into the language model's token space. At startup, the loader parses tensor headers, metadata key-value pairs, and state-slot tables from both files inside the same OS process.
Teams carefully choose, benchmark, and compare the primary .gguf weights, while the accompanying projector file rides along unexamined from whichever quantization mirror happened to bundle it. Every auxiliary artifact in a model directory shares that same privilege boundary:
| Artifact in model directory | Typical filename | What parses it | Failure mode when untrusted |
|---|---|---|---|
| Primary model weights | model-Q4_K_M.gguf | gguf_init_from_file / tensor loader | Malformed tensor offsets or metadata integer overflows |
| Multimodal projector | mmproj-model-f16.gguf | mtmd / pockettts-gen.cpp (--mmproj) | Out-of-bounds write in projector slot setup (CVE-2026-116096) |
| GBNF grammar constraints | json_tool.gbnf | llama_grammar_init (--grammar-file) | Parser recursion exhaustion or state-table corruption |
| Jinja chat templates | tokenizer_config.json / embedded GGUF KV | Template engine (--chat-template-file) | Silent prompt/tool-call macro injection into agent turns |
The threat-model debate, both sides
The top-voted comment in the PSA thread (80+ upvotes) dismissed local file triggers as a non-issue on the grounds that running untrusted model files locally already hands control to the uploader. A sharper counter-argument in the thread pointed out the operational reality: local LLM workflows routinely download multi-file repositories from community mirrors (especially during the October Hugging Face takedown wave), and operators treat GGUF directories as passive data rather than executables.
Both perspectives capture part of the truth. Treat every file passed to a C/C++ tensor parser as untrusted binary input. When an operator verifies the SHA-256 of the main 20 GB .gguf against an upstream release table and grabs mmproj-f16.gguf from an unverified community re-upload, the unverified sidecar file becomes the easiest path into the workstation.
Model file supply chains, again
This vulnerability follows the same structural pattern as the LiteLLM 1.107.0 backdoor incident and the broader abliterated ecosystem supply analysis: in open-weight tooling, auxiliary artifacts between you and the base weights receive scrutiny proportional to their visibility rather than their OS privileges. Because mmproj sidecars attract almost no manual review, parser bugs in multimodal loaders carry low detection risk for an attacker.
Verification script and checklist for local runs
Before loading a downloaded model directory with llama-server or llama-mtmd-cli, lock down the binary version and record a full-directory manifest:
# 1. Check your llama.cpp build number (ensure >= b11871 or patched with PR #29360)
llama-server --version
# 2. Hash EVERY artifact in the model directory, including mmproj and grammars
find ./model-dir -type f \( -name "*.gguf" -o -name "*.gbnf" -o -name "*.json" \) \
-exec shasum -a 256 {} + | sort -k 2 > ./model-dir/SHA256SUMS.manifest
# 3. Verify manifest before launching inside an unprivileged container without host-home mounts
shasum -a 256 -c ./model-dir/SHA256SUMS.manifest && \
docker run --rm --network none --read-only \
-v "$(pwd)/model-dir:/models:ro" \
ghcr.io/ggml-org/llama.cpp:server \
-m /models/model-Q4_K_M.gguf --mmproj /models/mmproj-f16.gguf
Checklist for local lab environments:
- Fetch model directories from one canonical source and log the exact commit SHA of the Hugging Face or mirror repo.
- Hash every file in the directory (
.gguf,mmproj,.gbnf,.json) into a versioned manifest. - Run first-time loads inside a network-isolated container or disposable VM with read-only mounts.
- Pin your
llama.cppbuild revision and rebuild once PR #29360 is merged into your release track. - Strip
--mmprojflags from launch scripts whenever a workload only requires text completion.
Assessing someone else's local LLM deployment
If you red-team internal LLM deployments for clients, CVE-2026-116096 provides a concrete audit check. Ask the engineering team for their model artifact manifest and inspect three areas:
- The hash gap: teams frequently record SHA-256 digests for the primary
.ggufweights while leavingmmproj, grammar, and template sidecars unhashed. - The provenance gap: auxiliary projector files are often copied from secondary quantization repos when the primary uploader omits them, mixing trust domains inside one directory.
- The sandbox gap: developers test new multimodal checkpoints directly on corporate laptops holding SSH keys, cloud credentials, and engagement notes.
Each gap maps directly to a remediation item: enforce whole-directory manifest verification and isolate the C++ model loader from workstation credentials.
FAQ
Am I affected if I only run pure-text GGUF models? The vulnerable list_pockettts_state_slots path sits in the Pocket-TTS multimodal/audio component (tools/mtmd), so direct exposure requires loading an mmproj file. The broader control still applies: any file parsed by gguf_init_from_file or llama_grammar_init exercises C/C++ parsing code on your host.
Should I pull llama.cpp from master right now? Track the merge of PR #29360 for the bounds-checked fix, and ensure any interim build is at least at the b11871 GGML_ASSERT milestone while running inside a container sandbox.
Where a hosted endpoint changes the surface
Calling open-weight models through an OpenAI-compatible API removes local GGUF and mmproj binary parsing from operator workstations: your harness sends HTTPS JSON requests to https://api.unst33r.si/v1, and the inference provider owns artifact verification and container isolation. Teams that combine local R&D labs with hosted red-team harnesses should enforce the manifest checklist above on local GPUs and route automated agent sweeps through fixed hosted tiers (unst33r-flash, unst33r-deepseek, and unst33r-pro). Review our model catalog, per-token pricing, and acceptable-use policy for hosted deployment details.