Commit 2d7eacee24 for ffmpeg
commit 2d7eacee24b6dc5a150ccbd4f71a4f17cb8efe96
Author: Jun Zhao <barryjzhao@tencent.com>
Date: Fri Nov 6 15:03:34 2026 +0800
avcodec/flac_parser: reject variable-blocksize false syncs in fixed streams
A false sync inside the final frame of a short stream can survive the
header CRC and subframe sanity checks, then be emitted again on flush.
Reject variable-blocksize candidates when STREAMINFO declares equal
minimum and maximum block sizes. This rejects the false header in the
sample from #25766 without rejecting supported sample-rate or channel
changes. Apply the check only during sync search: complete container
packets already have boundaries, and chained Ogg packets can carry new
STREAMINFO only on the first packet of a link.
This narrows candidate selection; it does not change the EOF handling of
other false headers or streams without STREAMINFO.
Fixes: https://code.ffmpeg.org/FFmpeg/FFmpeg/issues/25766
Signed-off-by: Jun Zhao <barryjzhao@tencent.com>
diff --git a/libavcodec/flac_parser.c b/libavcodec/flac_parser.c
index f50b0b262d..8ee7439f7c 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,41 @@ typedef struct FLACParseContext {
int last_fi_valid; /**< set if last_fi is valid */
} FLACParseContext;
+/**
+ * Reject a variable-blocksize candidate when STREAMINFO declares a fixed
+ * blocksize. This is only a sync-search check: complete container packets
+ * already have known boundaries, and their STREAMINFO can change mid-stream.
+ */
+static int flac_blocking_strategy_matches(const AVCodecContext *avctx,
+ const FLACFrameInfo *fi)
+{
+ const uint8_t *si = avctx->extradata;
+ int size = avctx->extradata_size;
+ int min_blocksize, max_blocksize;
+
+ /* Raw/headerless FLAC has no STREAMINFO to validate against. */
+ if (!si || size < FLAC_STREAMINFO_SIZE)
+ return 1;
+
+ /* Also accept a 4-byte marker + metadata block header before 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);
+ /* 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)
{
@@ -322,7 +360,8 @@ static int find_headers_search_validate(FLACParseContext *fpc, int offset)
MAX_FRAME_VERIFY_SIZE + AV_INPUT_BUFFER_PADDING_SIZE,
&fpc->wrap_buf,
&fpc->wrap_buf_allocated_size);
- if (frame_header_is_valid(fpc->avctx, header_buf, &fi)) {
+ if (frame_header_is_valid(fpc->avctx, header_buf, &fi) &&
+ flac_blocking_strategy_matches(fpc->avctx, &fi)) {
FLACHeaderMarker **end_handle = &fpc->headers, *prev = NULL;
int i;