GraySoft
Projects Models Compare Cloud benchmarks FAQ Download guIDE →
Model Intelligence Sheet

MBM7/gguf-nested-array-recursion-poc overview

GGUF Nested Array ARRAY of ARRAY Uncontrolled Recursion — RecursionError DoS Status: Preparing for Huntr submission Package: gguf https://pypi.org/project/gguf…

region:us
Downloads
0
Likes
0
Pipeline
Author

Repository Files & Downloads

0 GGUF files detected
Direct downloads for local inference
FileTypeQuantizationSizeLink
Browse files on Hugging Face

Model Details

Model IDMBM7/gguf-nested-array-recursion-poc
AuthorMBM7
Pipeline
License
Base model
Last modified2026-07-20T09:39:39.000Z

Model README

GGUF Nested Array (ARRAY-of-ARRAY) Uncontrolled Recursion — RecursionError DoS

Status: Preparing for Huntr submission

Package: gguf (PyPI) — official gguf-py from ggml-org/llama.cpp

File / function: gguf/gguf_reader.py, GGUFReader._get_field_parts()

Class: CWE-674 (Uncontrolled Recursion)

Severity: Medium — an uncaught, unhandled exception crashes any application calling GGUFReader() on a ~12KB crafted file, with no exception type most code would think to catch.

Summary

When a GGUF key/value metadata field has type ARRAY, GGUFReader._get_field_parts() reads the array's element type and recursively calls itself to parse each element:

if gtype == GGUFValueType.ARRAY:
    raw_itype = self._get(offs, np.uint32)
    ...
    for idx in range(alen[0]):
        curr_size, curr_parts, curr_idxs, curr_types = self._get_field_parts(offs, raw_itype[0])

There is no check that the element type (raw_itype) isn't itself ARRAY, and no recursion depth limit. A GGUF file can declare an array whose elements are arrays, whose elements are arrays, nested as deep as the file's author chooses.

The reference C++ implementation explicitly rejects this. In ggml/src/gguf.cpp's type-dispatch switch:

case GGUF_TYPE_ARRAY:
default:
    {
        GGML_LOG_ERROR("%s: key '%s' has invalid GGUF type %d\n", ...);
        ok = false;
    } break;

Encountering ARRAY as an array's element type is treated as invalid input and cleanly rejected. The Python bindings have no equivalent check.

Proof of Concept

poc_gguf_nested_array_recursion.py builds a series of small, otherwise-valid GGUF files with a single KV field nested to increasing depth, and shows the divergence:

pip install gguf numpy
python poc_gguf_nested_array_recursion.py [path to a compiled llama-gguf binary, optional]

Results

| Nesting depth | File size | Python gguf-py | Native C++ reader |

|---:|---:|---|---|

| 1 | 70 bytes | accepted silently | rejected cleanly ("invalid GGUF type") |

| 10 | 178 bytes | accepted silently | (not tested, same as depth 1) |

| 100 | 1,258 bytes | accepted silently | — |

| 1,000 | 12,058 bytes | RecursionError: maximum recursion depth exceeded | — |

| 5,000 | 60,058 bytes | RecursionError: maximum recursion depth exceeded | — |

Even a single level of nesting is a genuine parity gap (Python accepts what the reference C++ implementation explicitly rejects as malformed), and by depth ~1000 — matching Python's default sys.setrecursionlimit() — it becomes an uncaught crash.

The RecursionError is not caught anywhere in gguf-py; it propagates all the way out of GGUFReader.__init__():

Traceback (most recent call last):
  File "...", line 3, in <module>
  File ".../gguf/gguf_reader.py", line 169, in __init__
    offs = self._build_fields(offs, kv_count)
  File ".../gguf/gguf_reader.py", line 298, in _build_fields
    field_size, field_parts, field_idxs, field_types = self._get_field_parts(offs, raw_kv_type[0])
  File ".../gguf/gguf_reader.py", line 248, in _get_field_parts
    curr_size, curr_parts, curr_idxs, curr_types = self._get_field_parts(offs, raw_itype[0])
  [Previous line repeated 990 more times]
  ...
RecursionError: maximum recursion depth exceeded

Any application calling GGUFReader(path) without a specific except RecursionError handler — unusual, since most file-parsing code anticipates ValueError/OSError, not RecursionError — crashes with an unhandled exception.

Impact

Any service that loads untrusted or third-party GGUF files with gguf-py (model preview/validation services, conversion tooling, CI harnesses testing community-submitted models) can be crashed by a ~12KB file that otherwise looks like an ordinary, if oddly-structured, metadata field.

Suggested fix

In _get_field_parts(), reject ARRAY as a valid array-element type the same way the C++ implementation does (this alone fixes the underlying parity gap and, as a side effect, removes the unbounded-recursion path entirely, since the recursion can only occur through nested arrays).

Relationship to other reports

Same vulnerability class (CWE-674, Uncontrolled Recursion) as a separately-reported finding in protobuf's json_format.ParseDict() — but a completely different library, format, and code path. Also distinct from this reporter's two other gguf-py findings: "KV Array Field Unbounded Length" (CWE-834, iteration-based, not recursion) and "Tensor Data Offset Aliasing" (CWE-1284, a data-integrity issue unrelated to metadata parsing). Also distinct from a separately and independently reported n_dims/GGML_MAX_DIMS parity gap (a different structural issue — per-tensor dimension count vs. this report's array nesting).

Disclosure

Please do not use this PoC against production systems you do not own or have explicit permission to test.

Run MBM7/gguf-nested-array-recursion-poc with guIDE

Download guIDE — the AI-native code editor with local LLM inference and 69 built-in tools.

Download guIDE → · Browse 524k+ models · Compare models

Source: Hugging Face · Compare models