YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
Ollama GGUF Parser β Tensor Offset Aliasing and Duplicate-Name Confusion
Status: Preparing for Huntr submission
Target: ollama/ollama β fs/gguf package (fs/gguf/gguf.go)
Class: CWE-1284/CWE-20 (missing structural invariant) + CWE-706 (Use of Incorrectly-Resolved Name)
Severity: Medium-High β silent model-integrity violation, not a crash.
Summary
Ollama's own Go GGUF parser (fs/gguf/gguf.go, the newer of two in-repo implementations, with explicit MaxStringLength/MaxArraySize/MaxTensorDims hardening already in place) has no validation that:
- Different tensors' declared byte offsets don't overlap, or
- Tensor names are unique.
Both gaps allow one tensor's real data to become silently unreachable β replaced, without any error, by a different tensor's data.
Bug 1 β Tensor Offset Aliasing
(*File).readTensor() reads each tensor's offset directly from the file with no check against other tensors' declared ranges:
offset, err := read[uint64](f)
...
ti := TensorInfo{
Name: name,
Offset: offset,
Shape: shape,
Type: TensorType(type_),
}
Two tensors with different names, shapes, or types can be crafted to declare the same (or overlapping) offset. Confirmed with a 192-byte file: blk.0.attn_q.weight (real data 1,2,3,4) and blk.0.attn_k.weight (declared data 9.9,9.9,9.9,9.9, patched offset to alias the first tensor). Reading both via the public TensorReader() API returns identical bytes for both β attn_k.weight's real data is completely unreachable.
Bug 2 β Duplicate Tensor Name Confusion
Tensors are stored in a plain slice (tensors []*Tensor, via append), with no uniqueness check on Name. (*File).TensorInfo(name) resolves lookups via slices.IndexFunc, which always returns the first match:
func (f *File) TensorInfo(name string) TensorInfo {
if index := slices.IndexFunc(f.tensors.values, func(t TensorInfo) bool {
return t.Name == name
}); ...
}
Two tensors with the same name but different, non-overlapping, real offsets and real data are accepted with no error. Confirmed: duplicate_name (offset 0, data 1,2,3,4) and duplicate_name (offset 32, data 9,9,9,9) β TensorReader("duplicate_name") always returns the first tensor's bytes, for both logical entries. The second tensor's real data is unreachable via any name-based access, which is the normal way this API is used.
Notably: neither the Python reference implementation (gguf-py) nor the native C++ reference implementation (ggml-org/llama.cpp) protects against offset-aliasing either β but both of those explicitly reject duplicate tensor names outright. Ollama's Go implementation validates neither.
Proof of Concept
poc_ollama_gguf_aliasing.pyβ builds the two crafted.gguffiles (pure stdlib + numpy, no dependency on any GGUF writer library).harness_main.goβ a small Go program that opens a given.gguffile with the realfs/ggufpackage and prints, for each tensor, its declared offset and the actual bytes read via the public API β showing the aliasing/confusion directly. Includes setup instructions for both a standard Go >= 1.26 environment and a workaround for older toolchains (the package has zero external dependencies, so it can be tested via an isolated copy with an oldergodirective without modifying any logic).
python3 poc_ollama_gguf_aliasing.py
go run harness_main.go go_poc_offset_alias.gguf
go run harness_main.go go_poc_duplicate_name.gguf
Results
=== go_poc_offset_alias.gguf ===
[0] name="blk.0.attn_q.weight" declared_offset=0 actual_bytes_read=0000803f000000400000404000008040
[1] name="blk.0.attn_k.weight" declared_offset=0 actual_bytes_read=0000803f000000400000404000008040
(identical bytes for both, despite different names/declared shapes)
=== go_poc_duplicate_name.gguf ===
[0] name="duplicate_name" declared_offset=0 actual_bytes_read=0000803f000000400000404000008040
[1] name="duplicate_name" declared_offset=32 actual_bytes_read=0000803f000000400000404000008040
(second entry's declared offset is 32, but reading by name still returns the first tensor's bytes)
Impact
A GGUF file loaded by Ollama can declare a full, plausible-looking set of named weight tensors while several of them silently alias or shadow each other's data β the file "loads successfully" with no error, but some declared tensors' real weight data is unreachable. This can be used to disguise an incomplete, corrupted, or backdoored model as a complete one, and can cause silently incorrect inference results with no diagnostic signal.
Suggested fix
In readTensor() (or wherever tensors are appended to f.tensors), track allocated [offset, offset+size) ranges and reject overlaps; separately, reject duplicate tensor names during parsing, matching the behavior already present in both the Python and C++ reference implementations.
Disclosure
Please do not use this PoC against production systems you do not own or have explicit permission to test.