PR #25574 opened by Romain Beauxis (toots)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25574
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25574.patch

Header packets of a chained stream are only buffered in identification,
comment, setup order. A misplaced packet discards the buffered set, so
a missing or misordered header no longer mixes headers from different
chains into the new extradata.


>From b1cfc30c130baf471ec39c1051f1911527256bf9 Mon Sep 17 00:00:00 2001
From: Romain Beauxis <[email protected]>
Date: Wed, 28 Oct 2026 16:33:52 -0500
Subject: [PATCH] avformat/oggparsevorbis: enforce header order in chained
 streams

Header packets of a chained stream are only buffered in identification,
comment, setup order. A misplaced packet discards the buffered set, so
a missing or misordered header no longer mixes headers from different
chains into the new extradata.
---
 libavformat/oggparsevorbis.c | 65 ++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)

diff --git a/libavformat/oggparsevorbis.c b/libavformat/oggparsevorbis.c
index 5a23fc44502..82f5aa23beb 100644
--- a/libavformat/oggparsevorbis.c
+++ b/libavformat/oggparsevorbis.c
@@ -209,9 +209,17 @@ int ff_vorbis_comment(AVFormatContext *as, AVDictionary 
**m,
  * [framing_flag] = read one bit | Not Used
  */
 
+enum VorbisPacketType {
+    VORBIS_PACKET_DATA,
+    VORBIS_PACKET_ID,
+    VORBIS_PACKET_COMMENT,
+    VORBIS_PACKET_SETUP,
+};
+
 struct oggvorbis_private {
     unsigned int len[3];
     unsigned char *packet[3];
+    enum VorbisPacketType last_packet; ///< type of the last packet seen by 
vorbis_packet()
     AVVorbisParseContext *vp;
     int64_t bos_page_pos;
     int ts_offset;
@@ -445,6 +453,53 @@ static int vorbis_header(AVFormatContext *s, int idx)
     return 1;
 }
 
+/**
+ * Check that a packet follows the identification, comment, setup order of a
+ * chained stream's headers, and record it as the last packet.
+ *
+ * An identification header starts a new set. A misplaced packet discards the
+ * buffered headers, so an incomplete or misordered set is never packed.
+ *
+ * @return 0 if the packet is in order, AVERROR_INVALIDDATA otherwise
+ */
+static int vorbis_check_packet_order(AVFormatContext *s,
+                                     struct oggvorbis_private *priv,
+                                     enum VorbisPacketType type)
+{
+    enum VorbisPacketType last = priv->last_packet;
+    const char *error = NULL;
+
+    switch (type) {
+    case VORBIS_PACKET_DATA:
+        if (last == VORBIS_PACKET_ID || last == VORBIS_PACKET_COMMENT)
+            error = "data packet before the setup header";
+        break;
+    case VORBIS_PACKET_ID:
+        if (last == VORBIS_PACKET_ID || last == VORBIS_PACKET_COMMENT)
+            error = "identification header before the setup header";
+        break;
+    case VORBIS_PACKET_COMMENT:
+        if (last != VORBIS_PACKET_ID)
+            error = "comment header without identification header";
+        break;
+    case VORBIS_PACKET_SETUP:
+        if (last != VORBIS_PACKET_COMMENT)
+            error = "setup header without comment header";
+        break;
+    }
+
+    if (error)
+        av_log(s, AV_LOG_WARNING,
+               "Invalid chained stream headers: %s, discarding them\n", error);
+
+    if (error || type == VORBIS_PACKET_ID)
+        for (int i = 0; i < 3; i++)
+            av_freep(&priv->packet[i]);
+
+    priv->last_packet = error && type != VORBIS_PACKET_ID ? VORBIS_PACKET_DATA 
: type;
+    return error ? AVERROR_INVALIDDATA : 0;
+}
+
 static int vorbis_packet(AVFormatContext *s, int idx)
 {
     struct ogg *ogg = s->priv_data;
@@ -553,6 +608,8 @@ static int vorbis_packet(AVFormatContext *s, int idx)
      * skipped; the headers are buffered and attached as extradata to the
      * first data packet that follows. */
     if (flags & VORBIS_FLAG_HEADER) {
+        vorbis_check_packet_order(s, priv, VORBIS_PACKET_ID);
+
         ret = vorbis_parse_header(s, s->streams[idx], os->buf + os->pstart, 
os->psize);
         if (ret < 0)
             return ret;
@@ -572,6 +629,9 @@ static int vorbis_packet(AVFormatContext *s, int idx)
         if (ret < 0)
             return ret;
 
+        if (vorbis_check_packet_order(s, priv, VORBIS_PACKET_COMMENT) < 0)
+            return 1;
+
         ret = av_reallocp(&priv->packet[1], os->psize);
         if (ret < 0)
             return ret;
@@ -584,6 +644,9 @@ static int vorbis_packet(AVFormatContext *s, int idx)
     }
 
     if (flags & VORBIS_FLAG_SETUP) {
+        if (vorbis_check_packet_order(s, priv, VORBIS_PACKET_SETUP) < 0)
+            return 1;
+
         ret = av_reallocp(&priv->packet[2], os->psize);
         if (ret < 0)
             return ret;
@@ -594,6 +657,8 @@ static int vorbis_packet(AVFormatContext *s, int idx)
         skip_packet = 1;
     }
 
+    if (!skip_packet)
+        vorbis_check_packet_order(s, priv, VORBIS_PACKET_DATA);
 
     /* All three headers for the new chained stream have been collected;
      * pack them into new extradata to be attached to the next data packet. */
-- 
2.54.0

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

Reply via email to