Audit round: an ADTS frame that cannot hold its own CRC, and three drifts
An ADTS header that declares a CRC follows must be at least nine bytes — seven of header plus the two the CRC occupies — because the declared frame length counts them. The structural gate compared against a flat seven and never read the bit that says whether a CRC is there at all, so a frame whose own header describes something impossible was accepted and handed to the muxer as decodable. The pipeline's spawn doc named Sweep, and the thread Sweep would have created, as callers to look for. Neither has been in this crate since the recovery passes moved out. The same paragraph already records fixing this once, for a different departed caller — it simply drifted again a sentence later, so it now says to name callers that live here or name none. Whether a disc is structurally AACS-encrypted was spelled out by hand in both the fast identify and the full scan. They agreed today; nothing made them agree tomorrow, and disagreeing would mean the same disc reported encrypted by one path and clear by the other. There is one definition now, and the comment that pointed at it by line number points at its name. The AC-3 parser built a fresh buffer on every packet — of the order of a hundred thousand times per title — to work around a borrow it cannot avoid. The copy stays; the allocation does not. The buffer is now lent out and handed back, and a test pins that, because reverting it would be invisible in behaviour. Left alone deliberately: send/send_with_halt and finish/finish_with_halt look like one action under two names, and are not. After the consumer fails, one must still accept items and the other must refuse them; that difference is what stops a producer reading an entire disc for a write that died on its first frame. Collapsing them was tried here and the existing test caught it. Both now say so where the choice is made.
This commit is contained in:
+21
-4
@@ -479,6 +479,25 @@ pub struct Extent {
|
||||
/// Union a set of extents into sorted, merged, disjoint `(start_lba,
|
||||
/// sector_count)` ranges — the pure, testable core of
|
||||
/// [`Disc::encrypted_content_ranges`]. Reuses [`crate::udf::merge_ranges`].
|
||||
/// Is this disc structurally AACS-encrypted — i.e. does it carry an AACS
|
||||
/// directory?
|
||||
///
|
||||
/// THE ONE definition of that question. `Disc::identify` (the fast path, which
|
||||
/// only reads the filesystem) and `Disc::scan_with` (the full scan) both need
|
||||
/// it, and both used to spell the same two `find_dir` calls out by hand. They
|
||||
/// agreed today; nothing made them agree tomorrow. Adding a third AACS
|
||||
/// location, or excluding an empty placeholder directory, to one copy and not
|
||||
/// the other would silently desync the fast identify from the full scan — the
|
||||
/// same disc reported encrypted by one and clear by the other.
|
||||
///
|
||||
/// This is STRUCTURAL, not cryptographic: it says the tree looks like an
|
||||
/// encrypted disc, not that any sector actually is. A folder copied verbatim
|
||||
/// from a decrypted disc keeps its `AACS/` and answers true here (see
|
||||
/// `session::scan_dir`, which corrects for exactly that).
|
||||
pub(crate) fn aacs_dir_present(udf_fs: &crate::udf::UdfFs) -> bool {
|
||||
udf_fs.find_dir("/AACS").is_some() || udf_fs.find_dir("/BDMV/AACS").is_some()
|
||||
}
|
||||
|
||||
fn merged_extents<'a>(extents: impl Iterator<Item = &'a Extent>) -> Vec<(u32, u32)> {
|
||||
let mut ranges: Vec<(u32, u32)> = extents.map(|e| (e.start_lba, e.sector_count)).collect();
|
||||
ranges.sort_by_key(|r| r.0);
|
||||
@@ -1628,8 +1647,7 @@ impl Disc {
|
||||
// (no titles needed: BD/UHD/FMTS come from the MKB generation). It no
|
||||
// longer defaults to BluRay or defers UHD/FMTS to the full scan.
|
||||
let format = Self::detect_disc_format(&mut buffered, &udf_fs, &[]);
|
||||
let encrypted =
|
||||
udf_fs.find_dir("/AACS").is_some() || udf_fs.find_dir("/BDMV/AACS").is_some();
|
||||
let encrypted = aacs_dir_present(&udf_fs);
|
||||
let layers = if capacity > 24_000_000 { 2 } else { 1 };
|
||||
|
||||
Ok(DiscId {
|
||||
@@ -2015,8 +2033,7 @@ impl Disc {
|
||||
let scan_with_t0 = std::time::Instant::now();
|
||||
tracing::info!(target: "freemkv::scan", phase = "scan_with", "begin");
|
||||
// 2. Resolve encryption (AACS, CSS, or none)
|
||||
let encrypted =
|
||||
udf_fs.find_dir("/AACS").is_some() || udf_fs.find_dir("/BDMV/AACS").is_some();
|
||||
let encrypted = aacs_dir_present(&udf_fs);
|
||||
|
||||
let (aacs, aacs_error) = if !encrypted {
|
||||
(None, None)
|
||||
|
||||
Reference in New Issue
Block a user