From 5115f77f08aeb4daa64849abce80d7c3b90adc53 Mon Sep 17 00:00:00 2001 From: Nikita Rakitin Date: Wed, 14 Oct 2026 06:20:10 +0300 Subject: [PATCH] Media: damaged audio no longer hangs the demux loop or panics in symphonia --- ATTRIBUTION.md | 1 + crates/media/src/audio.rs | 26 ++++++++++++--- crates/media/src/probe.rs | 6 ++-- crates/media/tests/decode.rs | 30 ++++++++++++++++++ .../tests/fixtures/damaged/demux-loop.m4a | Bin 0 -> 5324 bytes .../tests/fixtures/damaged/demux-loop.mp4 | Bin 0 -> 6102 bytes .../tests/fixtures/damaged/ebml-header.mkv | Bin 0 -> 905 bytes .../tests/fixtures/damaged/ebml-position.webm | Bin 0 -> 3914 bytes 8 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 crates/media/tests/fixtures/damaged/demux-loop.m4a create mode 100644 crates/media/tests/fixtures/damaged/demux-loop.mp4 create mode 100644 crates/media/tests/fixtures/damaged/ebml-header.mkv create mode 100644 crates/media/tests/fixtures/damaged/ebml-position.webm diff --git a/ATTRIBUTION.md b/ATTRIBUTION.md index 9ca8e5e8..66c85d41 100644 --- a/ATTRIBUTION.md +++ b/ATTRIBUTION.md @@ -26,6 +26,7 @@ deck and UI icons are original work of the DeckCraft contributors (drawn or gene | `docs/images/editor.png`, `docs/images/shape-format.png`, `docs/images/format-pane.png`, `docs/images/slide-sorter.png`, `docs/images/dark-mode.png`, `docs/images/sample-deck.png` | DeckCraft contributors | Screenshots of DeckCraft and slides rendered by `deckcraft-cli` from the built-in sample deck (original content; Inter font, OFL-1.1) | MIT OR Apache-2.0 | README screenshots | | `crates/media/tests/fixtures/tone*` (`tone.mp3`, `.m4a`, `.aac`, `.ogg`, `.opus`, `.flac`, `.wma`, `tone-alac.m4a`) | DeckCraft contributors | synthesised 0.5 s 440 Hz sine, encoded by `crates/media/tests/fixtures/make.sh` (ffmpeg as the encoder only) | CC0-1.0 | Audio decoding tests | | `crates/media/tests/fixtures/clip-*` (`clip-h264.mp4`, `clip-h264.mov`, `clip-hevc.mp4`, `clip-vp9.webm`, `clip-av1.mkv`) | DeckCraft contributors | 6 frames of a colour gradient generated by `make.sh` (python3), encoded by ffmpeg (x264 / x265 / libvpx / SVT-AV1 as encoders only) | CC0-1.0 | Video decoding tests | +| `crates/media/tests/fixtures/damaged/*` (`ebml-header.mkv`, `ebml-position.webm`, `demux-loop.mp4`, `demux-loop.m4a`) | DeckCraft contributors | the `clip-*` and `tone*` fixtures above with bytes changed by a mutation fuzzer | CC0-1.0 | Damaged media must not panic or hang | | `crates/media/tests/fixtures/make.sh` | DeckCraft contributors | this repository | MIT OR Apache-2.0 | Regenerates the media fixtures | | `crates/render/tests/fixtures/colr-squares.ttf` | DeckCraft contributors | two squares as a COLRv0/CPAL test font, built by `make_colr_font.py` (fontTools as the builder only) | CC0-1.0 | Colour glyph rendering tests | | `crates/fonts/tests/fixtures/legacy-semibold.ttf` | DeckCraft contributors | a one-glyph Semibold face named like Segoe UI Semibold (its Windows family differs from the typographic one), built by `make_legacy_font.py` (fontTools as the builder only) | CC0-1.0 | Font lookup by Windows family name tests | diff --git a/crates/media/src/audio.rs b/crates/media/src/audio.rs index 80ad66a4..e641113a 100644 --- a/crates/media/src/audio.rs +++ b/crates/media/src/audio.rs @@ -71,13 +71,13 @@ pub fn decode(bytes: &Bytes) -> Result { Container::Asf => Err(MediaError::Unsupported("Windows Media Audio (WMA)".into())), Container::Ogg => match decode_ogg_opus(bytes) { Some(r) => r, - None => decode_symphonia(bytes, "ogg"), + None => contained(|| decode_symphonia(bytes, "ogg")), }, Container::Matroska => match decode_mkv_opus(bytes) { Some(r) => r, - None => decode_symphonia(bytes, "mkv"), + None => contained(|| decode_symphonia(bytes, "mkv")), }, - c => decode_symphonia(bytes, c.name()), + c => contained(|| decode_symphonia(bytes, c.name())), } } @@ -142,6 +142,15 @@ fn sym_err(e: SymError) -> MediaError { } } +/// Runs symphonia, whose demuxers can panic on damaged files (Matroska's EBML reader asserts +/// its position, for one): the panic becomes a corrupt-file error instead of ending the app. +fn contained(f: impl FnOnce() -> Result) -> Result { + std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)).unwrap_or_else(|_| Err(MediaError::Corrupt("the audio could not be read".into()))) +} + +/// Demux errors in a row after which a file is taken to have no more audio. +const MAX_DEMUX_ERRORS: u32 = 64; + fn decode_symphonia(bytes: &Bytes, ext: &str) -> Result { let mss = MediaSourceStream::new(Box::new(Cursor::new(Shared(bytes.clone()))), MediaSourceStreamOptions::default()); let mut hint = Hint::new(); @@ -172,12 +181,21 @@ fn decode_symphonia(bytes: &Bytes, ext: &str) -> Result { let mut rate = 0; let mut channels = 0u16; let mut buf: Option> = None; + // A damaged file can make the demuxer report the same error forever without moving on. + let mut demux_errors = 0u32; loop { let packet = match format.next_packet() { - Ok(p) => p, + Ok(p) => { + demux_errors = 0; + p + } Err(SymError::IoError(_)) | Err(SymError::ResetRequired) => break, Err(SymError::DecodeError(e)) => { log::debug!("audio demux: {e}"); + demux_errors += 1; + if demux_errors > MAX_DEMUX_ERRORS { + break; + } continue; } Err(e) => { diff --git a/crates/media/src/probe.rs b/crates/media/src/probe.rs index 80a8a380..40afcbe8 100644 --- a/crates/media/src/probe.rs +++ b/crates/media/src/probe.rs @@ -58,8 +58,10 @@ pub fn probe(bytes: &Bytes) -> Result { }; if info.audio.is_none() && info.video.is_none() || info.audio.as_ref().is_some_and(|a| a.sample_rate == 0) { // Plain audio files (and anything our demuxers didn't describe): symphonia's probe. - let (codec, rate, channels, frames) = - crate::audio::probe_symphonia(bytes, c.name()).ok_or_else(|| MediaError::Unsupported("this kind of media file".into()))?; + let (codec, rate, channels, frames) = std::panic::catch_unwind(|| crate::audio::probe_symphonia(bytes, c.name())) + .ok() + .flatten() + .ok_or_else(|| MediaError::Unsupported("this kind of media file".into()))?; info.audio = Some(AudioInfo { codec: codec_label(&codec), sample_rate: rate, channels, decodable: true }); if info.duration_ms == 0 && let Some(n) = frames diff --git a/crates/media/tests/decode.rs b/crates/media/tests/decode.rs index b3179445..db53c056 100644 --- a/crates/media/tests/decode.rs +++ b/crates/media/tests/decode.rs @@ -325,3 +325,33 @@ fn player_plays_decoded_audio_through_the_mixer() { assert!((rms - 0.0884 * 0.5).abs() < 0.005, "{rms}"); assert!((p.status(1).map(|s| s.position).unwrap_or(0.0) - 0.1).abs() < 1e-6); } + +/// Damaged files found by mutation fuzzing the fixtures above (so CC0 like them): each must come +/// back as an error or as audio — not panic (symphonia's Matroska reader panics on the first two) +/// and not hang (the MP4 demuxer reported the same error forever on the last two). +#[test] +fn damaged_media_neither_panics_nor_hangs() { + for name in ["damaged/ebml-header.mkv", "damaged/ebml-position.webm", "damaged/demux-loop.mp4", "damaged/demux-loop.m4a"] { + let b = fixture(name); + let (tx, rx) = std::sync::mpsc::channel(); + std::thread::spawn(move || { + let ok = std::panic::catch_unwind(|| { + let _ = probe::probe(&b); + let _ = audio::decode(&b); + if let Ok(mut d) = video::VideoDecoder::open(b.clone()) { + for _ in 0..12 { + if !matches!(d.next_frame(), Ok(Some(_))) { + break; + } + } + } + }) + .is_ok(); + let _ = tx.send(ok); + }); + match rx.recv_timeout(Duration::from_secs(20)) { + Ok(ok) => assert!(ok, "{name} panicked"), + Err(_) => panic!("{name} still decoding after 20 s"), + } + } +} diff --git a/crates/media/tests/fixtures/damaged/demux-loop.m4a b/crates/media/tests/fixtures/damaged/demux-loop.m4a new file mode 100644 index 0000000000000000000000000000000000000000..9033ab9a65df2492b8d25184f70eb26a9e9942b8 GIT binary patch literal 5324 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 bza%A0=%0%WZVW#sA0JA_{{y`TxRd?`W;+`2 literal 0 HcmV?d00001 diff --git a/crates/media/tests/fixtures/damaged/demux-loop.mp4 b/crates/media/tests/fixtures/damaged/demux-loop.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..f6e6c0a27e15edd9dc125e284af323bd54c416a1 GIT binary patch literal 6102 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 literal 0 HcmV?d00001 diff --git a/crates/media/tests/fixtures/damaged/ebml-header.mkv b/crates/media/tests/fixtures/damaged/ebml-header.mkv new file mode 100644 index 0000000000000000000000000000000000000000..661a31775dd5801ce2e6bf88851e9808bca22f23 GIT binary patch literal 905 zcmb1gy}x*|Q(GgW({~{L)X3uWxsk)EsUtVBq$s~QJJG2fDAd}>BoW+@&d2})Ec*q0 zXO|q<-x5)It=D&Vckt>KQO}iW!D|{9LU>{!>#~8oGrqfffO0}1htq@CG%>nCxH7@J zfs#!u6W%~fV+aNtN?iivo@M)6oc=R=X!2=5yWYrHe6$1VB*r<73`$$)G?(V3bTl%wwT8tzh8Z?DGAciAZejiU zkm13U4UG=F8XaaeGKFkvWC`8S61uaIG4x#{1I&j^3=GMOCN?tgFmQ1L*{q@Uf0%wS zIDi7?=KhwGTZ3K{7hgy|(4CC1GyTMD*TwCwe$M_bu0byCeL%M-E97J*6_=GHmKmm> zSm?UA!zDDxF~rl~&!v5`fq@km>6sZo0fW5rwo<2O`&;f5?+AO|$gtSqdm{rw1Ka^w zKN}p39y1Cs%xGfb5OAKNq4L>w-f5=$0LG`MtP}-vNz@N2dPR zP=DGWX~~HPMi1v|xwW-TTw8NNEp@$lEa%r2(>Z24o(b6Y2dxiyA*8%=d0WTa{`t+5 z%BS<4zs&aY^fH5Mt>))m@jria+WqBB&8H8!&(wYica^u8)3Dg#K_f#7gV97r4HgCm z1_s+yhCn9{;R6f@7e)K(s>C6oLJC_>OPbr95 zd$wfg*R^H%s)}Kd(Edct_3li&psziQLvxS^!nyyor;x@e3YDn8jKV*n1BgF KWo{Eg;b8y;16joY literal 0 HcmV?d00001 diff --git a/crates/media/tests/fixtures/damaged/ebml-position.webm b/crates/media/tests/fixtures/damaged/ebml-position.webm new file mode 100644 index 0000000000000000000000000000000000000000..da5b80055b0d7b37585622af1f6474af17c81ed6 GIT binary patch literal 3914 zcmcJSc{o(<|Hsd`V6Lk!*SF$;=oAku_Ok3=tmtzVBO> z79vzCq(YSJ&G`-G`ToBD{Qmo$>nzvjy07#4Z14NNulqFe9We(Ui2>nTZu32(5*(ZM6BnaW4B@xMZd~S0w)%9N zW65Kz#@(()rC2cKw|T~>nLLO+v9tV}16Um5{y$y^QRQBmtPE#@!*tO&l)b-;nfl=e z5gi2!>p1jz5G-t<>`yRylNivqH|kF`U={}8tIZ1c^K^>B0Fe=pib=X+o-p#lSI!boOdC`>AFCXE=-WEBG(B}V}ON9eyMF%`@|d!SQ`cVKgA<1Nz>8yEvc9*Uua>jD69_7R z=;p)GgMz~fe0`rgcHDFPPcE(lSx6!tsi&cjKwd}SVc{ZR;T>?JM}f*_Lc+Id5k-8DVF>Kqc&#>?$8d>GA@ zW95!o>(UX>$TW9V78Y6!BngKwpV}*<;hTG~eWT)cGsz^7G-(Cxs9;VVSCI{a_8IQN zZ>bJB=kgXpdpukvguqez)^cdYRW2%QUqD*=n1dHeEN!3zyh!U?HIJrt%?1GC5EfuV z2R=GWIKBTsSFf1!m}~irI!59I7BD=5Ny)T_pg)ipwLAl^>PT}|!rRbc34D_`>*e+Y zovk7}({HGaFE{`OvHT2}4#_m|nfs5@C?QqJBFQ06UaAC$WVU$YlE+ff9*Xz^3poFq zw2`{sXgh26m#$<|NA*yW1UyceTkg}>adA(v!RKA-oKeepkc<~jHs?JeJJ`0LF0T10i$e0-AT z&T1Iz&=ZEn+von7<~Jn{wR+1h$Zc1mtW6=2bI`1Mbl6^3PdN<-Cj;}V=(H!gy(MR}htWokP@5NWhhZ2%BpdH&ZZi=_vm4^}G8Ja^Nvnma|KCL7(%R7l@T_qcT1FF}4mO1yKUO*#6*h363@JB*A z9fuO~6_g(w{^gT!?mZELGIB^@e`!ab zr4U47CcdGr9!?PQv9aRE35okai}+=*s@rRKVviL*YVPOHn02O8m-*f2@ZbL_CNn{@ zYIZCwv2@fsG9MHmYo{&EA%Q80m4LRZ8w)*Odjc2jx^jVop1k=tX&pZK20y&i10iM@ zAbs2Ye7mx1y24A3AB7y|p0IN+Y0jAu(p14N9sH_)w<3LLI8Z7l)K#B^UesXpigY2B zt>B2yu)z9JPh^x7N=0rauD+AI-rb74y-uO!InmSUHyUwFaWneOs_$yNl+P`rnKUc; zR3{OnazGM_ASJH`#xpvR5-UXlxu^8&$9l;Dg49PuDQ)VDI-MLTZyY!)Nr`he9)~=R z$t*XJ3;K#e>l(MXdLC0@jw}w0q-xSD3Kl&aZuCxMvGNwYA%t%bD~Lbot);Qk&e1#*{#{cZConr?yWS&mel=!3)}kM{ZhL zsN;D>dX-q|*Myj$pYr$aD%8ren4}I>ElKPG5VR}=p+!XIbg-e`AMa~>AQ5M2NtRdq zAqclsXk6WD`9T{^-cdmHqa=~vP>Zaw+8WE^$}hMbOA6u;mxntr@r+qlAdt=@Fh!A8oxUG1++} zi+V36v0BCHUSpVV+`D2q&E?@f7I#*X?fkimTBZzLq>=n~537dRc;;%#iTQJ@#1B|7 z6g^c*_>MPKnBl-o7wPy6Y&O~bB8C)q{B0>*mIOF<|wdGq0`I3=bbH*yP_I|i^lI1_xIn~T?? zapd*u_OPT%`?XL2DrWbutIYQ5x^iobd4{gqt%s41DA?npyN>&`N`z%#z=S``msalY z<%=xEoZT4lXSNHJXUvfp^N$d;*Zet8&1uRO`t=Qb{cL4Ggx*=Yq8B}Xd4#;P$>4Z` zWF#PHK=4OC2%Yh0x-wAgb2jX|RCg$*UKYOCs(w{mU)6w)mzKP!K~U5==v3BcN9eul zGIL`XM1n!FFo7|Qfo4YHM2B?PM6Qzdms*de7~r4+1?Z{r&!^AC9ic!hUfj1t zn=#Kb>qn60inn>Uv<8Kxi0)n8HcN*9mNl29M;HTFAEph1iM{p0i|!LRrE!K+=*wqe zYWIAwJ3RXO$PJ=vi7k^~eHv6*+CLgz0xKnTB z$m;(bbn>pOj*wMZSo=h{n02E{M>@NyQeir)hVm!x_n3}E=R2(m zIv)`h1CCGm0dG#_ye+krlM2_a?d4vr#`YV8FxHQo#|$N^1>J0b#MhUj8NXk-ntmt0 z|MdyEWqQHMJYMFs_(Lb}530r4@nA)BLmO#OaifwiHC3OFwx2FoMbIs?+*XI)?H_)r zx?^VJq6q}{yC|l@jQu?)xq*;^LPSKvOSJ4&A;GxDluLR>>%lnf!2<*QRbyzhuicmMoT@@w~&7rV-R>Xv@iGmm3s zyl53rGc6~W(|P}iKuP^tNGQ$3(D>!yfDw}2!D%+JIldYL)W?ud&FfeSwKdG0{{i!9 B2|)k= literal 0 HcmV?d00001