PR #25292 opened by baylesj URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25292 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25292.patch
# Summary of changes This PR contains a tightly focused group of fuzzer fixes in avformat's seek code, specifically to resolve potential integer overflow issues found by fuzzing FFmpeg inside of Chromium. The following four functions have minor changes to avoid integer overflow: 1. `reset_index_position()`: move to using saturating add when calculating metadata head size, so corrupt seekpoints cannot overflow `int64_t`. 2. `In flac_read_header()`, update timestamp and pos to be read as uint64 values (matching the spec) and bounds checked against INT64_MAX to avoid overflow issues. 3. `ff_seek_frame_binary()`: when mutating pos_limit, strictly bind min_distance to $\subset [0, pos\_limit]$ to avoid underflow issues. Used `FFMIN` / `FFMAX` to try to match library conventions. 4. `ff_gen_search()`: the most significant changes in this CL. Several checks to (a) reject negative position bounds or `pos_limit` values beyond `pos_max`, (b) saturate timestamp deltas to `< INT64_MAX` to avoid overflow in `av_rescale(), and (c) modify the logic to compute the bisection midpoint to prevent signed integer overflows. > [!NOTE] > The binary midpoint calculation previously used the traditional midpoint > formula, $mid = (low + high) / 2$, (where `/ 2` is done by `>> 1` in FFmpeg) > and now uses the subtration method, generally considered the most common and > readable workaround that is overflow safe: $mid = low + (high - low) / 2$. As this is a fuzzing fix, no new FATE test samples are included. >From c29f5f7fa90435f0eae21cb9f7245ab05bb6c4f9 Mon Sep 17 00:00:00 2001 From: Jordan Bayles <[email protected]> Date: Wed, 21 Oct 2026 09:15:40 +0000 Subject: [PATCH 1/2] avformat/flacdec: prevent integer overflow when parsing seektable In reset_index_position(), adding metadata_head_size to each seekpoint's stream_offset (stored as int64_t in AVIndexEntry.pos) can overflow signed 64-bit integers when a corrupt seekpoint has a large offset near INT64_MAX. Use av_sat_add64() to saturate the addition instead of triggering signed integer overflow UB, and read seekpoint timestamp and offset as uint64_t, skipping entries exceeding INT64_MAX (including 0xFFFFFFFFFFFFFFFF placeholder points) when parsing FLAC_METADATA_TYPE_SEEKTABLE. Signed-off-by: Jordan Bayles <[email protected]> --- libavformat/flacdec.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libavformat/flacdec.c b/libavformat/flacdec.c index 12d4b7305f7..1c7d52c34f9 100644 --- a/libavformat/flacdec.c +++ b/libavformat/flacdec.c @@ -20,6 +20,7 @@ */ #include "libavutil/channel_layout.h" +#include "libavutil/common.h" #include "libavutil/mem.h" #include "libavcodec/avcodec.h" #include "libavcodec/bytestream.h" @@ -47,7 +48,7 @@ static void reset_index_position(int64_t metadata_head_size, AVStream *st) FFStream *const sti = ffstream(st); /* the real seek index offset should be the size of metadata blocks with the offset in the frame blocks */ for (int i = 0; i < sti->nb_index_entries; i++) - sti->index_entries[i].pos += metadata_head_size; + sti->index_entries[i].pos = av_sat_add64(sti->index_entries[i].pos, metadata_head_size); } static const uint16_t sr_table[16] = { @@ -178,10 +179,12 @@ static int flac_read_header(AVFormatContext *s) flac->found_seektable = 1; if ((s->flags&AVFMT_FLAG_FAST_SEEK)) { for(i=0; i<seek_point_count; i++) { - int64_t timestamp = bytestream_get_be64(&seekpoint); - int64_t pos = bytestream_get_be64(&seekpoint); + uint64_t timestamp = bytestream_get_be64(&seekpoint); + uint64_t pos = bytestream_get_be64(&seekpoint); /* skip number of samples */ bytestream_get_be16(&seekpoint); + if (timestamp > INT64_MAX || pos > INT64_MAX) + continue; av_add_index_entry(st, pos, timestamp, 0, 0, AVINDEX_KEYFRAME); } } -- 2.54.0 >From 6f90708db337dfb0d3cbcbfa23de2cdc853deb66 Mon Sep 17 00:00:00 2001 From: Jordan Bayles <[email protected]> Date: Wed, 21 Oct 2026 09:15:40 +0000 Subject: [PATCH 2/2] avformat/seek: prevent signed integer overflows in ff_gen_search When seeking on a stream with corrupt index entries or extreme timestamps/positions: 1. In ff_seek_frame_binary(), subtracting e->min_distance from pos_max can underflow if pos_max < min_distance. Clamp the subtraction so pos_limit stays non-negative. 2. In ff_gen_search(), validate that pos_min, pos_max, and pos_limit are non-negative and pos_limit <= pos_max. 3. Computing ts_max - ts_min or target_ts - ts_min in signed int64_t overflows when timestamps span across negative and positive values. Compute the deltas in uint64_t and scale down by 1 bit if the range exceeds INT64_MAX before passing to av_rescale(). 4. Computing (pos_min + pos_limit) >> 1 overflows int64_t when both positions are large (sum > INT64_MAX). Use pos_min + ((pos_limit - pos_min) >> 1) instead. Signed-off-by: Jordan Bayles <[email protected]> --- libavformat/seek.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/libavformat/seek.c b/libavformat/seek.c index 31e3e6dd697..4a4459a9dd3 100644 --- a/libavformat/seek.c +++ b/libavformat/seek.c @@ -336,7 +336,7 @@ int ff_seek_frame_binary(AVFormatContext *s, int stream_index, av_assert1(e->timestamp >= target_ts); pos_max = e->pos; ts_max = e->timestamp; - pos_limit = pos_max - e->min_distance; + pos_limit = pos_max - FFMIN(pos_max, FFMAX(e->min_distance, 0)); av_log(s, AV_LOG_TRACE, "using cached pos_max=0x%"PRIx64" pos_limit=0x%"PRIx64 " dts_max=%s\n", pos_max, pos_limit, av_ts2str(ts_max)); } @@ -417,6 +417,9 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, return -1; } + if (pos_min < 0) + return -1; + if (ts_min >= target_ts) { *ts_ret = ts_min; return pos_min; @@ -428,6 +431,9 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, pos_limit = pos_max; } + if (pos_max < 0 || pos_limit < 0 || pos_limit > pos_max) + return -1; + if (ts_max <= target_ts) { *ts_ret = ts_max; return pos_max; @@ -444,13 +450,18 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, if (no_change == 0) { int64_t approximate_keyframe_distance = pos_max - pos_limit; + uint64_t ts_delta = (uint64_t)target_ts - (uint64_t)ts_min; + uint64_t ts_range = (uint64_t)ts_max - (uint64_t)ts_min; + if (ts_range > INT64_MAX) { + ts_delta >>= 1; + ts_range >>= 1; + } // interpolate position (better than dichotomy) - pos = av_rescale(target_ts - ts_min, pos_max - pos_min, - ts_max - ts_min) + + pos = av_rescale(ts_delta, pos_max - pos_min, ts_range) + pos_min - approximate_keyframe_distance; } else if (no_change == 1) { // bisection if interpolation did not change min / max pos last time - pos = (pos_min + pos_limit) >> 1; + pos = pos_min + ((pos_limit - pos_min) >> 1); } else { /* linear search if bisection failed, can only happen if there * are very few or no keyframes between min/max */ -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
