PR #23163 opened by dalecurtis
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23163
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23163.patch

- Prevent integer overflow when summing header lengths; add bounds check.
- Call codec cleanup in ogg_replace_stream on stream switch to prevent state
  leakage between chained streams, and reset state in vorbis_cleanup.
- Support NULL parser context in vorbis_packet to allow bootstrapping new
  chained streams, and re-initialize it once new headers are accumulated.
- Refactor common first-byte header parsing logic into a new static inline
  helper ff_vorbis_parse_packet_flags in vorbis_parser_internal.h.

Signed-off-by: Dale Curtis <[email protected]>


>From 3e113523a74bb3bc0b79c5f203eb65c3449997aa Mon Sep 17 00:00:00 2001
From: Dale Curtis <[email protected]>
Date: Tue, 19 May 2026 22:01:18 +0000
Subject: [PATCH] avformat/ogg: Fix overflow and stale oggvorbis_private values

- Prevent integer overflow when summing header lengths; add bounds check.
- Call codec cleanup in ogg_replace_stream on stream switch to prevent state
  leakage between chained streams, and reset state in vorbis_cleanup.
- Support NULL parser context in vorbis_packet to allow bootstrapping new
  chained streams, and re-initialize it once new headers are accumulated.
- Refactor common first-byte header parsing logic into a new static inline
  helper ff_vorbis_parse_packet_flags in vorbis_parser_internal.h.

Signed-off-by: Dale Curtis <[email protected]>
---
 libavcodec/vorbis_parser.c          | 10 +++----
 libavcodec/vorbis_parser_internal.h | 12 +++++++++
 libavformat/oggdec.c                |  3 +++
 libavformat/oggparsevorbis.c        | 42 ++++++++++++++++++++++-------
 4 files changed, 51 insertions(+), 16 deletions(-)

diff --git a/libavcodec/vorbis_parser.c b/libavcodec/vorbis_parser.c
index 88b81fcb53..3a24d42b67 100644
--- a/libavcodec/vorbis_parser.c
+++ b/libavcodec/vorbis_parser.c
@@ -228,15 +228,11 @@ int av_vorbis_parse_frame_flags(AVVorbisParseContext *s, 
const uint8_t *buf,
                 goto bad_packet;
 
             /* Set the flag for which kind of special packet it is. */
-            if (buf[0] == 1)
-                *flags |= VORBIS_FLAG_HEADER;
-            else if (buf[0] == 3)
-                *flags |= VORBIS_FLAG_COMMENT;
-            else if (buf[0] == 5)
-                *flags |= VORBIS_FLAG_SETUP;
-            else
+            *flags |= ff_vorbis_parse_packet_flags(buf[0]);
+            if (!(buf[0] == 1 || buf[0] == 3 || buf[0] == 5)) {
                 av_log(s, AV_LOG_VERBOSE, "Ignoring packet with unknown type 
%u\n",
                        buf[0]);
+            }
 
             /* Special packets have no duration. */
             return 0;
diff --git a/libavcodec/vorbis_parser_internal.h 
b/libavcodec/vorbis_parser_internal.h
index 691a842385..a673b7533f 100644
--- a/libavcodec/vorbis_parser_internal.h
+++ b/libavcodec/vorbis_parser_internal.h
@@ -43,4 +43,16 @@ struct AVVorbisParseContext {
     int prev_mask;              ///< bitmask used to get the previous mode 
flag in each packet
 };
 
+static inline int ff_vorbis_parse_packet_flags(uint8_t first_byte)
+{
+    int flags = 0;
+    if (first_byte == 1)
+        flags |= VORBIS_FLAG_HEADER;
+    else if (first_byte == 3)
+        flags |= VORBIS_FLAG_COMMENT;
+    else if (first_byte == 5)
+        flags |= VORBIS_FLAG_SETUP;
+    return flags;
+}
+
 #endif /* AVCODEC_VORBIS_PARSER_INTERNAL_H */
diff --git a/libavformat/oggdec.c b/libavformat/oggdec.c
index a7b23c8d65..2db7fb0c54 100644
--- a/libavformat/oggdec.c
+++ b/libavformat/oggdec.c
@@ -237,6 +237,9 @@ static int ogg_replace_stream(AVFormatContext *s, uint32_t 
serial, char *magic,
     if (os->codec != codec)
         return AVERROR(EINVAL);
 
+    if (!ogg->state && os->codec && os->codec->cleanup)
+        os->codec->cleanup(s, 0);
+
     os->serial  = serial;
     os->codec   = codec;
     os->serial  = serial;
diff --git a/libavformat/oggparsevorbis.c b/libavformat/oggparsevorbis.c
index ed81a431f6..7ec7239b3d 100644
--- a/libavformat/oggparsevorbis.c
+++ b/libavformat/oggparsevorbis.c
@@ -31,6 +31,7 @@
 
 #include "libavcodec/bytestream.h"
 #include "libavcodec/vorbis_parser.h"
+#include "libavcodec/vorbis_parser_internal.h"
 
 #include "avformat.h"
 #include "demux.h"
@@ -230,8 +231,13 @@ static int fixup_vorbis_headers(AVFormatContext *as,
     int i, offset, len, err;
     int buf_len;
     unsigned char *ptr;
+    uint64_t total_len;
 
-    len = priv->len[0] + priv->len[1] + priv->len[2];
+    total_len = (uint64_t)priv->len[0] + priv->len[1] + priv->len[2];
+    if (total_len + total_len / 255 + 64 > INT_MAX)
+        return AVERROR_INVALIDDATA;
+
+    len = total_len;
     buf_len = len + len / 255 + 64;
 
     if (*buf)
@@ -270,6 +276,11 @@ static void vorbis_cleanup(AVFormatContext *s, int idx)
         av_freep(&priv->header);
         av_freep(&priv->comment);
         av_freep(&priv->setup);
+
+        memset(priv->len, 0, sizeof(priv->len));
+        priv->header_size = 0;
+        priv->comment_size = 0;
+        priv->setup_size = 0;
     }
 }
 
@@ -457,14 +468,11 @@ static int vorbis_packet(AVFormatContext *s, int idx)
     int ret, new_extradata_size;
     PutByteContext pb;
 
-    if (!priv->vp)
-        return AVERROR_INVALIDDATA;
-
     /* first packet handling
      * here we parse the duration of each packet in the first page and compare
      * the total duration to the page granule to find the encoder delay and
      * set the first timestamp */
-    if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && !(os->flags & 
OGG_FLAG_EOS) && (int64_t)os->granule>=0) {
+    if (priv->vp && (!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && 
!(os->flags & OGG_FLAG_EOS) && (int64_t)os->granule>=0) {
         int seg, d;
         uint8_t *last_pkt  = os->buf + os->pstart;
         uint8_t *next_pkt  = last_pkt;
@@ -514,10 +522,20 @@ static int vorbis_packet(AVFormatContext *s, int idx)
 
     /* parse packet duration */
     if (os->psize > 0) {
-        duration = av_vorbis_parse_frame_flags(priv->vp, os->buf + os->pstart, 
1, &flags);
-        if (duration < 0) {
-            os->pflags |= AV_PKT_FLAG_CORRUPT;
-            return 0;
+        uint8_t first_byte = os->buf[os->pstart];
+        if (!priv->vp) {
+            if (first_byte & 1) {
+                flags |= ff_vorbis_parse_packet_flags(first_byte);
+                duration = 0;
+            } else {
+                return AVERROR_INVALIDDATA;
+            }
+        } else {
+            duration = av_vorbis_parse_frame_flags(priv->vp, os->buf + 
os->pstart, 1, &flags);
+            if (duration < 0) {
+                os->pflags |= AV_PKT_FLAG_CORRUPT;
+                return 0;
+            }
         }
 
         if (flags & VORBIS_FLAG_HEADER) {
@@ -605,6 +623,12 @@ static int vorbis_packet(AVFormatContext *s, int idx)
         priv->comment_size = 0;
         av_freep(&priv->setup);
         priv->setup_size = 0;
+
+        av_vorbis_parse_free(&priv->vp);
+        priv->vp = av_vorbis_parse_init(os->new_extradata, 
os->new_extradata_size);
+        if (!priv->vp) {
+            av_log(s, AV_LOG_ERROR, "Failed to re-initialize Vorbis parser\n");
+        }
     }
 
     return skip_packet;
-- 
2.52.0

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

Reply via email to