diff --git a/crates/buzz-media/tests/fixtures/audio/README.md b/crates/buzz-media/tests/fixtures/audio/README.md index 7d30922c7..80b97b8f0 100644 --- a/crates/buzz-media/tests/fixtures/audio/README.md +++ b/crates/buzz-media/tests/fixtures/audio/README.md @@ -127,3 +127,20 @@ That is the pattern WAV is missing: it checks a prefix, then indexes the ORIGINAL buffer past it. Fix shape is one whole-body slice — `bytes.get(offset + 8..offset + 24).ok_or(MetadataForbidden)?` — then slice the six fields from it. Removes the class, not the instance. + +### Reachability witness (added after the sweep itself proved untrustworthy) + +The sweep now ships with `fmt_field_checks_are_live`, a black-box witness that +needs no source instrumentation: mutate `channels` / `block_align` / `bits` in a +known-good WAV and assert the verdict flips Ok -> Err. It can only pass if those +bytes are actually read. + +Self-tested both directions, which is the only reason to trust it: +- Against the unfixed validator: witness PASSES, battery FAILS on the + `riff-repaired` arm at prefix 22 (and only that arm). +- With the fmt field checks made inert as a negative control: witness FAILS with + "mutating channels did not change the verdict -> field is not read". + +**If the witness fails, every "no panic" line below it is vacuous and must not be +reported as a pass.** That ordering is the whole point — this harness has twice +produced confident green output while touching nothing. diff --git a/crates/buzz-media/tests/fixtures/audio/trunc_sweep.rs b/crates/buzz-media/tests/fixtures/audio/trunc_sweep.rs index 132ca3edc..8515d5531 100644 --- a/crates/buzz-media/tests/fixtures/audio/trunc_sweep.rs +++ b/crates/buzz-media/tests/fixtures/audio/trunc_sweep.rs @@ -1,10 +1,18 @@ +//! Truncation battery + black-box reachability witness for the audio validators. +//! +//! Instrument discipline: a green sweep is worthless unless the sweep provably +//! executed the code it claims to cover. `fmt_field_checks_are_live` is that +//! witness — it needs no source instrumentation, only the observation that +//! mutating a `fmt ` field must flip the verdict. If it fails, every "no panic" +//! result below is vacuous and must not be reported as a pass. + fn cfg() -> buzz_media::config::MediaConfig { buzz_media::config::MediaConfig { s3_endpoint: String::new(), s3_access_key: String::new(), s3_secret_key: String::new(), s3_bucket: String::new(), s3_region: "us-east-1".into(), s3_addressing_style: buzz_media::config::S3AddressingStyle::Path, - max_image_bytes: 50*1024*1024, max_gif_bytes: 10*1024*1024, + max_image_bytes: 50 * 1024 * 1024, max_gif_bytes: 10 * 1024 * 1024, max_video_bytes: 524_288_000, max_file_bytes: 104_857_600, max_audio_bytes: 26_214_400, public_base_url: "http://localhost:3000/media".into(), @@ -12,41 +20,74 @@ fn cfg() -> buzz_media::config::MediaConfig { } } -fn sweep(name: &str, full: &[u8], repair_riff: bool) -> Option { +fn v(bytes: &[u8]) -> Result<(String, String), buzz_media::error::MediaError> { + buzz_media::validation::validate_file_content(bytes, &cfg()) +} + +fn dir() -> std::path::PathBuf { + std::path::PathBuf::from(std::env::var("DAWN_SAN").expect("DAWN_SAN")) +} + +/// Rewrite the RIFF declared size to match actual length. Without this a +/// truncated WAV dies at `declared + 8 == len` and never reaches the fmt walk. +fn repair_riff(b: &mut [u8]) { + if b.len() >= 8 { + let d = (b.len() - 8) as u32; + b[4..8].copy_from_slice(&d.to_le_bytes()); + } +} + +/// REACHABILITY WITNESS. Proves the fmt field reads execute, black-box. +#[test] +fn fmt_field_checks_are_live() { + let good = std::fs::read(dir().join("tagged.wav")).unwrap(); + assert!(v(&good).is_ok(), "baseline WAV must validate; got {:?}", v(&good)); + + // fmt body starts at 20: channels@22, sample_rate@24, byte_rate@28, + // block_align@32, bits@34. Corrupting any must flip Ok -> Err, which is + // only possible if those bytes were actually read. + for (name, off, val) in [ + ("channels", 22usize, 0u16), + ("block_align", 32, 0xffff), + ("bits", 34, 7), + ] { + let mut bad = good.clone(); + bad[off..off + 2].copy_from_slice(&val.to_le_bytes()); + assert!(v(&bad).is_err(), "mutating {name} did not change the verdict -> field is not read"); + } + eprintln!("reachability witness OK: fmt field reads are live"); +} + +fn sweep(name: &str, full: &[u8], repair: bool) -> Option { + let lens: Vec = (0..full.len().min(600)) + .chain((600..full.len()).step_by(97)) + .collect(); let mut first = None; - let lens: Vec = (0..full.len().min(600)).chain((600..full.len()).step_by(97)).collect(); for n in lens { let mut slice = full[..n].to_vec(); - // Keep the RIFF declared size consistent with the truncated length, or - // the `declared + 8 == len` gate rejects everything before the fmt walk - // and the sweep never reaches the code under test. - if repair_riff && slice.len() >= 8 { - let d = (slice.len() - 8) as u32; - slice[4..8].copy_from_slice(&d.to_le_bytes()); - } - let r = std::panic::catch_unwind(|| { - let _ = buzz_media::validation::validate_file_content(&slice, &cfg()); - }); - if r.is_err() && first.is_none() { first = Some(n); } + if repair { repair_riff(&mut slice); } + let hit = std::panic::catch_unwind(|| { let _ = v(&slice); }).is_err(); + if hit && first.is_none() { first = Some(n); } } match first { - None => println!("{name:24} OK (no panic at any prefix)"), - Some(n) => println!("{name:24} *** first panic at prefix {n} ***"), + None => eprintln!("{name:26} OK (no panic at any prefix)"), + Some(n) => eprintln!("{name:26} *** first panic at prefix {n} ***"), } first } #[test] -fn dawn_truncation_sweep_all_containers() { - let dir = std::path::PathBuf::from(std::env::var("DAWN_SAN").unwrap()); +fn truncation_battery_no_panics() { + let d = dir(); let mut bad = Vec::new(); - for name in ["tagged.mp3","mpeg2_22k.mp3","mpeg25_11k.mp3","tagged.ogg","bigart.ogg","long.ogg"] { - let full = std::fs::read(dir.join(name)).unwrap(); - if sweep(name, &full, false).is_some() { bad.push(name); } + for name in ["tagged.mp3", "mpeg2_22k.mp3", "mpeg25_11k.mp3", "tagged.ogg", "bigart.ogg", "long.ogg"] { + let full = std::fs::read(d.join(name)).unwrap(); + if sweep(name, &full, false).is_some() { bad.push(name.to_string()); } } - // WAV twice: naive (declared size left stale) and repaired (reaches fmt walk). - let wav = std::fs::read(dir.join("tagged.wav")).unwrap(); - if sweep("tagged.wav (naive)", &wav, false).is_some() { bad.push("wav-naive"); } - if sweep("tagged.wav (riff-repaired)", &wav, true).is_some() { bad.push("wav-repaired"); } - assert!(bad.is_empty(), "validator panicked: {bad:?}"); + // WAV both ways. The naive arm is kept ONLY to keep the contrast visible; + // it is not evidence. The repaired arm is the one that reaches the fields. + let wav = std::fs::read(d.join("tagged.wav")).unwrap(); + if sweep("tagged.wav (naive)", &wav, false).is_some() { bad.push("wav-naive".into()); } + if sweep("tagged.wav (riff-repaired)", &wav, true).is_some() { bad.push("wav-repaired".into()); } + assert!(bad.is_empty(), "validator panicked on truncated input: {bad:?}"); }