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:

  1. Different tensors' declared byte offsets don't overlap, or
  2. 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 .gguf files (pure stdlib + numpy, no dependency on any GGUF writer library).
  • harness_main.go β€” a small Go program that opens a given .gguf file with the real fs/gguf package 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 older go directive 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.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support