Make five tests capable of failing, and stop the presence probe unmounting the disc
The worst of the five was a regression suite that never touched the code it guarded: nine batch-count tests called `safe_batch_count` and `buggy_batch_count`, both defined in the test file itself. The u16 truncation they exist to prevent could be reintroduced in sector/prefetched.rs with every one of them green. They now drive the real producer through the public API, and reinstating the truncation fails five of the nine. Worth recording that the symptom has changed since the original fix: the unit-alignment clamp below floors a zero batch at three sectors, so the bug is now a twenty-fold throughput cliff rather than the stall it once was. The MP4 reserve test's only numeric case was dominated by the floor and the buffer, so BYTES_PER_SAMPLE could be zeroed without failing it. It now has a case where the per-sample term dominates. The zero-count guard in FileSectorSource was likewise unfalsifiable — seek-past-EOF and a zero-length read both succeed — so the test now observes the file cursor. The AACS media-key ambiguity guard had no test at all; the pool scan is extracted so the verifier can be injected, because a genuine two-key collision needs one ciphertext decrypting under two AES-128 keys to plaintexts sharing a 64-bit magic, which is a 2^64 search and not a fixture. macOS implemented the documented cheap, side-effect-free presence probe by building a full exclusive transport — which force-unmounts the disc. Linux and Windows issue one TEST UNIT READY with no unmount; macOS was the outlier. It now walks the IOKit registry for the media object instead. The C shim's registry reads assumed CoreFoundation types the registry does not guarantee, so a driver publishing a CFNumber where a CFString was expected aborted the process from inside public API. Types are checked and a wrong type treated as absent. The unbounded waitpid on the unmount child is now a polled deadline, and the last-resort match gained the NULL check its two siblings already had. The empty-CDB guard existed only on Linux while a shared helper's comment claimed all three backends had it. Moved into the helper, so the comment is now true and macOS and Windows are covered. One finding was REJECTED with evidence rather than fixed. The TrueHD buffer-cap test was indeed bogus, but MAX_TRUEHD_BUF turns out to be unreachable by any input: the parser only retains data when the buffer is shorter than the declared AU, and that declaration is twelve bits, so the worst case is 8189 bytes against a 256 KiB cap. An exhaustive sweep over all 65536 AU headers confirmed it. The fixture now sits at the reachable ceiling and asserts that instead. The cap itself is left in place as defence, unreachable by construction, matching how the AC-3 resync guard was handled earlier in this audit. Two behaviour changes worth naming: Linux's empty-CDB error becomes InvalidCdbLength rather than a transport failure, and an unknown device now reports absent media rather than a not-found error, because the registry cannot tell an empty drive from a missing one. The latter is a conflation of the kind this audit has fixed three times; it is recorded for the next round rather than left silent.
This commit is contained in:
+36
-9
@@ -1638,22 +1638,43 @@ mod tests {
|
||||
assert_eq!(parser.buf.len(), 100, "partial AU retained");
|
||||
}
|
||||
|
||||
/// Largest AU the 12-bit length field can declare: 0xFFF words × 2.
|
||||
const MAX_AU_BYTES: usize = 0xFFF * 2; // 8190
|
||||
|
||||
#[test]
|
||||
fn buffer_stays_bounded_across_many_partial_pes() {
|
||||
// Malformed/never-completing input must keep the reassembly buffer
|
||||
// bounded by MAX_TRUEHD_BUF. Repeatedly feed AU fragments whose declared
|
||||
// length always exceeds what is buffered, so no AU ever completes; the
|
||||
// post-loop cap guard must clear the buffer instead of letting it grow
|
||||
// unbounded across many calls.
|
||||
// bounded across an unbounded number of PES packets.
|
||||
//
|
||||
// The bound that actually holds is MAX_AU_BYTES, not MAX_TRUEHD_BUF:
|
||||
// `parse`'s loop only breaks with data retained when
|
||||
// `self.buf.len() < unit_bytes`, and `unit_bytes` is
|
||||
// `((buf[0] << 8 | buf[1]) & 0xFFF) * 2 <= 8190`. Every other exit
|
||||
// drains. So the post-loop `buf.len() > MAX_TRUEHD_BUF` cap (256 KiB) is
|
||||
// an unreachable backstop — an exhaustive sweep of all 65536 two-byte
|
||||
// AU heads × fragment sizes {3, 5, 100, 4096, 8189, 65535} over 20 PES
|
||||
// each peaks at 8189 bytes. Asserting only `<= MAX_TRUEHD_BUF` is
|
||||
// therefore vacuous; assert the reachable ceiling instead.
|
||||
//
|
||||
// Fixture: heads of 0xFF 0xFF (masked to 0xFFF words = 8190 bytes
|
||||
// declared — this also exercises the 12-bit mask) with 8189 bytes
|
||||
// present, so each PES leaves the buffer one byte short of a complete
|
||||
// AU. The previous fixture used 4096-byte fragments, which completed an
|
||||
// AU every second call and never loaded the buffer past ~4 KiB.
|
||||
let mut parser = TrueHdParser::new();
|
||||
// Each PES: a head declaring 0xFFF words (8190 bytes) but only 4096 bytes
|
||||
// present → incomplete → retained. Across many PES this would accumulate
|
||||
// without the cap.
|
||||
let mut worst = 0usize;
|
||||
for _ in 0..200 {
|
||||
let mut frag = vec![0u8; 4096];
|
||||
frag[0] = 0x0F; // 0x0FFF words = 4095 → 8190 bytes declared
|
||||
let mut frag = vec![0u8; MAX_AU_BYTES - 1];
|
||||
frag[0] = 0xFF;
|
||||
frag[1] = 0xFF;
|
||||
let _ = parser.parse(&make_pes(frag, Some(0)));
|
||||
worst = worst.max(parser.buf.len());
|
||||
assert!(
|
||||
parser.buf.len() < MAX_AU_BYTES,
|
||||
"reassembly buffer exceeded the AU-length ceiling: {} >= {}",
|
||||
parser.buf.len(),
|
||||
MAX_AU_BYTES
|
||||
);
|
||||
assert!(
|
||||
parser.buf.len() <= MAX_TRUEHD_BUF,
|
||||
"reassembly buffer exceeded cap: {} > {}",
|
||||
@@ -1661,6 +1682,12 @@ mod tests {
|
||||
MAX_TRUEHD_BUF
|
||||
);
|
||||
}
|
||||
// The fixture must genuinely load the buffer, not self-drain: if this
|
||||
// trips, the test is measuring nothing.
|
||||
assert!(
|
||||
worst >= MAX_AU_BYTES - 8,
|
||||
"fixture must drive the buffer to the ceiling, peaked at {worst}"
|
||||
);
|
||||
}
|
||||
|
||||
// --- ac3_boundary_corroborated: the AC-3-vs-TrueHD disambiguation ---
|
||||
|
||||
Reference in New Issue
Block a user