diff --git a/src/audio_signature.cpp b/src/audio_signature.cpp index 6e7f6c2..27684a8 100644 --- a/src/audio_signature.cpp +++ b/src/audio_signature.cpp @@ -155,9 +155,24 @@ struct DecodeCtx { bool open_resampler(DecodeCtx& c, const AVFrame* f) { #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 24, 100) - AVChannelLayout out_layout; + // Both MUST be zero-initialised. av_channel_layout_copy documents that it + // "will always uninitialize the destination before copy", and + // av_channel_layout_uninit() calls av_freep() on u.map — so a declaration + // without {} hands free() whatever pointer-shaped garbage the stack frame + // happened to hold. That is a real crash ("free(): invalid pointer"), not a + // theoretical one: it reproduced in roughly 1 run in 4 of UT-103, the only + // test that exercises this branch, because it is the only one whose input + // is stereo and so the only one that reaches the downmix path at all. + // + // It hid for two reasons worth remembering. It is stack-dependent, so it + // vanishes under a sanitizer build and looks like a flake in the aggregate + // test binary; and the golden-vector tests (UT-101) pass a mono 11025 Hz + // fixture, which is chosen precisely so the vector does not depend on the + // resampler — so bit-exactness against the golden vector proves nothing + // about this function. + AVChannelLayout out_layout{}; av_channel_layout_default(&out_layout, 1); // mono - AVChannelLayout in_layout; + AVChannelLayout in_layout{}; if (av_channel_layout_copy(&in_layout, &f->ch_layout) < 0) return false; if (in_layout.nb_channels <= 0) { av_channel_layout_uninit(&in_layout);