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 <mmirchev@comp.nus.edu.sg>
This commit is contained in:
Martin Mirchev authored and Romain Beauxis committed 2026-10-28 01:57:05 +00:00
1 parent 58584087cf
commit 94110fbc39
1 file changed
+20 -9
+20 -9
View File
@@ -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;