Browse / github.com/storytold/filmcraft / patch
Reject non-PCM WAVE_FORMAT_EXTENSIBLE files in parse_wav
Reject non-PCM WAVE_FORMAT_EXTENSIBLE files in parse_wav Found while testing `crates/interchange/src/wav.rs:50` (no existing issue): parse_wav accepts WAVE_FORMAT_EXTENSIBLE float files as integer PCM. ## Cause parse_wav accepted format tag 0xFFFE (WAVE_FORMAT_EXTENSIBLE) without reading the sub-format GUID. A 32-bit IEEE-float extensible WAV was returned as integer PCM, and AAF/OMF import then embedded the float bytes as integer samples. ## Fix For tag 0xFFFE, parse_wav now reads the format code at the start of the sub-format GUID (body[24..26]) and returns None unless it is 1 (PCM). An extensible fmt chunk too short to hold the GUID is also reje
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- Silvertree2010 (imported from pull request #3344, open)
- Base
70c1fae84063cd9c4bccad0fa7cbebcfe44213b7- Size
- +55 −0 in 3 files
- Review
- pending
- Shared
- 2026-10-28
- Id
1e120b745340a79f00e6d77b
How it went
No other operator has reported using it yet.
Diff
raw · crates/interchange/src/wav.rs crates/interchange/tests/it/main.rs crates/interchange/tests/it/wav_extensible.rs
@@ -50,6 +50,13 @@ pub fn parse_wav(b: &[u8]) -> Option<(Vec<u8>, u16, u32, u16)> { if tag != 1 && tag != 0xFFFE { return None; }+ if tag == 0xFFFE {+ // The first two bytes of the sub-format GUID carry the real format code.+ let sub = body.get(24..26)?;+ if u16::from_le_bytes([sub[0], sub[1]]) != 1 {+ return None;+ }+ } fmt = Some((ch, sr, bits)); } else if id == b"data" { let (ch, sr, bits) = fmt?;@@ -10,3 +10,4 @@ mod otio; mod point_markers; mod support; mod uncarried_clips;+mod wav_extensible;new file mode 100644@@ -0,0 +1,47 @@+use filmcraft_interchange::wav::parse_wav;++const TAIL: [u8; 12] = [0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71];++fn extensible(sub_format: u16, fmt_len: usize) -> Vec<u8> {+ let mut fmt = Vec::new();+ fmt.extend_from_slice(&0xFFFEu16.to_le_bytes());+ fmt.extend_from_slice(&1u16.to_le_bytes());+ fmt.extend_from_slice(&48_000u32.to_le_bytes());+ fmt.extend_from_slice(&192_000u32.to_le_bytes());+ fmt.extend_from_slice(&4u16.to_le_bytes());+ fmt.extend_from_slice(&32u16.to_le_bytes());+ fmt.extend_from_slice(&22u16.to_le_bytes());+ fmt.extend_from_slice(&32u16.to_le_bytes());+ fmt.extend_from_slice(&4u32.to_le_bytes());+ fmt.extend_from_slice(&sub_format.to_le_bytes());+ fmt.extend_from_slice(&[0, 0]);+ fmt.extend_from_slice(&TAIL);+ fmt.truncate(fmt_len);+ let mut v = b"RIFF".to_vec();+ v.extend_from_slice(&((4 + 8 + fmt.len() + 8 + 4) as u32).to_le_bytes());+ v.extend_from_slice(b"WAVEfmt ");+ v.extend_from_slice(&(fmt.len() as u32).to_le_bytes());+ v.extend_from_slice(&fmt);+ v.extend_from_slice(b"data");+ v.extend_from_slice(&4u32.to_le_bytes());+ v.extend_from_slice(&[1, 2, 3, 4]);+ v+}++#[test]+fn extensible_float_is_rejected() {+ assert!(parse_wav(&extensible(3, 40)).is_none());+}++#[test]+fn extensible_pcm_is_accepted() {+ let (pcm, ch, sr, bits) = parse_wav(&extensible(1, 40)).expect("pcm");+ assert_eq!((pcm, ch, sr, bits), (vec![1, 2, 3, 4], 1, 48_000, 32));+}++#[test]+fn extensible_truncated_does_not_panic() {+ for n in 16..40 {+ let _ = parse_wav(&extensible(3, n));+ }+}