A parser defect: one chunk type is not fixed-stride, and over-reads

[75 42 ef f2 71 b5] -- the signature that is both level-specific and
variable-length -- makes three chunks per level, 81 total:

    stride 76   deltas [76, 76, 76, 76, 76, 76, 76, 76]    uniform
    stride 92   deltas [92, 88, 84, 92, 92, 92, 84, 88]    NOT uniform
    stride 104  deltas [104, 104, 104, ...]                uniform

Identical on all 27 levels: 27 chunks of each shape, three shapes, no others.

So "this signature has strides {76, 92, 104}" is a misdescription. It has three
chunk shapes, one of which is not fixed-stride at all. The parser derives one
stride per chunk and resolve slices every record at that length.

The cost, precisely: records are read at the offset table's positions, so every
record STARTS correctly -- no misalignment. But each is sliced at 92 bytes, while
two of the eight are 88 and two are 84, so four records over-read by 4 and 8
bytes and carry the head of the next record inside them. Bounds checking holds;
this is a data-correctness bug, not a safety one. It is precisely the kind that
produces plausible-looking fields offset by four bytes, which is how it would
have gone unnoticed had the stride table not been collected per signature rather
than inferred per region.

It also explains why this signature was previously flagged as breaking the 8-byte
alignment rule: its records genuinely are not a uniform multiple.

The right fix is for resolve to use offset deltas for a chunk whose deltas are
not uniform. Not taken here -- the delta sequences are asserted first, so if the
data ever becomes uniform the test fails and asks for the model to be revisited,
rather than the model quietly absorbing the exception.

Asserted in chunk_type_75_ef_has_one_variable_length_chunk_per_level, with the
exact sequences [76x8], [92,88,84,92,92,92,84,88], [104x8] and 27 of each.

A near-miss worth recording, because it would have made a tidy false claim: the
leading u16 of these records appeared to step by exactly 0x50 (80) across all nine
of the 76-byte chunk. Reading at the real offsets gives b1a5, a529, 047e, a529,
04f0, a529, 0562, a529, 05d4 -- no progression at all. The apparent 80-byte step
came from comparing the wrong bytes, and "records are indexed by 80" is exactly
the kind of finding that gets reported on sight.

243 tests, clippy clean.
This commit is contained in:
2026-10-13 07:46:57 +02:00
parent af62032ddc
commit b9e9835639
3 changed files with 152 additions and 1 deletions
+8 -1
View File
@@ -477,7 +477,14 @@ Phase 1 hands over the extents; this phase works out what each one contains.
rather than level content -- asserted. Consequence for method: the 65
cannot be decoded by comparing levels, because there is nothing to compare.
`[75 42 ef]` is both level-specific *and* the three-stride type, so it is
where per-type decoding should start. The body
where per-type decoding should start -- and decoding it found a **parser
defect**: it makes three chunks per level, one of which has record offsets
stepping `92, 88, 84, 92, 92, 92, 84, 88` on every level. `resolve` slices
every record at the chunk's single stride, so four of the eight records
over-read by 4 and 8 bytes and include the next record's head. Records
still *start* correctly and bounds checking holds, so it is a data-correctness
bug, not a safety one. The "strides {76, 92, 104}" description is a
misdescription of three chunk shapes, one of them variable. The body
is dense (74% of `f32` reads plausible against a 49% random control) with
an index-like tail -- 12 of 163 `u16` slots in the largest type stay below
`0x1000` -- but **no count field** exists in the first 64 body bytes at any