This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit 260d32d06d9b158ccbf31569421d074eafc5043c Author: Jun Zhao <[email protected]> AuthorDate: Sat Oct 24 16:05:24 2026 +0800 Commit: Jun Zhao <[email protected]> CommitDate: Sat Oct 24 15:15:50 2026 +0000 avformat/id3v2: tighten v2.4 non-synchsafe frame size boundary check A valid synchsafe integer has the MSB cleared in every byte, and a candidate next-frame boundary inside an ID3v2.4 tag must be followed by the end of the tag, zero padding, or a plausible 10-byte frame header whose size fits in the remaining tag bytes. Previously, size_to_syncsafe(tlen) was probed even when tlen had bit 7 set in one of its bytes (such as 0x000000b6), and check_tag() accepted any four uppercase ASCII characters (such as "LLLL" inside a chapter title) as a valid next frame ID without checking the following frame size. It could also read past the end of the ID3v2 tag when the last frame had fewer than 4 bytes of padding. Check tlen with is_syncsafe() before probing size_to_syncsafe(tlen), bound the probe to the remaining tag bytes, and verify in check_tag() that a candidate frame header fits in the remaining tag length (including empty frames, which id3v2_parse() skips). Signed-off-by: Jun Zhao <[email protected]> --- libavformat/id3v2.c | 59 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 14 deletions(-) diff --git a/libavformat/id3v2.c b/libavformat/id3v2.c index 22a15ab794..20d37f345b 100644 --- a/libavformat/id3v2.c +++ b/libavformat/id3v2.c @@ -182,6 +182,14 @@ static unsigned int get_size(AVIOContext *s, int len) return v; } +static int is_syncsafe(unsigned int size) +{ + return !(size & ((0x80U << 0) | + (0x80U << 8) | + (0x80U << 16) | + (0x80U << 24))); +} + static unsigned int size_to_syncsafe(unsigned int size) { return (((size) & (0x7f << 0)) >> 0) + @@ -208,20 +216,38 @@ static int is_tag(const char *buf, unsigned int len) } /** - * Return 1 if the tag of length len at the given offset is valid, 0 if not, -1 on error + * Return 1 if the candidate frame boundary at offset (with rem bytes left in + * the ID3v2 tag after it) is followed by the end of the tag, zero padding, or + * a plausible 10-byte ID3v2.3/2.4 frame header; 0 if not, -1 on error. */ -static int check_tag(AVIOContext *s, int offset, unsigned int len) +static int check_tag(AVIOContext *s, int64_t offset, unsigned int rem) { char tag[4]; + unsigned int nsize, nlen; - if (len > 4 || - avio_seek(s, offset, SEEK_SET) < 0 || - avio_read(s, tag, len) < (int)len) + if (!rem) + return 1; + if (avio_seek(s, offset, SEEK_SET) < 0) return -1; - else if (!AV_RB32(tag) || is_tag(tag, len)) + if (rem < sizeof(tag)) { + while (rem--) + if (avio_r8(s)) + return 0; return 1; - - return 0; + } + if (avio_read(s, tag, sizeof(tag)) < (int)sizeof(tag)) + return -1; + if (!AV_RB32(tag)) + return 1; + if (rem < ID3v2_HEADER_SIZE || !is_tag(tag, sizeof(tag))) + return 0; + nsize = avio_rb32(s); + nlen = is_syncsafe(nsize) ? size_to_syncsafe(nsize) : nsize; + /* ID3v2 requires frames to be at least 1 byte, but id3v2_parse() + * explicitly skips empty frames (tlen == 0) to recover from broken tags. + * Accept nlen == 0 here as well so a valid synchsafe boundary followed by + * an empty frame is not rejected. */ + return nlen <= rem - ID3v2_HEADER_SIZE; } /** @@ -944,18 +970,23 @@ static void id3v2_parse(AVIOContext *pb, AVDictionary **metadata, * so check the next tag to see which one to use */ tlen = avio_rb32(pb); if (tlen > 0x7f) { - if (tlen < len) { + unsigned int rem = len - taghdrlen; + + if (tlen <= rem) { int64_t cur = avio_tell(pb); + unsigned int ss_tlen = size_to_syncsafe(tlen); - if (ffio_ensure_seekback(pb, 2 /* tflags */ + tlen + 4 /* next tag */)) + if (ffio_ensure_seekback(pb, 2 /* tflags */ + + FFMIN(rem, tlen + ID3v2_HEADER_SIZE))) break; - if (check_tag(pb, cur + 2 + size_to_syncsafe(tlen), 4) == 1) - tlen = size_to_syncsafe(tlen); - else if (check_tag(pb, cur + 2 + tlen, 4) != 1) + if (is_syncsafe(tlen) && + check_tag(pb, cur + 2 + ss_tlen, rem - ss_tlen) == 1) + tlen = ss_tlen; + else if (check_tag(pb, cur + 2 + tlen, rem - tlen) != 1) break; avio_seek(pb, cur, SEEK_SET); - } else + } else if (is_syncsafe(tlen)) tlen = size_to_syncsafe(tlen); } } -- To stop receiving notification emails like this one, please contact [email protected]. _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
