PR #24931 opened by tguilbert-google
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24931
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24931.patch

# Summary of changes

This fixes a heap buffer overflow in `libopus_decode()`. If a demuxer
changes a stream's codec ID during `avformat_find_stream_info()`, the
new codec's parser runs on the context of the still-open decoder, and
can shrink the channel count used to size the output buffer.
The first commit closes the decoder and parser when the codec ID
changes. The second makes the libopus wrapper refuse to decode if the
channel count or sample format changed since init.


>From bcddef7158cdb31766fafa3eb24a6b5726f141f2 Mon Sep 17 00:00:00 2001
From: Thomas Guilbert <[email protected]>
Date: Mon, 5 Oct 2026 21:32:05 +0000
Subject: [PATCH 1/2] avformat/demux: Update the context when the codec ID
 changes

Demuxers can change `codecpar->codec_id` without setting
`need_context_update`, e.g. mov with a top-level `esds` box. The new
codec's parser then runs on the context of the still-open decoder, and
can change the channel count used to size its output buffers.

This commit treats a codec ID mismatch on an open decoder as a context
update.

Fixes: heap-buffer-overflow in opus_copy_channel_out_short
---
 libavformat/demux.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/libavformat/demux.c b/libavformat/demux.c
index 97eaa5f9bc..6be8fdc2d9 100644
--- a/libavformat/demux.c
+++ b/libavformat/demux.c
@@ -1423,6 +1423,13 @@ static int read_frame_internal(AVFormatContext *s, 
AVPacket *pkt)
         if (new_extradata)
             sti->need_context_update = 1;
 
+        /* Some demuxers change codecpar->codec_id without requesting a
+         * context update. Treat it as one, so that we never run a parser or
+         * decoder for one codec on a context opened for another. */
+        if (avcodec_is_open(sti->avctx) &&
+            sti->avctx->codec_id != st->codecpar->codec_id)
+            sti->need_context_update = 1;
+
         /* update context if required */
         if (sti->need_context_update) {
             if (avcodec_is_open(sti->avctx)) {
-- 
2.52.0


>From 44a16452f039e9bcf33dca53c7835bdac6507596 Mon Sep 17 00:00:00 2001
From: Thomas Guilbert <[email protected]>
Date: Mon, 5 Oct 2026 21:33:14 +0000
Subject: [PATCH 2/2] avcodec/libopusdec: Check the output parameters before
 decoding

`libopus_decode()` sizes its output buffer from `avc`, but libopus
writes as many channels as it was created with. This commit refuses to
decode if the channel count or sample format changed since init.

Fixes: heap-buffer-overflow in opus_copy_channel_out_short
---
 libavcodec/libopusdec.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/libavcodec/libopusdec.c b/libavcodec/libopusdec.c
index fc2119baff..6721694802 100644
--- a/libavcodec/libopusdec.c
+++ b/libavcodec/libopusdec.c
@@ -39,6 +39,8 @@ struct libopus_context {
     AVClass *class;
     OpusMSDecoder *dec;
     int pre_skip;
+    int channels;
+    enum AVSampleFormat sample_fmt;
 #ifndef OPUS_SET_GAIN
     union { int i; double d; } gain;
 #endif
@@ -113,6 +115,8 @@ static av_cold int libopus_decode_init(AVCodecContext *avc)
                opus_strerror(ret));
         return ff_opus_error_to_averror(ret);
     }
+    opus->channels   = channels;
+    opus->sample_fmt = avc->sample_fmt;
 
 #ifdef OPUS_SET_GAIN
     ret = opus_multistream_decoder_ctl(opus->dec, OPUS_SET_GAIN(gain_db));
@@ -163,6 +167,15 @@ static int libopus_decode(AVCodecContext *avc, AVFrame 
*frame,
     struct libopus_context *opus = avc->priv_data;
     int ret, nb_samples;
 
+    /* The output buffer is sized from avc, but libopus writes interleaved
+     * samples for as many channels as the decoder was created with. */
+    if (avc->ch_layout.nb_channels != opus->channels ||
+        avc->sample_fmt != opus->sample_fmt) {
+        av_log(avc, AV_LOG_ERROR,
+               "Channel count or sample format changed since init.\n");
+        return AVERROR(EINVAL);
+    }
+
     frame->nb_samples = MAX_FRAME_SIZE;
     if ((ret = ff_get_buffer(avc, frame, 0)) < 0)
         return ret;
-- 
2.52.0

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

Reply via email to