PoC: Denial of Service in gguf (gguf-py) GGUFReader via unchecked ARRAY length
This repository contains a 49-byte proof-of-concept GGUF file that triggers a
non-terminating loop with unbounded memory growth in the official gguf Python
package (gguf.GGUFReader). Loading the file for metadata inspection causes the
process to hang and consume memory until it is OOM-killed.
This artifact is for defensive security research / responsible disclosure only.
Affected
gguf(PyPI) 0.18.0gguf(PyPI) 0.19.0- Upstream
ggml-org/llama.cppgguf-pyatb5f52280fb781cf63e7c3fb79f8bb8de215293e3(verified after the#19856integer-overflow patch).
Root cause
In gguf/gguf_reader.py, _get_field_parts() parses a metadata ARRAY by reading a
64-bit element count straight from the file and looping that many times:
alen = self._get(offs, np.uint64) # attacker-controlled element count
for idx in range(alen[0]): # no bound vs. remaining file bytes
curr_size, curr_parts, ... = self._get_field_parts(offs, raw_itype[0])
aparts += curr_parts # grows every iteration
offs += curr_size # curr_size == 0 once past EOF
For a 1-byte element type (UINT8/BOOL), once offs reaches EOF the element read
returns an empty array (curr_size == 0), so offs never advances and the loop runs
up to alen = 2**64-1 times, appending to aparts each iteration.
Reproduce
pip install gguf==0.19.0
python -c "from gguf import GGUFReader; GGUFReader('poc_array_dos.gguf')"
# never returns; resident memory climbs ~230 MiB/s
Safe, time-boxed demonstration (sampling RSS, aborts after 8s):
python verify_dos.py poc_array_dos.gguf
Expected output: the parser is still running after 8s with RSS having grown >1 GiB
from a 49-byte input. A finite-length control (make_poc.py out.gguf 0x2) returns
in ~0.01s.
Rebuild the PoC
python make_poc.py poc_array_dos.gguf 0xFFFFFFFFFFFFFFFF
Suggested fix
Reject array lengths that exceed the remaining file size (each element is at least
1 byte), and add a progress guard for truncated elements, mirroring the bounds
checks already present in the C++ reader (ggml/src/gguf.cpp):
remaining = self.data.size - offs
if alen[0] > remaining:
raise ValueError(f'array length {int(alen[0])} exceeds remaining file size {remaining}')
...
if curr_size <= 0:
raise ValueError('truncated GGUF ARRAY element')
- Downloads last month
- -
We're not able to determine the quantization variants.