PR #25552 opened by Romain Beauxis (toots) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25552 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25552.patch
>From 88d8ae7b23879cb45e5307f5030ac10633214626 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Tue, 6 Oct 2026 07:33:05 +0200 Subject: [PATCH 1/3] avformat/oggenc: Reset the current page when it cannot be buffered On allocation failure the full page stayed in the stream context, and the next segment was added past its 255 entry segment table. Fixes: out of array access Fixes: flOsaBhTFc6F Fixes: AISLE-2026-0111-00072 Negative size memset Replicated through API with ASAN and av_max_alloc() lowered after the header Found-by: Joshua Rogers <[email protected]> (cherry picked from commit 6d40aa17395aaba80922441bcfb58399a88475e3) --- libavformat/oggenc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libavformat/oggenc.c b/libavformat/oggenc.c index 5c7e3da0bcd..cee9c012239 100644 --- a/libavformat/oggenc.c +++ b/libavformat/oggenc.c @@ -192,8 +192,10 @@ static int ogg_buffer_page(AVFormatContext *s, OGGStreamContext *oggstream) OGGPageList **p = &ogg->page_list; OGGPageList *l = av_mallocz(sizeof(*l)); - if (!l) + if (!l) { + ogg_reset_cur_page(oggstream); return AVERROR(ENOMEM); + } l->page = oggstream->page; oggstream->page.start_granule = ogg_granule_to_timestamp(oggstream, oggstream->page.granule); -- 2.54.0 >From 53f1941354b2b13a00dd072c11bc1c874121ab33 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Wed, 7 Oct 2026 00:03:31 +0200 Subject: [PATCH 2/3] avformat/oggenc: Propagate page buffering errors Return the error, as recommended in the report AISLE-2026-0111-00072. Not a security issue. Fixes: TjbJ8BXPA93x Found-by: Joshua Rogers <[email protected]> (cherry picked from commit a659cd842b122e2f41973ac7c4e826426cc0d756) --- libavformat/oggenc.c | 60 ++++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/libavformat/oggenc.c b/libavformat/oggenc.c index cee9c012239..43317e943ef 100644 --- a/libavformat/oggenc.c +++ b/libavformat/oggenc.c @@ -221,7 +221,7 @@ static int ogg_buffer_data(AVFormatContext *s, AVStream *st, OGGContext *ogg = s->priv_data; int total_segments = size / 255 + 1; const uint8_t *p = data; - int i, segments, len, flush = 0; + int i, segments, len, ret, flush = 0; // Handles VFR by flushing page because this frame needs to have a timestamp // For theora and VP8, keyframes also need to have a timestamp to correctly mark @@ -232,15 +232,17 @@ static int ogg_buffer_data(AVFormatContext *s, AVStream *st, (ogg_granule_to_timestamp(oggstream, granule) > ogg_granule_to_timestamp(oggstream, oggstream->last_granule) + 1 || ogg_key_granule(oggstream, granule))) { - if (oggstream->page.granule != -1) - ogg_buffer_page(s, oggstream); + if (oggstream->page.granule != -1 && + (ret = ogg_buffer_page(s, oggstream)) < 0) + return ret; flush = 1; } // avoid a continued page if (!header && oggstream->page.size > 0 && MAX_PAGE_SIZE - oggstream->page.size < size) { - ogg_buffer_page(s, oggstream); + if ((ret = ogg_buffer_page(s, oggstream)) < 0) + return ret; } for (i = 0; i < total_segments; ) { @@ -275,17 +277,20 @@ static int ogg_buffer_data(AVFormatContext *s, AVStream *st, time_base, AV_TIME_BASE_Q); if (page->segments_count == 255) { - ogg_buffer_page(s, oggstream); + if ((ret = ogg_buffer_page(s, oggstream)) < 0) + return ret; } else if (!header) { if (ogg->pref_duration > 0 && next - start >= ogg->pref_duration) { - ogg_buffer_page(s, oggstream); + if ((ret = ogg_buffer_page(s, oggstream)) < 0) + return ret; } } } } - if (flush && oggstream->page.granule != -1) - ogg_buffer_page(s, oggstream); + if (flush && oggstream->page.granule != -1 && + (ret = ogg_buffer_page(s, oggstream)) < 0) + return ret; return 0; } @@ -630,24 +635,33 @@ static int ogg_init(AVFormatContext *s) static int ogg_write_header(AVFormatContext *s) { OGGStreamContext *oggstream = NULL; - int i, j; + int i, j, ret; for (j = 0; j < s->nb_streams; j++) { oggstream = s->streams[j]->priv_data; - ogg_buffer_data(s, s->streams[j], oggstream->header[0], - oggstream->header_len[0], 0, 1); + ret = ogg_buffer_data(s, s->streams[j], oggstream->header[0], + oggstream->header_len[0], 0, 1); + if (ret < 0) + return ret; oggstream->page.flags |= 2; // bos - ogg_buffer_page(s, oggstream); + ret = ogg_buffer_page(s, oggstream); + if (ret < 0) + return ret; } for (j = 0; j < s->nb_streams; j++) { AVStream *st = s->streams[j]; oggstream = st->priv_data; for (i = 1; i < 3; i++) { - if (oggstream->header_len[i]) - ogg_buffer_data(s, st, oggstream->header[i], - oggstream->header_len[i], 0, 1); + if (oggstream->header_len[i]) { + ret = ogg_buffer_data(s, st, oggstream->header[i], + oggstream->header_len[i], 0, 1); + if (ret < 0) + return ret; + } } - ogg_buffer_page(s, oggstream); + ret = ogg_buffer_page(s, oggstream); + if (ret < 0) + return ret; } oggstream->page.start_granule = AV_NOPTS_VALUE; @@ -768,15 +782,16 @@ static int ogg_write_packet_internal(AVFormatContext *s, AVPacket *pkt) static int ogg_write_packet(AVFormatContext *s, AVPacket *pkt) { - int i; + int i, ret; if (pkt) return pkt->size || !pkt->side_data_elems ? ogg_write_packet_internal(s, pkt) : 0; for (i = 0; i < s->nb_streams; i++) { OGGStreamContext *oggstream = s->streams[i]->priv_data; - if (oggstream->page.segments_count) - ogg_buffer_page(s, oggstream); + if (oggstream->page.segments_count && + (ret = ogg_buffer_page(s, oggstream)) < 0) + return ret; } ogg_write_pages(s, 2); @@ -785,14 +800,15 @@ static int ogg_write_packet(AVFormatContext *s, AVPacket *pkt) static int ogg_write_trailer(AVFormatContext *s) { - int i; + int i, ret; /* flush current page if needed */ for (i = 0; i < s->nb_streams; i++) { OGGStreamContext *oggstream = s->streams[i]->priv_data; - if (oggstream->page.segments_count) - ogg_buffer_page(s, oggstream); + if (oggstream->page.segments_count && + (ret = ogg_buffer_page(s, oggstream)) < 0) + return ret; } ogg_write_pages(s, 1); -- 2.54.0 >From 99b632d544941f305595b4ed005a4fa3ad82f0ea Mon Sep 17 00:00:00 2001 From: Martin Mirchev <[email protected]> Date: Tue, 27 Oct 2026 11:49:49 +0800 Subject: [PATCH 3/3] 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]> (cherry picked from commit 94110fbc39a85f442530fc73d5c9b906d2551244) --- libavformat/oggparsevorbis.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/libavformat/oggparsevorbis.c b/libavformat/oggparsevorbis.c index 4044ad3bf75..71de123a941 100644 --- a/libavformat/oggparsevorbis.c +++ b/libavformat/oggparsevorbis.c @@ -458,7 +458,6 @@ static int vorbis_packet(AVFormatContext *s, int idx) int duration, flags = 0; int skip_packet = 0; int ret, new_extradata_size; - PutByteContext pb; if (!priv->vp) return AVERROR_INVALIDDATA; @@ -587,20 +586,32 @@ static int vorbis_packet(AVFormatContext *s, int idx) } 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 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
