fix: W3-3 — read_field/write_field misread aligned maxLength reservations
The running validate_pair campaign found a third crash: in aligned mode a maxLength reservation (ADR-003 strategy 2, VARCHAR(N)) stores RAW zero-padded data with no length prefix — materialize and validate_bytes implement exactly that — but read_field read the entry through data_access::read_string, i.e. parsed the window's first four bytes as a u32 length prefix. Raw reservation bytes that look like a large prefix then fail bounds with Access while validate_bytes says Ok: the validate⇒read agreement lattice breaks on every aligned maxLength string/bytes field (any schema declaring maxLength in aligned mode). write_field had the same mismatch (prefix+data into a raw window). Engine fix: - VariableEncoding gains MaxLengthReserved (additive variant, ADR-003 strategy 2). OffsetMap::compute records it for maxLength fields with the default encoding; maxLength+offset-indirect stays OffsetIndirect (the pair read is intentional, the window reserves max_len bytes), preserving the W3-1 combination semantics. - read_field String/Bytes arms dispatch on MaxLengthReserved → new data_access::read_reservation_string / read_reservation (raw window inside-buffer check + NUL trim — the materializer's exact semantics). - write_field dispatches → new data_access::write_reservation (zero- pads the window, rejects oversized values with Access). - materialize_aligned reads MaxLengthReserved through the same new read_reservation paths (single source of truth; replaces the inline trim logic with an identical implementation). - offset_map compute rejects a MaxLengthReserved encoding reaching the walk with a clean Offset error (recorded, never declared). - builder round-trips: MaxLengthReserved serializes via maxLength (the document form), never as an encoding value. - three engine regression tests: raw-not-prefixed read, zero-pad write + oversize rejection, validate⇒read_field agreement. - fuzz/shared validate_pair invariant updated: the W3-1 shorter-than-reservation exemption now applies to offset-indirect only; reservations assert the full window in-bounds (fixed engine). - corpus regenerated for generator-consistent numbering (seeds 037-044 relabeled; W3-1/W3-2 artifacts remain 044/045-047 → now 044, 048-050 region) — 48 seeds, replay 30/30 green. Verification: main crate 573 tests pass; clippy -D warnings clean (crate + shared); wasm clean; cargo fuzz build clean.
This commit is contained in:
1 parent
b7ead99724
commit
a0dd3d2de4
15 files changed
+307
-34
No files matched your search
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -408,19 +408,20 @@ fn drive_pair(engine: &AlkTypeEngine, doc: &Value, root: &str, buffer: &[u8]) {
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
// W3-1 (pinned contract, not engine behavior to
|
||||
// change): an offset-indirect entry's range is
|
||||
// the pair/reservation window (a declared
|
||||
// maxLength contributes its full size), but the
|
||||
// {data_offset, data_length} pair points
|
||||
// absolutely into the whole buffer — the data
|
||||
// may live anywhere in the buffer and the
|
||||
// buffer may be shorter than the reservation
|
||||
// (the wave-1 data_access bounds partition is
|
||||
// the contract: the pointed-to window sits
|
||||
// inside the buffer, nothing about the
|
||||
// reservation). For every other encoding a
|
||||
// successful read implies range.end ≤ len.
|
||||
// W3-1 (pinned contract): an offset-indirect
|
||||
// entry's range is the absolute pair window
|
||||
// and the {data_offset, data_length} pair
|
||||
// points anywhere in the buffer, so only the
|
||||
// pointed-to window must sit inside the buffer.
|
||||
// W3-3 fix (engine change): maxLength
|
||||
// reservations now read as raw NUL-trimmed
|
||||
// windows — the read requires range.end ≤ len,
|
||||
// so a validated buffer shorter than the
|
||||
// reservation window fails both validate and
|
||||
// the read; the exemption is unnecessary and
|
||||
// the full assertion applies. For every other
|
||||
// encoding a successful read implies
|
||||
// range.end ≤ len.
|
||||
let indirect =
|
||||
entry.meta.encoding == alktype::VariableEncoding::OffsetIndirect;
|
||||
if !indirect {
|
||||
|
||||
Reference in new issue
Block a user