Files
libfreemkv/docs/ebml.md
T
Matthew Jackson 6e14fc0c0c libfreemkv: 1.7.0 groundwork — sector-recycle soundness + CI hardening
Security
- Remove the unsafe Vec::set_len over recycled sector buffers — UB reachable
  from safe code (GHSA-j8ww-f5fg-9pmh, low). Uses Vec::resize; pooling unchanged.

CI / tooling
- clippy on stable with the MSRV single-sourced from Cargo.toml; cargo-deny
  dependency-audit gate with first-party git sources allowed (#15); audience-based
  comment-guard (#17).

Docs
- README (keydb ownership, BD-J parser count), drive-access transport/reset
  notes, mux timeline module-doc links.
2026-09-02 12:40:55 -07:00

2.8 KiB

src/mux/ebml.rs

fixed_width_vint8

Encodes data_size as the FIXED-WIDTH 8-octet EBML VINT used to back-patch a master element's size field: the 0x01 VINT_MARKER octet followed by the 56-bit VINT_DATA payload, big-endian (RFC 8794 section 4.4 — an 8-octet VINT carries 7 octets of VINT_DATA).

Extracted so the full 56-bit payload can be exercised directly: reaching the high payload bytes through end_master / end_master_buf would take a multi-terabyte buffer, leaving them unconstrained by any test.

data_size must be below 2^56; both callers check that first.

start_master_buf / end_master_buf

This is the seek-free twin of start_master/end_master for elements small enough to assemble whole before hitting the file (a BlockGroup: one Block plus a couple of tiny elements). Because end_master always back-patches a FIXED-WIDTH 8-byte VINT (0x01 + 7 payload bytes) rather than a minimal-width one, the bytes produced by this pair are byte-for-byte identical to the seek-and-back-patch pair — assembling in memory cannot change the emitted Matroska.

read_exact_bounded

Reads exactly len bytes WITHOUT trusting len to size the allocation.

vec![0u8; len] on an attacker-controlled EBML size would allocate gigabytes before the read fails. Instead the reader is capped to len and the buffer grows as bytes actually arrive: a malformed element that claims a huge length but supplies few bytes allocates only what it delivers, then errors on the short read.

Test: buffered_master_matches_seeking_master_byte_for_byte

The buffer-based master helpers must produce byte-for-byte what the seek-based ones produce. write_block_group was rewritten onto start_master_buf/end_master_buf to eliminate ~4 seeks (and 4 MiB BufWriter flushes) PER FRAME, which on an MPEG-2 title is ~350k frames — a change only safe because the two encodings are identical.

Both write a FIXED-width 8-byte VINT placeholder (0x01 + 7 payload bytes) and patch it in place; neither ever emits a minimal-width size. This test pins that, so a future "optimisation" of either one to minimal-width sizes cannot silently desync the two and change emitted Matroska.

Test: buffered_master_nests_correctly

Nested masters must patch correctly too — the MVC BlockGroup nests BlockAdditions > BlockMore inside the BlockGroup, and in-memory patching works by index rather than by file offset, so nesting is where an index-arithmetic slip would show up.

Test: fixed_width_vint8_is_big_endian_over_the_full_payload

The fixed-width 8-octet VINT is 0x01 followed by the 56-bit VINT_DATA payload in BIG-ENDIAN order (RFC 8794 section 4.4). Every payload octet is distinct here, so a shifted, reversed or dropped octet is visible; the top payload octets are unreachable through end_master without a multi-terabyte buffer, which is why this is tested at the encoder.