Proof of Concept: Embedded-NUL Metadata Key Canonicalization Mismatch in GGUF Reserved-Key Lookup
This repository contains a deterministic PoC for a GGUF parsing issue in llama.cpp.
The issue is simple: GGUF metadata keys are parsed as length-prefixed strings, but some reserved-key lookups in llama.cpp later use C-string comparison. That creates a mismatch for keys containing an embedded NUL byte. During duplicate validation, keys such as general.alignment and general.alignment\0evil are treated as different. During reserved-key lookup, they collapse to the same logical key.
The result is order-dependent metadata resolution inside llama.cpp. The same GGUF file is also interpreted differently by gguf-py, which keeps exact string semantics throughout.
What This PoC Shows
There are two main cases in generated/.
1. Alignment aliasing
alias_first.gguf contains:
general.alignment\0evil = 32general.alignment = 4096
Observed behavior:
gguf-pyresolves alignment to4096llama.cppresolves alignment to32
Because alignment controls the start of the tensor data section, the two implementations end up reading different payload bytes from the same file.
plain_first.gguf swaps only the metadata order. In that case, both implementations resolve 4096. That shows the llama.cpp result is order-dependent.
2. Another reserved key
architecture_alias_first.gguf contains:
general.architecture\0evil = "AAA"general.architecture = "llama"
Observed behavior:
gguf-pyresolvesgeneral.architecturetollamallama.cppresolvesgeneral.architecturetoAAA
This demonstrates that the issue is not limited to general.alignment.
Why It Happens
At a high level, the bug is:
- parse keys as full strings
- validate duplicates using full-string equality
- later resolve reserved keys using
strcmp()
That last step introduces truncation at the first embedded NUL byte.
Repository Layout
create_poc.pyBuilds the GGUF files from scratch. No external model files are needed.verify_python.pyVerifies behavior withgguf-py.verify_llamacpp.shBuilds a smallgguf_init_from_file()harness and verifies behavior withllama.cpp.verify_payloads.pyConfirms the tensor payload differential.run_all.shRuns the full flow and compares the result withexpected_output.txt.generated/Contains the deterministic GGUF samples.
Reproduction
Prerequisites:
- Python 3
- a local
llama.cppcheckout at../llama.cpp, orLLAMA_CPP_DIR=/path/to/llama.cpp - CMake and a working C++ compiler
Run:
./run_all.sh
That command regenerates the GGUF files, runs the Python-side verification, builds and runs the llama.cpp harness, checks the payload differential, and diffs the combined output against expected_output.txt.
Expected Results
For alias_first.gguf:
gguf-pyreportsalignment = 4096llama.cppreportsalignment = 32gguf-pyreadsPYTHON____PAYLD!llama.cppreadsCPLUSPLUS_PAYLD!
For plain_first.gguf:
- both implementations resolve
alignment = 4096 - both read the same payload
For architecture_alias_first.gguf:
gguf-pyresolvesgeneral.architecturetollamallama.cppresolvesgeneral.architecturetoAAA
Notes
This PoC is focused on the parser differential itself. It does not rely on prompt behavior, tokenizer semantics, or model policy. The generated files are intentionally small and only exercise the metadata and tensor-layout paths needed to show the inconsistency.
- Downloads last month
- 11
We're not able to determine the quantization variants.