PR #25516 opened by Marti2203
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25516
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25516.patch

When a new chained Vorbis stream starts in an Ogg file, `vorbis_packet()` packs 
its three header packets into new extradata with 16-bit big-endian size 
prefixes. A header larger than 65535 bytes gets its size truncated. This is 
usually a comment header with embedded cover art (`METADATA_BLOCK_PICTURE`). 
The Vorbis parser then cannot be reinitialized ("Wrong packet type in Setup 
header", "Failed to re-initialize Vorbis parser") and demuxing stops with 
"Invalid data found when processing input". The audio of that chained stream is 
lost, and so is everything after it. The first stream of a file is not 
affected, because `vorbis_header()` builds its extradata with Xiph lacing.

The fix builds the chained-stream extradata with Xiph lacing as well. 
`avpriv_split_xiph_headers()`, the Vorbis parser and the decoder already accept 
that format.

Verification (master a497ab4d07, release and ASan builds):
- I cut 1 s from `vorbis/moog_small.ogg` with a comment of 1000, 65000, 70000 
or 300000 bytes and appended it to the full sample as a second chained stream. 
Before the fix, the 70000- and 300000-byte cases lose all 48 packets of the 
second stream. With a small third stream appended after it, that one is lost 
too (352 of 448 packets demuxed). The 1000- and 65000-byte cases are fine. 
After the fix, every case demuxes and decodes completely, and a chained copy 
decodes to the same PCM as the file on its own (checked with the file chained 
to itself). Both builds are ASan-clean.
- `fate-ogg-vorbis`, `fate-vorbis` (decode tests; `vorbis-encode-1` needs 
`audio-reference/`, which I did not have), `fate-oggtheora-demux`, 
`fate-oggvp8-demux` and `fate-limited_input_seek` pass unchanged.
- The new `fate-ogg-vorbis-chained-large-comment` (second patch) needs no new 
sample. It remuxes two 0.5 s pieces of `vorbis/moog_small.ogg`, the second with 
a 70000-byte comment read from a generated ffmetadata file. It then runs 
`framecrc -c copy` on their concatenation through the concat protocol. It fails 
without the fix (all 26 packets of the second stream are missing) and passes 
with it.

This issue was found by an LLM-Enhanced Static Analysis Engine and verified 
manually.

🤖 Generated with [Claude Code](https://claude.com/claude-code)



From 2f05793853e2dfbffc5aae7631e58741c4b334ca Mon Sep 17 00:00:00 2001
From: Martin Mirchev <[email protected]>
Date: Tue, 27 Oct 2026 11:49:49 +0800
Subject: [PATCH 1/2] avformat/oggparsevorbis: fix chained streams with headers
 over 64 KiB

When a chained Vorbis stream starts, its three header packets are
packed into new extradata with 16-bit size prefixes. A header larger
than 65535 bytes, typically a comment header with embedded cover art,
gets its size truncated, so the Vorbis parser cannot be reinitialized
and demuxing fails with "Failed to re-initialize Vorbis parser". The
audio of the new chained stream and everything after it is lost.

Use Xiph lacing instead, as vorbis_header() already does for the
initial extradata.

Signed-off-by: Martin Mirchev <[email protected]>
---
 libavformat/oggparsevorbis.c | 29 ++++++++++++++++++++---------
 1 file changed, 20 insertions(+), 9 deletions(-)

diff --git a/libavformat/oggparsevorbis.c b/libavformat/oggparsevorbis.c
index 3ad72cd69a4..18160ec3ddb 100644
--- a/libavformat/oggparsevorbis.c
+++ b/libavformat/oggparsevorbis.c
@@ -463,7 +463,6 @@ static int vorbis_packet(AVFormatContext *s, int idx)
     int duration, flags = 0;
     int bos, skip_packet = 0;
     int ret, new_extradata_size;
-    PutByteContext pb;
 
     if (os->psize == 0)
         return AVERROR_INVALIDDATA;
@@ -609,20 +608,32 @@ static int vorbis_packet(AVFormatContext *s, int idx)
     /* All three headers for the new chained stream have been collected;
      * pack them into new extradata to be attached to the next data packet. */
     if (priv->header && priv->comment && priv->setup) {
-        new_extradata_size = priv->header_size + priv->comment_size + 
priv->setup_size + 6;
+        uint64_t total_size = (uint64_t)priv->header_size + priv->comment_size 
+
+                              priv->setup_size;
+        uint8_t *p;
+
+        /* Use Xiph lacing like vorbis_header() does: 16-bit sizes cannot
+         * describe headers larger than 64 KiB, e.g. a comment header with
+         * embedded cover art. */
+        if (total_size + total_size / 255 + 3 > INT_MAX)
+            return AVERROR_INVALIDDATA;
+        new_extradata_size = 1 + priv->header_size / 255 + 1 +
+                             priv->comment_size / 255 + 1 + total_size;
 
         ret = av_reallocp(&os->new_extradata, new_extradata_size);
         if (ret < 0)
             return ret;
 
         os->new_extradata_size = new_extradata_size;
-        bytestream2_init_writer(&pb, os->new_extradata, new_extradata_size);
-        bytestream2_put_be16(&pb, priv->header_size);
-        bytestream2_put_buffer(&pb, priv->header, priv->header_size);
-        bytestream2_put_be16(&pb, priv->comment_size);
-        bytestream2_put_buffer(&pb, priv->comment, priv->comment_size);
-        bytestream2_put_be16(&pb, priv->setup_size);
-        bytestream2_put_buffer(&pb, priv->setup, priv->setup_size);
+        p     = os->new_extradata;
+        *p++  = 2;
+        p    += av_xiphlacing(p, priv->header_size);
+        p    += av_xiphlacing(p, priv->comment_size);
+        memcpy(p, priv->header, priv->header_size);
+        p    += priv->header_size;
+        memcpy(p, priv->comment, priv->comment_size);
+        p    += priv->comment_size;
+        memcpy(p, priv->setup, priv->setup_size);
 
         av_freep(&priv->header);
         priv->header_size = 0;
-- 
2.54.0


From 38b5196d3fbb259f11b567e2723738949a1f2aa2 Mon Sep 17 00:00:00 2001
From: Martin Mirchev <[email protected]>
Date: Tue, 27 Oct 2026 11:49:49 +0800
Subject: [PATCH 2/2] fate/ogg-vorbis: add a test for a chained stream with a
 large comment

Remux two short streams from an existing sample, the second one with a
70000-byte comment header, and demux their concatenation.

Signed-off-by: Martin Mirchev <[email protected]>
---
 tests/fate/ogg-vorbis.mak                     | 13 +++++
 .../ref/fate/ogg-vorbis-chained-large-comment | 58 +++++++++++++++++++
 2 files changed, 71 insertions(+)
 create mode 100644 tests/ref/fate/ogg-vorbis-chained-large-comment

diff --git a/tests/fate/ogg-vorbis.mak b/tests/fate/ogg-vorbis.mak
index e9151b63157..51d6a9e4d6a 100644
--- a/tests/fate/ogg-vorbis.mak
+++ b/tests/fate/ogg-vorbis.mak
@@ -7,6 +7,19 @@ fate-ogg-vorbis-copy-chained-meta: 
$(APITESTSDIR)/api-dump-stream-meta-test$(EXE
 fate-ogg-vorbis-copy-chained-meta: REF = 
$(SRC_PATH)/tests/ref/fate/ogg-vorbis-chained-meta.txt
 fate-ogg-vorbis-copy-chained-meta: CMD = run_with_temp "$(FFMPEG) -nostdin 
-hide_banner -loglevel quiet -i $(TARGET_SAMPLES)/ogg-vorbis/chained-meta.ogg 
-c copy -f ogg -y" "$(APITESTSDIR)/api-dump-stream-meta-test$(EXESUF)" ogg
 
+# Chain two streams cut from the same sample, the second one with a comment
+# header larger than 64 KiB (as with embedded cover art).
+FATE_OGG_COPY_VORBIS-$(call FRAMECRC, OGG FFMETADATA, VORBIS, OGG_MUXER 
CONCAT_PROTOCOL) += fate-ogg-vorbis-chained-large-comment
+fate-ogg-vorbis-chained-large-comment: CMD = \
+    cleanfiles="$${cleanfiles} $${outfile}.txt $${outfile}.1.ogg 
$${outfile}.2.ogg"; \
+    printf ";FFMETADATA1\ncomment=%070000d\n" 0 > "$${outfile}.txt" && \
+    ffmpeg -i $(TARGET_SAMPLES)/vorbis/moog_small.ogg -c copy -t 0.5 -fflags 
+bitexact \
+        -y "$$(target_path "$${outfile}.1.ogg")" && \
+    ffmpeg -i $(TARGET_SAMPLES)/vorbis/moog_small.ogg -i "$$(target_path 
"$${outfile}.txt")" \
+        -map 0 -map_metadata 1 -c copy -t 0.5 -fflags +bitexact -serial_offset 
1 \
+        -y "$$(target_path "$${outfile}.2.ogg")" && \
+    framecrc -i "concat:$$(target_path "$${outfile}.1.ogg")|$$(target_path 
"$${outfile}.2.ogg")" -c copy
+
 FATE_OGG_VORBIS_PROBE-$(call DEMDEC, OGG, VORBIS) += 
fate-ogg-vorbis-initial-padding-chained
 fate-ogg-vorbis-initial-padding-chained: CMD = ffprobe_demux -of default 
-show_entries stream=codec_name,initial_padding 
$(TARGET_SAMPLES)/ogg-vorbis/chained-meta.ogg
 
diff --git a/tests/ref/fate/ogg-vorbis-chained-large-comment 
b/tests/ref/fate/ogg-vorbis-chained-large-comment
new file mode 100644
index 00000000000..20ca11e82cd
--- /dev/null
+++ b/tests/ref/fate/ogg-vorbis-chained-large-comment
@@ -0,0 +1,58 @@
+#extradata 0:    12456, 0xa1161932
+#tb 0: 1/44100
+#media_type 0: audio
+#codec_id 0: vorbis
+#sample_rate 0: 44100
+#channel_layout_name 0: stereo
+0,       -128,       -128,      128,        3, 0x00000000, S=1, Strings 
Metadata,      106, 0xe088249e
+0,          0,          0,      576,        4, 0x0028000a
+0,        576,        576,     1024,        4, 0x0038000e
+0,       1600,       1600,     1024,        4, 0x0038000e
+0,       2624,       2624,     1024,        4, 0x00180006
+0,       3648,       3648,      576,        3, 0x00000000
+0,       4224,       4224,      128,       54, 0x014c1a96
+0,       4352,       4352,      128,       55, 0x2f911b84
+0,       4480,       4480,      576,      420, 0xf6dbdbbc
+0,       5056,       5056,     1024,      387, 0x0c56c7ba
+0,       6080,       6080,     1024,      393, 0x6327c817
+0,       7104,       7104,     1024,      358, 0x578bb0b5
+0,       8128,       8128,     1024,      301, 0x3f658f2f
+0,       9152,       9152,     1024,      303, 0xe1a2955f
+0,      10176,      10176,     1024,      311, 0x4f829c54
+0,      11200,      11200,     1024,      308, 0x1d1a9ae7
+0,      12224,      12224,     1024,      282, 0x10428728
+0,      13248,      13248,     1024,      274, 0xd201846b
+0,      14272,      14272,     1024,      297, 0x688a8dee
+0,      15296,      15296,     1024,      279, 0x01fc85c1
+0,      16320,      16320,     1024,      299, 0xeb658ff2
+0,      17344,      17344,     1024,      286, 0x01569307
+0,      18368,      18368,     1024,      307, 0x331a8a6a
+0,      19392,      19392,     1024,      293, 0x59eb908a
+0,      20416,      20416,     1024,      294, 0x11dc9474
+0,      21440,      21440,     1024,      300, 0x7c9d9254, S=1, Skip Samples,  
     10, 0x03000080
+0,      22464,      22464,      128,        3, 0x00000000, S=2, Strings 
Metadata,    70115, 0xf8df6f8e, New Extradata,    82871, 0x33479e75
+0,      22592,      22592,      576,        4, 0x0028000a
+0,      23168,      23168,     1024,        4, 0x0038000e
+0,      24192,      24192,     1024,        4, 0x0038000e
+0,      25216,      25216,     1024,        4, 0x00180006
+0,      26240,      26240,      576,        3, 0x00000000
+0,      26816,      26816,      128,       54, 0x014c1a96
+0,      26944,      26944,      128,       55, 0x2f911b84
+0,      27072,      27072,      576,      420, 0xf6dbdbbc
+0,      27648,      27648,     1024,      387, 0x0c56c7ba
+0,      28672,      28672,     1024,      393, 0x6327c817
+0,      29696,      29696,     1024,      358, 0x578bb0b5
+0,      30720,      30720,     1024,      301, 0x3f658f2f
+0,      31744,      31744,     1024,      303, 0xe1a2955f
+0,      32768,      32768,     1024,      311, 0x4f829c54
+0,      33792,      33792,     1024,      308, 0x1d1a9ae7
+0,      34816,      34816,     1024,      282, 0x10428728
+0,      35840,      35840,     1024,      274, 0xd201846b
+0,      36864,      36864,     1024,      297, 0x688a8dee
+0,      37888,      37888,     1024,      279, 0x01fc85c1
+0,      38912,      38912,     1024,      299, 0xeb658ff2
+0,      39936,      39936,     1024,      286, 0x01569307
+0,      40960,      40960,     1024,      307, 0x331a8a6a
+0,      41984,      41984,     1024,      293, 0x59eb908a
+0,      43008,      43008,     1024,      294, 0x11dc9474
+0,      44032,      44032,     1024,      300, 0x7c9d9254, S=1, Skip Samples,  
     10, 0x03000080
-- 
2.54.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to