PR #25806 opened by Jun Zhao (mypopydev) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25806 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25806.patch
A false sync pattern inside a frame's residual can pass both the CRC-8 header check and the subframe sanity checks. When it sits inside the final frame of a short stream, the parser outputs that final frame with the remaining buffer when flushing at EOF, but leaves the false header queued and emits a spurious zero-duration junk packet on the next flush, which the decoder rejects with "invalid residual". Validate candidate headers against the STREAMINFO supplied by the demuxer, rejecting those whose sample rate, channel count or blocking strategy contradicts it. bps and blocksize are deliberately not checked: the decoder already handles frames that legitimately disagree with STREAMINFO. Raw/headerless FLAC has no extradata, so no check is done there. Fixes: https://code.ffmpeg.org/FFmpeg/FFmpeg/issues/25766 Signed-off-by: Jun Zhao <[email protected]> # Summary of changes Briefly describe what this PR does and why. <!-- If this PR requires new FATE test samples, attach them to the PR and list their target paths below (relative to the fate-suite root). Attached filenames must match the sample's filename: ```fate-samples # e.g. vorbis/new-sample.ogg ``` --> >From e9c7199464b60a0f65ac90b7e536f62357ad1ac9 Mon Sep 17 00:00:00 2001 From: Jun Zhao <[email protected]> Date: Fri, 6 Nov 2026 15:03:34 +0800 Subject: [PATCH] avcodec/flac_parser: validate frame headers against STREAMINFO A false sync pattern inside a frame's residual can pass both the CRC-8 header check and the subframe sanity checks. When it sits inside the final frame of a short stream, the parser outputs that final frame with the remaining buffer when flushing at EOF, but leaves the false header queued and emits a spurious zero-duration junk packet on the next flush, which the decoder rejects with "invalid residual". Validate candidate headers against the STREAMINFO supplied by the demuxer, rejecting those whose sample rate, channel count or blocking strategy contradicts it. bps and blocksize are deliberately not checked: the decoder already handles frames that legitimately disagree with STREAMINFO. Raw/headerless FLAC has no extradata, so no check is done there. Fixes: https://code.ffmpeg.org/FFmpeg/FFmpeg/issues/25766 Signed-off-by: Jun Zhao <[email protected]> --- libavcodec/flac_parser.c | 59 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/libavcodec/flac_parser.c b/libavcodec/flac_parser.c index f50b0b262d7..9b2ef099abf 100644 --- a/libavcodec/flac_parser.c +++ b/libavcodec/flac_parser.c @@ -34,7 +34,10 @@ #include "libavutil/attributes.h" #include "libavutil/crc.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/macros.h" #include "libavutil/mem.h" +#include "flac.h" #include "flac_parse.h" #include "parser_internal.h" @@ -102,6 +105,59 @@ typedef struct FLACParseContext { int last_fi_valid; /**< set if last_fi is valid */ } FLACParseContext; +/** + * Reject a candidate frame header whose absolute properties contradict the + * STREAMINFO supplied by the demuxer. extradata is re-read on every call: + * demux.c temporarily swaps it for AV_PKT_DATA_NEW_EXTRADATA when a chained + * Ogg FLAC link starts. ff_flac_parse_streaminfo() cannot be used here as it + * modifies avctx and logs. + * + * bps and blocksize are deliberately not checked: a stream may legitimately + * contain frames that disagree with STREAMINFO (and rely on the decoder to + * reject them), while the sample rate, channel count and blocking strategy + * are fixed for the whole stream. + */ +static int flac_streaminfo_matches(const AVCodecContext *avctx, + const FLACFrameInfo *fi) +{ + const uint8_t *si = avctx->extradata; + int size = avctx->extradata_size; + uint64_t v; + unsigned samplerate, channels; + int min_blocksize, max_blocksize; + + /* Raw/headerless FLAC has no STREAMINFO to validate against. */ + if (!si || size < FLAC_STREAMINFO_SIZE) + return 1; + + /* Matroska stores the 4-byte marker + metadata block header + STREAMINFO. */ + if (AV_RL32(si) == MKTAG('f', 'L', 'a', 'C')) { + if (size < 8 + FLAC_STREAMINFO_SIZE) + return 1; + si += 8; + } + + min_blocksize = AV_RB16(si); + max_blocksize = AV_RB16(si + 2); + v = AV_RB64(si + 10); + samplerate = v >> 44; + channels = ((v >> 41) & 7) + 1; + + /* A zero sample rate in either place means "unknown / use STREAMINFO". */ + if (samplerate && fi->samplerate && fi->samplerate != samplerate) + return 0; + if (fi->channels != channels) + return 0; + /* A fixed-blocksize stream cannot contain variable-blocksize frames. + * Compare the strategy bit, not the blocksize: the final frame of a + * fixed stream may legitimately be shorter than the declared size. */ + if (min_blocksize && max_blocksize && min_blocksize == max_blocksize && + fi->is_var_size) + return 0; + + return 1; +} + static int frame_header_is_valid(AVCodecContext *avctx, const uint8_t *buf, FLACFrameInfo *fi) { @@ -133,6 +189,9 @@ static int frame_header_is_valid(AVCodecContext *avctx, const uint8_t *buf, return 0; } + if (!flac_streaminfo_matches(avctx, fi)) + return 0; + return 1; } -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
