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]
