libfreemkv: 10-phase release audit fixes (v1.5.2..HEAD)
Multi-round audit of the decrypt/AACS/mux-codec refactor. Fixes, in descending severity: - mux/mp4/read.rs: bound untrusted-input allocations. `sample_budget` now also capped by file_len (a fixed-size stsz claiming count=u32::MAX can't inflate the Vec<SampleRef> past the file's own size); trak scan capped at MAX_TRACKS matches; find_box() takes only the first match (cap=1) instead of materializing every match. Removes dead find_boxes wrapper. - disc/mod.rs: merge_content_key_ranges now UNIONS same-key overlapping ranges (coverage-preserving) instead of dropping the non-overlapping tail, which silently left encrypted LBAs uncovered -> ciphertext passthrough in the whole-disc sweep/patch map. Different-key overlap (malformed) still dropped to keep the set disjoint. - sector/decrypting.rs: remove dead unit_key_idx field + with_unit_key_idx setter (vestigial from the pre-keymap trial-decrypt design; AACS is map-only now). Fix stale docs. - decrypt.rs / resolve.rs / error.rs / extract.rs: doc/comment drift from the refactor (AacsKeyMap positive-map semantics, resolve_mux_key_map doc reattachment, decrypt_sectors_in_content legacy-alias, E_MP4_INVALID meaning, multi-CPS orphan by-design note). Test coverage (all mutation-verified real): - DTS NeedMore force-flush buffer bound; FLAC/MPEG-audio PTS carry-forward; mp4 mdhd timescale=0 divide-by-zero guard, MAX_TRACKS cap, sample-count file_len bound, MAX_ALLOC_BYTES cap under inflated file_len. - resolve_fmts_key_map: extracted filter_addressable_segments, resolve_tie_phase, fill_base_key_gaps as pure behavior-preserving helpers, each unit-tested (segment filter, phase-tie arms, gap-fill gaplessness over every extent).
This commit is contained in:
+15
-15
@@ -201,9 +201,13 @@ pub enum Phase {
|
||||
/// concern, exactly as for a physically-read clear disc.
|
||||
///
|
||||
/// Ranges are `[start_lba, end_lba)` → index into the `Aacs { unit_keys }` pool,
|
||||
/// sorted and disjoint. `default_idx` covers any LBA no range claims — the
|
||||
/// single-CPS case is just an empty range list with `default_idx = 0`, so the
|
||||
/// common disc pays zero lookup cost and needs no structural walk.
|
||||
/// sorted and disjoint. The map is a POSITIVE list: an LBA in no range is passed
|
||||
/// through untouched (no default key). How a single-CPS disc is mapped depends on
|
||||
/// the caller: the whole-disc EXTRACT path uses one blanket range `(0, u32::MAX,
|
||||
/// 0)` so every encrypted unit — parsed title or orphan clip — resolves to key 0;
|
||||
/// the per-title MUX/sweep path (`resolve_mux_key_map` → `content_map`) maps only
|
||||
/// the title's own extents, so an orphan clip outside them is left as pass-through.
|
||||
/// Either way, clear nav/filesystem sectors (encrypted-flag off) pass through.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct AacsKeyMap {
|
||||
// (start_lba, end_lba, key_idx, phase). An LBA in NO range is passed through
|
||||
@@ -514,17 +518,13 @@ pub fn decrypt_sectors(
|
||||
decrypt_sectors_impl(buf, keys, unit_key_idx, None)
|
||||
}
|
||||
|
||||
/// Like [`decrypt_sectors`], but ONLY decrypts/verifies units whose absolute LBA
|
||||
/// falls inside `content_ranges` — the disc's AACS-encrypted content (the m2ts
|
||||
/// stream extents). Units OUTSIDE content (UDF filesystem / nav) are left
|
||||
/// untouched and never counted as decrypt loss: they are clear by definition, so
|
||||
/// the content-clarity check [`is_clean`](crate::aacs::content::is_clean) must not
|
||||
/// be consulted about them (a filesystem unit has no TS sync, so it would
|
||||
/// otherwise be mistaken for ciphertext). `base_lba` is
|
||||
/// the absolute LBA of `buf`'s first sector; aligned units are 3 sectors.
|
||||
///
|
||||
/// `content_ranges` is sorted, merged, disjoint `(start_lba, sector_count)`
|
||||
/// tuples (each covering `[start_lba, start_lba + sector_count)`).
|
||||
/// Legacy alias of [`decrypt_sectors`]. Under the keymap-only model AACS decrypts
|
||||
/// EXCLUSIVELY through the resolved key map (`decrypt_sectors_mapped`), so there is
|
||||
/// no per-unit content-extent gate here any more: the AACS arm fails loud and the
|
||||
/// CSS / `None` arm self-gates on its per-sector scramble flag. `base_lba` and
|
||||
/// `content_ranges` are therefore inert — retained only so the wrapper signature
|
||||
/// stays stable for the `DecryptingSectorSource` dispatch. Prefer
|
||||
/// [`decrypt_sectors`] in new code.
|
||||
pub fn decrypt_sectors_in_content(
|
||||
buf: &mut [u8],
|
||||
keys: &mut DecryptKeys,
|
||||
@@ -607,7 +607,7 @@ mod tests {
|
||||
v
|
||||
}
|
||||
|
||||
// ── Content-extent gate (`decrypt_sectors_in_content` / `lba_in_ranges`) ──
|
||||
// ── `decrypt_sectors_in_content` (now a legacy alias of `decrypt_sectors`) ──
|
||||
|
||||
/// `DecryptKeys::None` is a no-op even with a content map + scrambled bytes.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user