Commit a6e9b539f7 for ffmpeg

commit a6e9b539f7af80690a4cf5cc98ce4f72b8fd270d
Author: Michael Niedermayer <michael@niedermayer.cc>
Date:   Sun Nov 1 14:34:12 2026 +0100

    avformat/mov: ignore sample description atoms outside a trak

    Make get_curr_st() return NULL when neither a trak nor a HEIF item is
    being parsed, and use it in the readers that rewrite the codec id. The
    RTP QuickTime payload parser builds its own MOVContext for the sample
    description it receives, so it has to set up the same state.

    Fixes: heap-buffer-overflow in libopus_decode()
    Fixes: mov/esds-outside-trak.mp4
    Fixes: JWOFdec1AfGI
    Fixes: #24931
    Reported-by: Thomas Guilbert <tguilbert@chromium.org>

diff --git a/libavformat/mov.c b/libavformat/mov.c
index c62d4fe239..edab98a037 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -212,7 +212,8 @@ static HEIFItem *get_heif_item(MOVContext *c, unsigned id)

 /**
  * Get the current stream in the parsing process. This can either be the
- * latest stream added to the context, or the stream referenced by an item.
+ * stream of the trak being parsed, or the stream referenced by an item.
+ * Returns NULL when neither a trak nor an item is being parsed.
  */
 static AVStream *get_curr_st(MOVContext *c)
 {
@@ -222,8 +223,13 @@ static AVStream *get_curr_st(MOVContext *c)
     if (c->fc->nb_streams < 1)
         return NULL;

-    if (c->cur_item_id == -1)
+    if (c->cur_item_id == -1) {
+        if (c->trak_index < 0) {
+            av_log(c->fc, AV_LOG_WARNING, "Sample description atom outside TRAK\n");
+            return NULL;
+        }
         return c->fc->streams[c->fc->nb_streams-1];
+    }

     item = get_heif_item(c, c->cur_item_id);
     if (item)
@@ -865,6 +871,8 @@ static int mov_read_hdlr(MOVContext *c, AVIOContext *pb, MOVAtom atom)

 static int mov_read_esds(MOVContext *c, AVIOContext *pb, MOVAtom atom)
 {
+    if (!get_curr_st(c))
+        return 0;
     return ff_mov_read_esds(c->fc, pb);
 }

@@ -1361,9 +1369,9 @@ static int mov_read_wfex(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     AVStream *st;
     int ret;

-    if (c->fc->nb_streams < 1)
+    st = get_curr_st(c);
+    if (!st)
         return 0;
-    st = c->fc->streams[c->fc->nb_streams-1];

     if ((ret = ff_get_wav_header(c->fc, pb, st->codecpar, atom.size, 0)) < 0)
         av_log(c->fc, AV_LOG_WARNING, "get_wav_header failed\n");
@@ -2093,13 +2101,12 @@ static int mov_read_mvhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     return 0;
 }

-static void set_last_stream_little_endian(AVFormatContext *fc)
+static void set_curr_stream_little_endian(MOVContext *c)
 {
-    AVStream *st;
+    AVStream *st = get_curr_st(c);

-    if (fc->nb_streams < 1)
+    if (!st)
         return;
-    st = fc->streams[fc->nb_streams-1];

     switch (st->codecpar->codec_id) {
     case AV_CODEC_ID_PCM_S16BE:
@@ -2127,7 +2134,7 @@ static int mov_read_enda(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     int little_endian = avio_rb16(pb) & 0xFF;
     av_log(c->fc, AV_LOG_TRACE, "enda %d\n", little_endian);
     if (little_endian == 1)
-        set_last_stream_little_endian(c->fc);
+        set_curr_stream_little_endian(c);
     return 0;
 }

@@ -2158,10 +2165,9 @@ static int mov_read_pcmc(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     format_flags = avio_r8(pb);
     pcm_sample_size = avio_r8(pb);

-    if (fc->nb_streams < 1)
-        return AVERROR_INVALIDDATA;
-
-    st = fc->streams[fc->nb_streams - 1];
+    st = get_curr_st(c);
+    if (!st)
+        return 0;
     sc = st->priv_data;

     if (sc->format == MOV_MP4_FPCM_TAG) {
@@ -2202,7 +2208,7 @@ static int mov_read_pcmc(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     }

     if (format_flags & 1) // indicates little-endian format. If not present, big-endian format is used
-        set_last_stream_little_endian(c->fc);
+        set_curr_stream_little_endian(c);
     st->codecpar->bits_per_coded_sample = av_get_bits_per_sample(st->codecpar->codec_id);

     return 0;
@@ -2726,7 +2732,7 @@ static int mov_read_cdsc_rndr(MOVContext* c, AVIOContext* pb, MOVAtom atom)

     st = get_curr_st(c);
     if (!st)
-        return AVERROR_INVALIDDATA;
+        return 0;
     sc = st->priv_data;

     MovTref *tag = mov_add_tref_tag(sc, atom.type);
@@ -3477,9 +3483,9 @@ static int mov_read_stsd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     MOVStreamContext *sc;
     int ret, entries;

-    if (c->fc->nb_streams < 1)
+    st = get_curr_st(c);
+    if (!st)
         return 0;
-    st = c->fc->streams[c->fc->nb_streams - 1];
     sc = st->priv_data;

     if (sc->extradata) {
@@ -7911,9 +7917,9 @@ static int mov_read_frma(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     enum AVCodecID id;
     AVStream *st;

-    if (c->fc->nb_streams < 1)
+    st = get_curr_st(c);
+    if (!st)
         return 0;
-    st = c->fc->streams[c->fc->nb_streams - 1];
     sc = st->priv_data;

     switch (sc->format)
diff --git a/libavformat/rtpdec_qt.c b/libavformat/rtpdec_qt.c
index af00a7e767..ce149d9723 100644
--- a/libavformat/rtpdec_qt.c
+++ b/libavformat/rtpdec_qt.c
@@ -146,6 +146,8 @@ static int qt_rtp_parse_packet(AVFormatContext *s, PayloadContext *qt,
                 if (!mc)
                     return AVERROR(ENOMEM);
                 mc->fc = s;
+                mc->cur_item_id = -1;
+                mc->trak_index  = st->index;
                 st->priv_data = msc = av_mallocz(sizeof(MOVStreamContext));
                 if (!msc) {
                     av_free(mc);