Files
libfreemkv/docs/mkb.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

3.5 KiB

AACS MKB internals — relocated rationale

Supplementary notes for src/aacs/mkb.rs comments that exceed the comment-guard's internal-comment cap. Each section is pointed to by a short // See docs/mkb.md — <topic> comment at the relevant call site.

Record-walker consolidation (mkb_records)

Yields (offset, rec_type, rec_len) for each record — a 4-byte header (type byte + big-endian 24-bit length) then the body — stopping at the 00 000000 end marker or a malformed/out-of-bounds length. It is lazy (no body clone), so a find-one-record caller never materialises the multi-MB cvalue table. walk_mkb and every MKB record walk in aacs::resolve/aacs::derive are built on this, so the framing rules — and any future fix to them — live in exactly one place. They had previously drifted across six hand-rolled copies before being consolidated here.

cvalue record selection (mkb_find_cvalues)

The cvalue table is record type 0x05 (Media Key Data) on BOTH AACS 1.0 and AACS 2.x MKBs — its 16-byte cvalue entries are 1:1 with the 5-byte Subset-Difference index entries in record 0x04 — the standard AACS MKB layout.

On AACS 2.x in-drive UHD MKBs the 0x05 table is large (the full subset-difference cvalue set: ~181k entries on a retail MKB, 1:1 with the giant 0x04 index), while record 0x07 (Explicit Subset-Difference Record) is a much smaller structure (~96 entries) and is NOT the cvalue table. An earlier version of this function preferred 0x07, which under-tested the Subset-Difference walk on UHD discs and prevented the DK→walk path from ever finding the matching uv. The selection MUST therefore be 0x05-first; 0x07 is only a fallback for malformed/legacy MKBs that somehow lack a 0x05 record.

BE24 length field test (mkb_records_honors_the_high_byte_of_the_be24_length)

The record length is a big-endian 24-bit field, so the high byte carries lengths of 64 KiB and up. The MKB records that matter most are exactly that size — a real UHD cvalue table is 46_101 * 16 bytes and a 0x2d variant record is ~92 KiB — so a walker that dropped the high byte would mis-frame every record of a real MKB from the first big one onward, and every downstream key lookup would read the wrong bytes.

(The pre-existing high-byte test used total length 0x0110, whose high byte is ZERO — it exercised the middle byte only. This test puts a non-zero value in the high byte.)

Header-only record test (mkb_records_yields_a_header_only_record_at_the_buffer_end)

rec_len == 4 is a well-formed HEADER-ONLY record (the minimum the walker accepts), including one sitting at the very end of the buffer with no bytes after it. Rejecting either — the pos + 4 bound or the rec_len < 4 floor being off by one — silently drops the MKB's last record, and "the record isn't there" is indistinguishable from "the disc doesn't carry it".

MKBType raw-bytes test (mkb_type_raw_reads_all_four_body_bytes)

mkb_type_raw reports the 32-bit MKBType field verbatim ([C] §3.2.5.1.1 Table 3-2), including a value this build does not recognise — the caller uses it to tell "unknown MKB generation" from "no Type record at all". All four bytes must come from the record body; reading any of them from the wrong offset yields a type that silently classifies as a different AACS generation.

The recognised constants all share bytes with the 0x10 record-type header byte (e.g. MKB_21_CATEGORY_C is 48 15 10 03), so this test uses a value with four distinct bytes, none of them 0x10.