Downloads · 30 days
9
24% of all-time downloads
ybgwon96/llamacpp-control-vector-int-overflow-poc
llamacpp-control-vector-int-overflow-poc is a machine learning model from ybgwon96. Use it for the machine learning task on the model card, and read the license before you ship it in a product. The card lists the license as mit.
Proof-of-concept model file for a huntr Model File Vulnerabilities report against ggml-org/llama.cpp @ d4cff11.
Downloads · 30 days
9
24% of all-time downloads
All-time downloads
37
Public
Repo size
—
Likes
0
Public
Click a slice to open those files.
.py3.4 KB · 41%
From the Hugging Face model README
Proof-of-concept model file for a huntr Model File Vulnerabilities report against
ggml-org/llama.cpp @ d4cff11.
This repository contains a deliberately malformed GGUF control-vector file for authorized security research (responsible disclosure via huntr). It is not a usable model.
When llama.cpp loads a control vector (--control-vector foo.gguf), each tensor named direction.<N>
contributes its data at layer offset N, parsed from the tensor name with std::stoi and with
no upper bound. Both the buffer-sizing expression and the destination-pointer expression multiply
n_embd * layer_idx in 32-bit int, which overflows for large N:
// common/common.cpp (common_control_vector_load_one)
:1824 layer_idx = std::stoi(name.substr(dotpos + 1)); // from tensor NAME, no upper bound
:1852 result.n_embd = ggml_nelements(tensor); // int, set by first tensor
:1860 result.data.resize(std::max(result.data.size(), static_cast<size_t>(result.n_embd * layer_idx)), 0.0f);
// ^^^^^^^^^^^^^^^^^^^^^^^ int*int overflow
:1863 float * dst = result.data.data() + result.n_embd * (layer_idx - 1); // int*int overflow -> negative offset
:1865 dst[j] += src[j] * load_info.strength; // OOB WRITE of attacker floats
By choosing N so the product wraps, resize() does not grow the buffer while dst is computed to
lie before the allocation → attacker-controlled heap out-of-bounds (underflow) write. Both the
write offset (tensor name) and the payload (tensor F32 data × strength) are attacker-controlled.
Two 1-D F32 tensors: direction.1 (4 elements → sets n_embd = 4, grows buffer to 4 floats) and
direction.1073741824 (2^30). Then: n_embd*layer_idx = 4*2^30 = 2^32 ≡ 0 (int wrap) → buffer not
grown; n_embd*(layer_idx-1) = 2^32-4 ≡ -4 (int) → dst = data() - 16 bytes → 16-byte heap underflow
write.
| file | description |
|---|---|
poc_ctrlvec_overflow.gguf | valid GGUF control vector with direction.1 and direction.1073741824. |
gen_ctrlvec_poc.py | pure-python generator (no deps). |
llama-cli -m <any-small-model.gguf> --control-vector poc_ctrlvec_overflow.gguf -p "hi"
Build llama.cpp with -fsanitize=address to observe the heap-buffer-overflow at common/common.cpp:1865.
The root cause and 32-bit-wrap arithmetic are verified line-by-line against source; the offset/payload
are fully attacker-controlled.
Not in ggml/src/gguf.cpp (the file covered by the oss-sec May-2026 advisory) — this is consumer/glue
arithmetic in common/common.cpp control-vector loading, unrelated to the GGUF parser itself.
Widen to 64-bit before multiplying and bound-check layer_idx against the model's layer count:
if (layer_idx <= 0 || (size_t)layer_idx > (size_t)n_layers) return; // reject
const size_t off = (size_t)result.n_embd * (size_t)(layer_idx - 1);