Audit fixes + DVD support foundation (IFO, PS demux, MPEG-2, CSS crack)

Audit fixes (14 critical, 22 warnings):
- UDF: bounds checks on all ICB/FID parsing from disc data
- SCSI Linux: saturating_sub on residual, CDB length guard, buffer size guard
- SCSI macOS: SCSITaskStatus u32 (was u8 — stack corruption)
- AACS: EC mod_inv returns infinity instead of panic, key reduced mod n
- AACS: do_handshake tries all host certs (was returning on first failure)
- H.264: bounds check on SPS < 4 bytes
- ContentReader: error on missing unit key (was zero-fill)
- KEYDB: flat redirect loop (was recursive), 100MB response limit, Windows HOME fallback
- ISO writer: AVDP extent order, partition length, allocation cap
- Network: removed TCP_NODELAY on bulk stream
- MKV: guard on u64::MAX seek
- disc.rs: saturating_sub on extent offset, simplified dead region code
- cargo fmt (610 violations), cargo clippy --fix (55 auto-fixes)

DVD support (new files):
- src/ifo.rs — IFO parser (VIDEO_TS.IFO, VTS_XX_0.IFO, PGC chains, cells, streams) — 13 tests
- src/mux/ps.rs — MPEG-2 Program Stream demuxer (pack headers, PES, private stream 1) — 12 tests
- src/mux/codec/mpeg2.rs — MPEG-2 video parser (sequence headers, I-frame detection) — 15 tests
- src/css/crack.rs — split-attack algorithm (LFSR cipher needs verification — test ignored)

226 tests total (was 186), 1 ignored (CSS crack needs cipher verification).
This commit is contained in:
MattJackson
2026-04-11 16:52:22 +00:00
parent 6e771a1867
commit ff5547363b
57 changed files with 6189 additions and 1519 deletions
+12 -7
View File
@@ -12,9 +12,9 @@
//! sub_com1_idx="23,24,25" />
//! ```
use super::{LabelPurpose, LabelQualifier, StreamLabel, StreamLabelType};
use crate::sector::SectorReader;
use crate::udf::UdfFs;
use super::{StreamLabel, StreamLabelType, LabelPurpose, LabelQualifier};
pub fn detect(udf: &UdfFs) -> bool {
super::jar_file_exists(udf, "playlists.xml")
@@ -31,12 +31,13 @@ pub fn parse(reader: &mut dyn SectorReader, udf: &UdfFs) -> Option<Vec<StreamLab
// Parse audio streams
if let Some(aud) = extract_attr(&feature, "aud") {
let com_idx = extract_attr(&feature, "aud_com1_idx")
.and_then(|s| s.parse::<usize>().ok());
let com_idx = extract_attr(&feature, "aud_com1_idx").and_then(|s| s.parse::<usize>().ok());
for (i, lang) in aud.split(',').enumerate() {
let lang = lang.trim();
if lang.is_empty() { continue; }
if lang.is_empty() {
continue;
}
let purpose = if com_idx == Some(i) {
LabelPurpose::Commentary
} else {
@@ -67,7 +68,9 @@ pub fn parse(reader: &mut dyn SectorReader, udf: &UdfFs) -> Option<Vec<StreamLab
for (i, lang) in sub.split(',').enumerate() {
let lang = lang.trim();
if lang.is_empty() { continue; }
if lang.is_empty() {
continue;
}
let purpose = if com_indices.contains(&i) {
LabelPurpose::Commentary
@@ -94,7 +97,9 @@ pub fn parse(reader: &mut dyn SectorReader, udf: &UdfFs) -> Option<Vec<StreamLab
}
}
if labels.is_empty() { return None; }
if labels.is_empty() {
return None;
}
Some(labels)
}
@@ -134,7 +139,7 @@ fn find_feature_playlist(xml: &str) -> Option<String> {
}
/// Extract an XML attribute value from an element string.
fn extract_attr<'a>(element: &'a str, name: &str) -> Option<String> {
fn extract_attr(element: &str, name: &str) -> Option<String> {
let needle = format!("{}=\"", name);
let start = element.find(&needle)? + needle.len();
let end = element[start..].find('"')? + start;