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


>From 50ee70a83fca215c4e4319b99e0448efb4deb146 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 04:23:00 +0200
Subject: [PATCH 01/41] avcodec/dcadec: skip reserved speaker positions when
 exporting the downmix matrix

Out of array read Replicated through UnModified FFmpeg with ASAN

Fixes: out of array read
Fixes: 
562992376/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_DCA_DEC_fuzzer-6657589774188544
Fixes: XsCYzcvQNPUj
Regression since: 57708daf619c1de0ea25366ef11e527f292acd55

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/dcadec.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavcodec/dcadec.c b/libavcodec/dcadec.c
index 927c74742c..17d8347a18 100644
--- a/libavcodec/dcadec.c
+++ b/libavcodec/dcadec.c
@@ -184,7 +184,7 @@ int ff_dca_export_downmix_matrix(AVCodecContext *avctx, 
AVFrame *frame,
     matrix_l = av_downmix_matrix_coeff(dm, 0, 0);
     matrix_r = av_downmix_matrix_coeff(dm, 1, 0);
 
-    for (int i = 0; i <= av_log2(output_mask); i++) {
+    for (int i = 0; i < DCA_SPEAKER_RSV1; i++) {
         if (!(output_mask & (1U << i)))
             continue;
 
-- 
2.52.0


>From 14ed27204c42d2935db1b750bd4952ac9b59866b Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 04:50:50 +0200
Subject: [PATCH 02/41] avformat/wavdec: saturate the next tag offset after a
 saturated RF64 data end

Signed integer overflow Replicated through API with UBSAN

Fixes: signed integer overflow: 9223372036854775807 + 1 cannot be represented 
in type 'int64_t'
Fixes: 
538780590/clusterfuzz-testcase-minimized-ffmpeg_dem_WAV_fuzzer-5528579277651968
Fixes: LkQQ7nojUzdg
Regression since: 5bbc00c05d6e78542d09bb56c55759ce6b53c139

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavformat/wavdec.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavformat/wavdec.c b/libavformat/wavdec.c
index 8d19e52ead..d1f061d65b 100644
--- a/libavformat/wavdec.c
+++ b/libavformat/wavdec.c
@@ -468,7 +468,7 @@ static int wav_read_header(AVFormatContext *s)
 
             if (rf64 || bw64) {
                 wav->data_end = av_sat_add64(avio_tell(pb), data_size);
-                next_tag_ofs = wav->data_end + (data_size & 1);
+                next_tag_ofs = av_sat_add64(wav->data_end, data_size & 1);
             } else if (size > 0 && size != 0xFFFFFFFF) {
                 data_size    = size;
                 wav->data_end = avio_tell(pb) + size;
-- 
2.52.0


>From 7835d61d2f81809e08f823dd9819fe2a7e5eb461 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 04:50:50 +0200
Subject: [PATCH 03/41] avformat/mov: treat a negative rescaled edit list
 duration as overflow

Signed integer overflow Replicated through API with UBSAN

Fixes: signed integer overflow: -9223372036854775808 + -72057594037927936 
cannot be represented in type 'int64_t'
Fixes: 
538868974/clusterfuzz-testcase-minimized-ffmpeg_dem_MOV_fuzzer-5819272002338816
Fixes: erHRV4uA0F71

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavformat/mov.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libavformat/mov.c b/libavformat/mov.c
index 71c4e38c88..5d4f2973e1 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -4196,7 +4196,8 @@ static int get_edit_list_entry(MOVContext *mov,
     *edit_list_duration = av_rescale(*edit_list_duration, msc->time_scale,
                                      global_timescale);
 
-    if (*edit_list_duration + (uint64_t)*edit_list_media_time > INT64_MAX)
+    if (*edit_list_duration < 0 ||
+        *edit_list_duration + (uint64_t)*edit_list_media_time > INT64_MAX)
         *edit_list_duration = 0;
 
     return 1;
-- 
2.52.0


>From 759d1198adb1449db84241264f86b4728bae2b68 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 04:50:50 +0200
Subject: [PATCH 04/41] avcodec/tiff: compute the DNG tile count without
 overflowing

Signed integer overflow Replicated through API with UBSAN

Fixes: signed integer overflow: 8224 + 2147483647 cannot be represented in type 
'int'
Fixes: 
554665253/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_TIFF_DEC_fuzzer-5826816041091072
Fixes: CiA9e8B5xODW

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/tiff.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libavcodec/tiff.c b/libavcodec/tiff.c
index d6db7ef519..41db29d6e2 100644
--- a/libavcodec/tiff.c
+++ b/libavcodec/tiff.c
@@ -1004,8 +1004,8 @@ static int dng_decode_tiles(AVCodecContext *avctx, 
AVFrame *frame,
     has_height_leftover = (s->height % s->tile_length != 0);
 
     /* Calculate tile counts (round up) */
-    tile_count_x = (s->width + s->tile_width - 1) / s->tile_width;
-    tile_count_y = (s->height + s->tile_length - 1) / s->tile_length;
+    tile_count_x = (s->width - 1) / s->tile_width + 1;
+    tile_count_y = (s->height - 1) / s->tile_length + 1;
 
     /* Iterate over the number of tiles */
     for (tile_idx = 0; tile_idx < tile_count_x * tile_count_y; tile_idx++) {
-- 
2.52.0


>From d1ad804ca812d06179ad3dbdba08f9704bd5ec8a Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 04:50:51 +0200
Subject: [PATCH 05/41] avcodec/jpeg2000htdec: apply the ROI shift as an
 unsigned shift

Undefined shift Replicated through UnModified FFmpeg with UBSAN

Fixes: left shift of 448 by 23 places cannot be represented in type 'int32_t'
Fixes: 
538245301/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_JPEG2000_DEC_fuzzer-5803355348467712
Fixes: QvL1tuhWgRGM

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/jpeg2000htdec.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavcodec/jpeg2000htdec.c b/libavcodec/jpeg2000htdec.c
index 5b12062f40..9464eba75d 100644
--- a/libavcodec/jpeg2000htdec.c
+++ b/libavcodec/jpeg2000htdec.c
@@ -1337,7 +1337,7 @@ ff_jpeg2000_decode_htj2k(const Jpeg2000DecoderContext *s, 
Jpeg2000CodingStyle *c
             if (roi_shift && (((uint32_t)val & ~mask) == 0)) {
                 if ((32 - ff_clz(val | 1)) < roi_shift)
                     // Assuming that the internal precision for codeblock 
decoding is 32 bits.
-                    val <<= roi_shift;
+                    val = (uint32_t)val << roi_shift;
                 else {
                     av_log(s->avctx, AV_LOG_ERROR,
                         "Pixel precision in ROI is beyond the supported 
range.\n"
-- 
2.52.0


>From f58000aa5fa2a7bd2c27fe1cd002d247d1331c52 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 04:52:05 +0200
Subject: [PATCH 06/41] avcodec/av1dec: reject a tile group whose tg_end is
 beyond NumTiles

Division by zero Replicated through API with UBSAN

Fixes: division by zero
Fixes: 
539034287/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_AV1_fuzzer-6190309328093184
Fixes: oovCgd09vKl2

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/av1dec.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libavcodec/av1dec.c b/libavcodec/av1dec.c
index d7ccebfbd5..6e15590193 100644
--- a/libavcodec/av1dec.c
+++ b/libavcodec/av1dec.c
@@ -437,6 +437,9 @@ static int get_tiles_info(AVCodecContext *avctx, const 
AV1RawTileGroup *tile_gro
     s->tg_start = tile_group->tg_start;
     s->tg_end = tile_group->tg_end;
 
+    if (tile_group->tg_end >= s->raw_frame_header->tile_cols * 
s->raw_frame_header->tile_rows)
+        return AVERROR_INVALIDDATA;
+
     for (tile_num = tile_group->tg_start; tile_num <= tile_group->tg_end; 
tile_num++) {
         tile_row = tile_num / s->raw_frame_header->tile_cols;
         tile_col = tile_num % s->raw_frame_header->tile_cols;
-- 
2.52.0


>From 9046b7adf06a8844a39ebde8a2cec6f4a11dd834 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 04:50:51 +0200
Subject: [PATCH 07/41] avcodec/aac/aacdec_usac_mps212: avoid shifting a
 negative IPD index

Undefined shift Replicated through API with UBSAN

Fixes: left shift of negative value -1
Fixes: 
536321788/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_AAC_DEC_fuzzer-4601480396210176
Fixes: IsFvv6HEhVCP

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/aac/aacdec_usac_mps212.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavcodec/aac/aacdec_usac_mps212.c 
b/libavcodec/aac/aacdec_usac_mps212.c
index eec7943bb8..a003d0544c 100644
--- a/libavcodec/aac/aacdec_usac_mps212.c
+++ b/libavcodec/aac/aacdec_usac_mps212.c
@@ -375,7 +375,7 @@ static void attach_lsb(GetBitContext *gb, int16_t *data_msb,
         int msb = data_msb[i];
         if (nb_lsb > 0) {
             uint32_t lsb = get_bits(gb, nb_lsb);
-            data[i] = ((msb << nb_lsb) | lsb) - offset;
+            data[i] = ((msb * (1 << nb_lsb)) | lsb) - offset;
         } else {
             data[i] = msb - offset;
         }
-- 
2.52.0


>From 864a44223729353d70ba202a75ff6df959078a83 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 04:50:51 +0200
Subject: [PATCH 08/41] tools/target_bsf_fuzzer: skip filters that
 av_bsf_alloc() does not support

This is not a security issue.

Fixes: 
538624321/clusterfuzz-testcase-minimized-ffmpeg_BSF_LCEVC_MERGE_fuzzer-6371982149746688
Fixes: lrIny8K4GGmb

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 tools/target_bsf_fuzzer.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tools/target_bsf_fuzzer.c b/tools/target_bsf_fuzzer.c
index 44a4d1467d..b60d428080 100644
--- a/tools/target_bsf_fuzzer.c
+++ b/tools/target_bsf_fuzzer.c
@@ -60,6 +60,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
     }
 
     res = f ? av_bsf_alloc(f, &bsf) : av_bsf_get_null_filter(&bsf);
+    if (res == AVERROR(ENOTSUP))
+        return 0; // multi-input filters cannot be used through av_bsf_alloc()
     if (res < 0)
         error("Failed memory allocation");
     f = bsf->filter;
-- 
2.52.0


>From e75ff228f7967f027ac69d629b40c4e06c0222df Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 04:53:31 +0200
Subject: [PATCH 09/41] avcodec/cbs_apv: reject a frame width or height of 0

Division by zero Replicated through API with UBSAN

Fixes: division by zero
Fixes: 
538197062/clusterfuzz-testcase-minimized-ffmpeg_BSF_APV_METADATA_fuzzer-6204722298814464
Fixes: p9U20UaYiMFw

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/cbs_apv_syntax_template.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libavcodec/cbs_apv_syntax_template.c 
b/libavcodec/cbs_apv_syntax_template.c
index 1a97878bb9..f8c902d95b 100644
--- a/libavcodec/cbs_apv_syntax_template.c
+++ b/libavcodec/cbs_apv_syntax_template.c
@@ -71,8 +71,8 @@ static int FUNC(frame_info)(CodedBitstreamContext *ctx, 
RWContext *rw,
 
     u(5, reserved_zero_5bits, 0, 0);
 
-    ub(24, frame_width);
-    ub(24, frame_height);
+    u(24, frame_width,  1, MAX_UINT_BITS(24));
+    u(24, frame_height, 1, MAX_UINT_BITS(24));
 
     u(4, chroma_format_idc, 0, 4);
     if (current->chroma_format_idc == 1) {
-- 
2.52.0


>From 20d1366586c5a782321f33b39d5b0dd2a3b87976 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 04:55:21 +0200
Subject: [PATCH 10/41] avcodec/imm5: switch to the receive_frame API

Pull packets with ff_decode_get_packet() and receive from the sub-decoder
until it has no frame left before feeding it the next packet. This also
drains the sub-decoder at EOF instead of dropping its delayed frames.

Assertion failure Replicated through API

Fixes: assertion failure
Fixes: 
538301564/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_IMM5_fuzzer-5256353978580992
Fixes: PFQtJZPcOsLc

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/imm5.c | 43 +++++++++++++++++++++++++++++++------------
 1 file changed, 31 insertions(+), 12 deletions(-)

diff --git a/libavcodec/imm5.c b/libavcodec/imm5.c
index d130db76ae..129614b31f 100644
--- a/libavcodec/imm5.c
+++ b/libavcodec/imm5.c
@@ -24,10 +24,13 @@
 
 #include "avcodec.h"
 #include "codec_internal.h"
+#include "decode.h"
+#include "internal.h"
 
 typedef struct IMM5Context {
     AVCodecContext *h264_avctx;   // wrapper context for H264
     AVCodecContext *hevc_avctx;   // wrapper context for HEVC
+    AVCodecContext *codec_avctx;
 } IMM5Context;
 
 static const struct IMM5_unit {
@@ -66,6 +69,7 @@ static av_cold int imm5_init(AVCodecContext *avctx)
     ret = avcodec_open2(ctx->h264_avctx, NULL, NULL);
     if (ret < 0)
         return ret;
+    ctx->codec_avctx = ctx->h264_avctx;
 
     EXTERN const FFCodec ff_hevc_decoder;
     ctx->hevc_avctx = avcodec_alloc_context3(&ff_hevc_decoder.p);
@@ -82,8 +86,7 @@ static av_cold int imm5_init(AVCodecContext *avctx)
     return 0;
 }
 
-static int imm5_decode_frame(AVCodecContext *avctx, AVFrame *frame,
-                             int *got_frame, AVPacket *avpkt)
+static int imm5_send_packet(AVCodecContext *avctx, AVPacket *avpkt)
 {
     IMM5Context *ctx = avctx->priv_data;
     AVCodecContext *codec_avctx = ctx->h264_avctx;
@@ -131,18 +134,36 @@ static int imm5_decode_frame(AVCodecContext *avctx, 
AVFrame *frame,
         }
     }
 
+    ctx->codec_avctx = codec_avctx;
     ret = avcodec_send_packet(codec_avctx, avpkt);
-    if (ret < 0) {
+    if (ret < 0)
         av_log(avctx, AV_LOG_ERROR, "Error submitting a packet for 
decoding\n");
-        return ret;
-    }
+    return ret;
+}
 
-    ret = avcodec_receive_frame(codec_avctx, frame);
-    if (ret == AVERROR(EAGAIN))
-        return avpkt->size;
+static int imm5_receive_frame(AVCodecContext *avctx, AVFrame *frame)
+{
+    IMM5Context *ctx = avctx->priv_data;
+    AVPacket *avpkt = avctx->internal->in_pkt;
+    int ret;
+
+    while ((ret = avcodec_receive_frame(ctx->codec_avctx, frame)) == 
AVERROR(EAGAIN)) {
+        ret = ff_decode_get_packet(avctx, avpkt);
+        if (ret == AVERROR_EOF) {
+            avcodec_send_packet(ctx->codec_avctx, NULL);
+            continue;
+        }
+        if (ret < 0)
+            return ret;
+        ret = imm5_send_packet(avctx, avpkt);
+        av_packet_unref(avpkt);
+        if (ret < 0)
+            return ret;
+    }
     if (ret < 0)
         return ret;
 
+    AVCodecContext *codec_avctx = ctx->codec_avctx;
     avctx->pix_fmt      = codec_avctx->pix_fmt;
     avctx->coded_width  = codec_avctx->coded_width;
     avctx->coded_height = codec_avctx->coded_height;
@@ -155,9 +176,7 @@ static int imm5_decode_frame(AVCodecContext *avctx, AVFrame 
*frame,
     avctx->color_primaries = codec_avctx->color_primaries;
     avctx->chroma_sample_location = codec_avctx->chroma_sample_location;
 
-    *got_frame = 1;
-
-    return avpkt->size;
+    return 0;
 }
 
 static av_cold void imm5_flush(AVCodecContext *avctx)
@@ -184,7 +203,7 @@ const FFCodec ff_imm5_decoder = {
     .p.type         = AVMEDIA_TYPE_VIDEO,
     .p.id           = AV_CODEC_ID_IMM5,
     .init           = imm5_init,
-    FF_CODEC_DECODE_CB(imm5_decode_frame),
+    FF_CODEC_RECEIVE_FRAME_CB(imm5_receive_frame),
     .close          = imm5_close,
     .flush          = imm5_flush,
     .priv_data_size = sizeof(IMM5Context),
-- 
2.52.0


>From 38ae959dee306a398a8e44803700c553f98b4b33 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 04:58:54 +0200
Subject: [PATCH 11/41] avcodec/cbs_h266: store
 pps_exp_slice_height_in_ctus_minus1 per slice

CBS allocates a content structure per NAL unit, so a packet with a few
thousand PPS NAL units needed gigabytes.

Out of memory Replicated through API

Fixes: out of memory
Fixes: 
529105369/clusterfuzz-testcase-minimized-ffmpeg_BSF_VVC_METADATA_fuzzer-5351121246552064
Fixes: zF6ZXkVvhMcx

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/cbs_h266.h                 |  2 +-
 libavcodec/cbs_h266_syntax_template.c | 10 +++++-----
 libavcodec/vaapi_vvc.c                |  2 +-
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/libavcodec/cbs_h266.h b/libavcodec/cbs_h266.h
index d315f6b64a..2751ef5dd0 100644
--- a/libavcodec/cbs_h266.h
+++ b/libavcodec/cbs_h266.h
@@ -537,7 +537,7 @@ typedef struct H266RawPPS {
     uint16_t pps_slice_width_in_tiles_minus1[VVC_MAX_SLICES];
     uint16_t pps_slice_height_in_tiles_minus1[VVC_MAX_SLICES];
     uint16_t pps_num_exp_slices_in_tile[VVC_MAX_SLICES];
-    uint16_t 
pps_exp_slice_height_in_ctus_minus1[VVC_MAX_SLICES][VVC_MAX_TILE_ROWS];
+    uint16_t pps_exp_slice_height_in_ctus_minus1[VVC_MAX_SLICES];
     int16_t  pps_tile_idx_delta_val[VVC_MAX_SLICES];
 
     uint8_t  pps_loop_filter_across_slices_enabled_flag;
diff --git a/libavcodec/cbs_h266_syntax_template.c 
b/libavcodec/cbs_h266_syntax_template.c
index 55178db1ea..c7152d4e2c 100644
--- a/libavcodec/cbs_h266_syntax_template.c
+++ b/libavcodec/cbs_h266_syntax_template.c
@@ -2021,12 +2021,12 @@ static int FUNC(pps) (CodedBitstreamContext *ctx, 
RWContext *rw,
 
                         for (j = 0; j < current->pps_num_exp_slices_in_tile[i];
                              j++) {
-                            ues(pps_exp_slice_height_in_ctus_minus1[i][j], 0,
-                                remaining_height_in_ctbs_y - 
(current->pps_num_exp_slices_in_tile[i] - j), 2,
-                                i, j);
+                            ues(pps_exp_slice_height_in_ctus_minus1[i + j], 0,
+                                remaining_height_in_ctbs_y - 
(current->pps_num_exp_slices_in_tile[i] - j), 1,
+                                i + j);
                             slice_height_in_ctus =
                                 current->
-                                pps_exp_slice_height_in_ctus_minus1[i][j] + 1;
+                                pps_exp_slice_height_in_ctus_minus1[i + j] + 1;
 
                             current->slice_height_in_ctus[i + j] =
                                 slice_height_in_ctus;
@@ -2038,7 +2038,7 @@ static int FUNC(pps) (CodedBitstreamContext *ctx, 
RWContext *rw,
                         }
                         uniform_slice_height = 1 +
                             (j == 0 ? current->row_height_val[tile_y] - 1:
-                            
current->pps_exp_slice_height_in_ctus_minus1[i][j-1]);
+                            current->pps_exp_slice_height_in_ctus_minus1[i + j 
- 1]);
 
                         num_uniform_slices = (remaining_height_in_ctbs_y + 
uniform_slice_height - 1)
                                            / uniform_slice_height;
diff --git a/libavcodec/vaapi_vvc.c b/libavcodec/vaapi_vvc.c
index 908db7bfab..f5ee07ee87 100644
--- a/libavcodec/vaapi_vvc.c
+++ b/libavcodec/vaapi_vvc.c
@@ -425,7 +425,7 @@ static int vaapi_vvc_start_frame(AVCodecContext          
*avctx,
     if (!pps->pps_no_pic_partition_flag && pps->pps_rect_slice_flag && 
!pps->pps_single_slice_per_subpic_flag) {
         for (i = 0; i <= pps->pps_num_slices_in_pic_minus1; i++) {
             for (j = 0; j < pps->pps_num_exp_slices_in_tile[i]; j++) {
-                exp_slice_height_in_ctus[i + j] = 
pps->pps_exp_slice_height_in_ctus_minus1[i][j] + 1;
+                exp_slice_height_in_ctus[i + j] = 
pps->pps_exp_slice_height_in_ctus_minus1[i + j] + 1;
             }
         }
         for (i = 0; i <= pps->pps_num_slices_in_pic_minus1; i++) {
-- 
2.52.0


>From 266ea39ef65a091b46d4be9a82007769115132db Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 04:58:54 +0200
Subject: [PATCH 12/41] avformat/sbgdec: reject tone-sets with more than 16
 channels

Out of memory Replicated through API

Fixes: out of memory
Fixes: 
474457174/clusterfuzz-testcase-minimized-ffmpeg_dem_SBG_fuzzer-6678348798689280
Fixes: CjKWQoTOOUJW

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavformat/sbgdec.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libavformat/sbgdec.c b/libavformat/sbgdec.c
index 71cbad2dae..0792520723 100644
--- a/libavformat/sbgdec.c
+++ b/libavformat/sbgdec.c
@@ -37,6 +37,7 @@
 #define SBG_SCALE (1 << 16)
 #define DAY (24 * 60 * 60)
 #define DAY_TS ((int64_t)DAY * AV_TIME_BASE)
+#define SBG_MAX_CHANNELS 16
 
 struct sbg_demuxer {
     AVClass *class;
@@ -756,6 +757,11 @@ static int parse_synth_def(struct sbg_parser *p,
     lex_space(p);
     if (synth == p->scs.nb_synth)
         return AVERROR_INVALIDDATA;
+    if (p->scs.nb_synth - synth > SBG_MAX_CHANNELS) {
+        snprintf(p->err_msg, sizeof(p->err_msg),
+                 "too many channels in tone-set");
+        return AVERROR_INVALIDDATA;
+    }
     if (!lex_line_end(p))
         return AVERROR_INVALIDDATA;
     def->type        = 'S';
-- 
2.52.0


>From 119e9339d3515a477c00d3277c224e9b2c67c946 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 04:58:54 +0200
Subject: [PATCH 13/41] avcodec/eatgq: reject packets too small for the frame
 before allocating it

Timeout Replicated through API

Fixes: Timeout
Fixes: 
557846267/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_EATGQ_fuzzer-6100142894415872
Fixes: a3IxtdH9wk9F

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/eatgq.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/libavcodec/eatgq.c b/libavcodec/eatgq.c
index 8d6ef122ce..e50f6056d9 100644
--- a/libavcodec/eatgq.c
+++ b/libavcodec/eatgq.c
@@ -272,12 +272,17 @@ static int tgq_decode_frame(AVCodecContext *avctx, 
AVFrame *frame,
     tgq_calculate_qtable(s, bytestream2_get_byteu(&gbyte));
     bytestream2_skipu(&gbyte, 3);
 
+    int mb_width  = FFALIGN(avctx->width,  16) >> 4;
+    int mb_height = FFALIGN(avctx->height, 16) >> 4;
+    if (bytestream2_get_bytes_left(&gbyte) < mb_width * mb_height)
+        return AVERROR_INVALIDDATA;
+
     if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
         return ret;
 
     frame->flags |= AV_FRAME_FLAG_KEY;
-    for (y = 0; y < FFALIGN(avctx->height, 16) >> 4; y++)
-        for (x = 0; x < FFALIGN(avctx->width, 16) >> 4; x++)
+    for (y = 0; y < mb_height; y++)
+        for (x = 0; x < mb_width; x++)
             if (tgq_decode_mb(s, &gbyte, frame, y, x) < 0)
                 return AVERROR_INVALIDDATA;
 
-- 
2.52.0


>From c506e86576aecd8986a4f2a3a28fea055545e081 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 04:58:34 +0200
Subject: [PATCH 14/41] avformat/wtvdec: reject file allocation tables that are
 not strictly increasing

The sectors of a sub-file are allocated in ascending order, so a table
that is not strictly increasing is rejected when the sub-file is opened.

Out of memory Replicated through API

Fixes: out of memory
Fixes: 
472529274/clusterfuzz-testcase-minimized-ffmpeg_dem_WTV_fuzzer-5867226843643904
Fixes: 12e707wVt9Ky

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavformat/wtvdec.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libavformat/wtvdec.c b/libavformat/wtvdec.c
index 873ba72243..621870e66f 100644
--- a/libavformat/wtvdec.c
+++ b/libavformat/wtvdec.c
@@ -208,6 +208,15 @@ static AVIOContext * wtvfile_open_sector(unsigned 
first_sector, uint64_t length,
         return NULL;
     }
 
+    for (int i = 1; i < wf->nb_sectors; i++) {
+        if (wf->sectors[i] <= wf->sectors[i - 1]) {
+            av_log(s, AV_LOG_ERROR, "file allocation table is not strictly 
increasing\n");
+            av_freep(&wf->sectors);
+            av_freep(&wf);
+            return NULL;
+        }
+    }
+
     size = avio_size(s->pb);
     if (size >= 0 && (int64_t)wf->sectors[wf->nb_sectors - 1] << 
WTV_SECTOR_BITS > size)
         av_log(s, AV_LOG_WARNING, "truncated file\n");
-- 
2.52.0


>From 5526081180b174792a8448fbabb90eca0418deb6 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 05:01:02 +0200
Subject: [PATCH 15/41] avformat/mov: bound the edit list rebuilt sample index
 by the input size

Out of memory Replicated through API

Fixes: out of memory
Fixes: 
476170159/clusterfuzz-testcase-minimized-ffmpeg_dem_MOV_fuzzer-6100179299401728
Fixes: 6p8Ik9Q1ozLW

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavformat/mov.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/libavformat/mov.c b/libavformat/mov.c
index 5d4f2973e1..a031a7d8eb 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -4547,6 +4547,10 @@ static void mov_fix_index(MOVContext *mov, AVStream *st)
         return;
     }
 
+    int64_t max_entries = avio_size(mov->fc->pb);
+    if (max_entries <= 0)
+        max_entries = INT64_MAX;
+
     // allocate the index ranges array
     msc->index_ranges = av_malloc_array(msc->elst_count + 1,
                                         sizeof(msc->index_ranges[0]));
@@ -4580,7 +4584,8 @@ static void mov_fix_index(MOVContext *mov, AVStream *st)
 
     start_dts = edit_list_dts_entry_end;
 
-    while (get_edit_list_entry(mov, msc, edit_list_index, 
&edit_list_media_time,
+    while (sti->nb_index_entries < max_entries &&
+           get_edit_list_entry(mov, msc, edit_list_index, 
&edit_list_media_time,
                                &edit_list_duration, mov->time_scale)) {
         av_log(mov->fc, AV_LOG_DEBUG, "Processing st: %d, edit list %"PRId64" 
- media time: %"PRId64", duration: %"PRId64"\n",
                st->index, edit_list_index, edit_list_media_time, 
edit_list_duration);
@@ -4646,6 +4651,11 @@ static void mov_fix_index(MOVContext *mov, AVStream *st)
         edit_list_start_encountered = 0;
         found_keyframe_after_edit = 0;
         for (; current < e_old_end; current++, index++) {
+            if (sti->nb_index_entries >= max_entries) {
+                av_log(mov->fc, AV_LOG_ERROR, "Edit list expands the sample 
index beyond the %"PRId64" "
+                       "entries the input could hold\n", max_entries);
+                break;
+            }
             // check  if frame outside edit list mark it for discard
             frame_duration = (current + 1 <  e_old_end) ?
                              ((current + 1)->timestamp - current->timestamp) : 
edit_list_duration;
-- 
2.52.0


>From 79171b20178f8d6e005440ad76bb4c10a07cb7c9 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 04:57:53 +0200
Subject: [PATCH 16/41] avcodec/mjpegdec: handle THP restart intervals in all
 scan types

Timeout Replicated through API

Fixes: Timeout
Fixes: 
565276248/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_THP_fuzzer-6001511570931712
Fixes: Z70IPpK3idWD
Regression since: 5672c410a6adc2764791e7fcc0e34acea07ac263

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/mjpegdec.c | 17 +++--------------
 libavcodec/mjpegdec.h |  5 +++++
 2 files changed, 8 insertions(+), 14 deletions(-)

diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c
index 3dc8662abb..cbb7268ed9 100644
--- a/libavcodec/mjpegdec.c
+++ b/libavcodec/mjpegdec.c
@@ -1477,20 +1477,9 @@ next_field:
             const int copy_mb = mb_bitmask && !get_bits1(&mb_bitmask_gb);
             int restart;
 
-            if (s->avctx->codec_id == AV_CODEC_ID_THP) {
-                if (s->restart_count < 0) {
-                    ret = ff_mjpeg_unescape_sos(s);
-                    if (ret < 0)
-                        return ret;
-                }
-                restart = ff_mjpeg_should_restart(s);
-                if (restart)
-                    align_get_bits(&s->gb);
-            } else {
-                ret = ff_mjpeg_handle_restart(s, &restart);
-                if (ret < 0)
-                    return ret;
-            }
+            ret = ff_mjpeg_handle_restart(s, &restart);
+            if (ret < 0)
+                return ret;
             if (restart) {
                 for (i = 0; i < nb_components; i++)
                     s->last_dc[i] = (4 << s->bits);
diff --git a/libavcodec/mjpegdec.h b/libavcodec/mjpegdec.h
index 5ba96eaa3e..9162de851f 100644
--- a/libavcodec/mjpegdec.h
+++ b/libavcodec/mjpegdec.h
@@ -217,8 +217,13 @@ static inline int 
ff_mjpeg_should_restart(MJpegDecodeContext *s)
 
 static inline int ff_mjpeg_handle_restart(MJpegDecodeContext *s, int *restart)
 {
+    int first = s->restart_count < 0;
     *restart = ff_mjpeg_should_restart(s);
     if (*restart) {
+        if (s->avctx->codec_id == AV_CODEC_ID_THP && !first) {
+            align_get_bits(&s->gb);
+            return 0;
+        }
         int ret = ff_mjpeg_unescape_sos(s);
         if (ret < 0)
             return ret;
-- 
2.52.0


>From a11b40b844a33b63ee7b7559e9125561aeec2d6e Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 05:09:49 +0200
Subject: [PATCH 17/41] avcodec/adpcm: compute the Rhetorex step index update
 in 32-bit wrapping arithmetic

Signed integer overflow Replicated through API with UBSAN

Fixes: signed integer overflow: 1962519552 + 223346688 cannot be represented in 
type 'int'
Fixes: 
570730457/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_ADPCM_RHETOREX_DEC_fuzzer-5448669789487104
Fixes: zBNgmUo4QhnW

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/adpcm.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavcodec/adpcm.c b/libavcodec/adpcm.c
index df25b665a4..3c294d9120 100644
--- a/libavcodec/adpcm.c
+++ b/libavcodec/adpcm.c
@@ -778,7 +778,7 @@ static inline int16_t 
adpcm_rhetorex_expand_nibble(ADPCMChannelStatus *c, uint16
     c->sample2 = c->sample1;
     c->sample1 = sample;
 
-    c->step_index = FFABS(64512 * c->step_index + 65536 * 
rhetorex_index[delta]) >> 16;
+    c->step_index = FFABS((int)(64512U * c->step_index + 65536U * 
rhetorex_index[delta])) >> 16;
 
     return sample;
 }
-- 
2.52.0


>From ed967d3c5878b8b3cee1d239918a4d730b6f2f04 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 05:23:52 +0200
Subject: [PATCH 18/41] avcodec/h264_cabac: do not adjust the CABAC mb_type of
 SI slices

Undefined shift Replicated through API with UBSAN on i386

Fixes: left shift of 1621233664 by 2 places cannot be represented in type 'int'
Fixes: 
538751135/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_H264_fuzzer-5842595151151104
Fixes: 88lh9wyp3VpG

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/h264_cabac.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libavcodec/h264_cabac.c b/libavcodec/h264_cabac.c
index 703b27aa96..7fc4817634 100644
--- a/libavcodec/h264_cabac.c
+++ b/libavcodec/h264_cabac.c
@@ -2018,8 +2018,6 @@ int ff_h264_decode_mb_cabac(const H264Context *h, 
H264SliceContext *sl)
         }
     } else {
         mb_type = decode_cabac_intra_mb_type(sl, 3, 1);
-        if (sl->slice_type == AV_PICTURE_TYPE_SI && mb_type)
-            mb_type--;
         av_assert2(sl->slice_type_nos == AV_PICTURE_TYPE_I);
 decode_intra_mb:
         partition_count = 0;
-- 
2.52.0


>From 1d348cf2cef511c85c143fbef1b5b068770fcdc8 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 05:23:52 +0200
Subject: [PATCH 19/41] avcodec/vvc/cabac: reject entropy coding sync without
 entry points

Undefined shift Replicated through UnModified FFmpeg with UBSAN

Fixes: left shift of 539332544 by 2 places cannot be represented in type 'int'
Fixes: 
543314355/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_VVC_fuzzer-6530021460869120
Fixes: zNquwYqrjFd2

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/vvc/cabac.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libavcodec/vvc/cabac.c b/libavcodec/vvc/cabac.c
index e386cdf4c8..329172e475 100644
--- a/libavcodec/vvc/cabac.c
+++ b/libavcodec/vvc/cabac.c
@@ -855,6 +855,10 @@ int ff_vvc_cabac_init(VVCLocalContext *lc,
             ret = cabac_reinit(lc);
         if (!ret)
             cabac_init_state(lc);
+    } else if (lc->sc->nb_eps == 1 && rx == pps->ctb_to_col_bd[rx] &&
+               lc->fc->ps.sps->r->sps_entropy_coding_sync_enabled_flag) {
+        avpriv_report_missing_feature(lc->fc->log_ctx, "Entropy coding sync 
without entry points");
+        return AVERROR_PATCHWELCOME;
     }
     return ret;
 }
-- 
2.52.0


>From 1a5a84038e5950e77455765caf2ef5d78da6b919 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 05:23:52 +0200
Subject: [PATCH 20/41] avcodec/exif: stop following an IFD chain that loops

Stop at the first offset that was already parsed.

Timeout Replicated through API

Fixes: Timeout
Fixes: 
561208791/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_SMVJPEG_fuzzer-5447460856856576
Fixes: PrLc2prh3sxt
Regression since: 784aa09fa8222f6704d953b5035f8d0f0bec1623

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/exif.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libavcodec/exif.c b/libavcodec/exif.c
index bbac4a436d..aca14bff67 100644
--- a/libavcodec/exif.c
+++ b/libavcodec/exif.c
@@ -930,6 +930,8 @@ int av_exif_parse_buffer(void *logctx, const uint8_t *buf, 
size_t size,
      * parse IFD0 here. If the return value is positive that tells us
      * there is subimage metadata, but we don't parse that IFD here
      */
+    int ifd_offsets[17] = { bytestream2_tell(&gbytes) };
+    int nb_ifds = 1;
     ret = exif_parse_ifd_list(logctx, &gbytes, le, 0, ifd, 0);
     if (ret < 0) {
         av_log(logctx, AV_LOG_ERROR, "error decoding EXIF data: %s\n", 
av_err2str(ret));
@@ -943,6 +945,13 @@ int av_exif_parse_buffer(void *logctx, const uint8_t *buf, 
size_t size,
     /* cap at 16 extra IFDs for sanity/parse security */
     for (int extra_tag = 0xFFFCu; extra_tag > 0xFFECu; extra_tag--) {
         AVExifMetadata extra_ifd = { 0 };
+        for (int i = 0; i < nb_ifds; i++) {
+            if (ifd_offsets[i] == next) {
+                av_log(logctx, AV_LOG_WARNING, "EXIF IFD chain loops back to 
offset %d\n", next);
+                goto finish;
+            }
+        }
+        ifd_offsets[nb_ifds++] = next;
         ret = exif_parse_ifd_list(logctx, &gbytes, le, 0, &extra_ifd, 1);
         if (ret < 0) {
             av_exif_free(&extra_ifd);
-- 
2.52.0


>From d0dd99d062f548708dc54affc2863b25b2576cdc Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 05:28:20 +0200
Subject: [PATCH 21/41] avcodec/exif: bound the parsed IFD entries by the size
 of the EXIF buffer

Every entry of a well formed IFD tree is a distinct 12 byte record
inside the buffer, so a tree cannot have more entries than the buffer
holds records. Count the parsed entries and reject the tree once that
number is exceeded.

Timeout Replicated through UnModified FFmpeg

Found during triage/debugging of OSS-Fuzz issue 561208791

Fixes: Timeout
Fixes: yiiM6atofYKk/nested16.jpg / gen_nested_jpg.py
Fixes: yiiM6atofYKk

Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/exif.c | 25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)

diff --git a/libavcodec/exif.c b/libavcodec/exif.c
index aca14bff67..86baa8a27c 100644
--- a/libavcodec/exif.c
+++ b/libavcodec/exif.c
@@ -476,10 +476,11 @@ static int exif_get_makernote_offset(GetByteContext *gb)
 }
 
 static int exif_parse_ifd_list(void *logctx, GetByteContext *gb, int le,
-                               int depth, AVExifMetadata *ifd, int guess);
+                               int depth, int *entries_left,
+                               AVExifMetadata *ifd, int guess);
 
 static int exif_decode_tag(void *logctx, GetByteContext *gb, int le,
-                           int depth, AVExifEntry *entry)
+                           int depth, int *entries_left, AVExifEntry *entry)
 {
     int ret = 0, makernote_offset = -1, tell, is_ifd, count;
     enum AVTiffDataType type;
@@ -532,7 +533,8 @@ static int exif_decode_tag(void *logctx, GetByteContext 
*gb, int le,
                 return AVERROR(ENOMEM);
             bytestream2_get_buffer(gb, entry->ifd_lead, entry->ifd_offset);
         }
-        ret = exif_parse_ifd_list(logctx, gb, le, depth + 1, 
&entry->value.ifd, entry->id == MAKERNOTE_TAG);
+        ret = exif_parse_ifd_list(logctx, gb, le, depth + 1, entries_left,
+                                  &entry->value.ifd, entry->id == 
MAKERNOTE_TAG);
         if (ret < 0 && entry->id == MAKERNOTE_TAG) {
             /*
              * we guessed that MakerNote was an IFD
@@ -559,7 +561,8 @@ end:
 }
 
 static int exif_parse_ifd_list(void *logctx, GetByteContext *gb, int le,
-                               int depth, AVExifMetadata *ifd, int guess)
+                               int depth, int *entries_left,
+                               AVExifMetadata *ifd, int guess)
 {
     uint32_t entries;
     size_t required_size;
@@ -589,6 +592,13 @@ static int exif_parse_ifd_list(void *logctx, 
GetByteContext *gb, int le,
         ret = AVERROR_INVALIDDATA;
         goto end;
     }
+    if (entries > *entries_left) {
+        av_log(logctx, guess ? AV_LOG_DEBUG : AV_LOG_ERROR,
+               "IFD tree has more entries than the EXIF buffer can hold\n");
+        ret = AVERROR_INVALIDDATA;
+        goto end;
+    }
+    *entries_left -= entries;
 
     ifd->count = entries;
     av_log(logctx, AV_LOG_DEBUG, "entry count for IFD: %u\n", ifd->count);
@@ -616,7 +626,7 @@ static int exif_parse_ifd_list(void *logctx, GetByteContext 
*gb, int le,
     memset(ifd->entries, 0, required_size);
 
     for (uint32_t i = 0; i < entries; i++) {
-        ret = exif_decode_tag(logctx, gb, le, depth, &ifd->entries[i]);
+        ret = exif_decode_tag(logctx, gb, le, depth, entries_left, 
&ifd->entries[i]);
         if (ret < 0)
             goto end;
     }
@@ -932,7 +942,8 @@ int av_exif_parse_buffer(void *logctx, const uint8_t *buf, 
size_t size,
      */
     int ifd_offsets[17] = { bytestream2_tell(&gbytes) };
     int nb_ifds = 1;
-    ret = exif_parse_ifd_list(logctx, &gbytes, le, 0, ifd, 0);
+    int entries_left = size / BASE_TAG_SIZE;
+    ret = exif_parse_ifd_list(logctx, &gbytes, le, 0, &entries_left, ifd, 0);
     if (ret < 0) {
         av_log(logctx, AV_LOG_ERROR, "error decoding EXIF data: %s\n", 
av_err2str(ret));
         return ret;
@@ -952,7 +963,7 @@ int av_exif_parse_buffer(void *logctx, const uint8_t *buf, 
size_t size,
             }
         }
         ifd_offsets[nb_ifds++] = next;
-        ret = exif_parse_ifd_list(logctx, &gbytes, le, 0, &extra_ifd, 1);
+        ret = exif_parse_ifd_list(logctx, &gbytes, le, 0, &entries_left, 
&extra_ifd, 1);
         if (ret < 0) {
             av_exif_free(&extra_ifd);
             break;
-- 
2.52.0


>From 45c15a183f091818f85b2cb285692c201654119b Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 06:20:43 +0200
Subject: [PATCH 22/41] avcodec/ftr: reject sub-decoder output that yields no
 frame

Assertion failure Replicated through UnModified FFmpeg

Found during triage/review of OSS-Fuzz issue 538301564

Fixes: assertion failure
Fixes: Z2pdJZ9KWkmq/ftr-idend.wav / mkftr.py
Fixes: Z2pdJZ9KWkmq

Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/ftr.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libavcodec/ftr.c b/libavcodec/ftr.c
index 31aef538f9..805edf13d7 100644
--- a/libavcodec/ftr.c
+++ b/libavcodec/ftr.c
@@ -132,6 +132,8 @@ static int ftr_decode_frame(AVCodecContext *avctx, AVFrame 
*frame,
         }
 
         ret = avcodec_receive_frame(codec_avctx, s->frame);
+        if (ret == AVERROR(EAGAIN))
+            return AVERROR_INVALIDDATA;
         if (ret < 0)
             return ret;
 
-- 
2.52.0


>From f768e379811fc03e93e2ed11e0b6f15385f9b8c7 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 06:20:44 +0200
Subject: [PATCH 23/41] avformat/wtvdec: append depth-2 allocation table blocks
 at the current sector count

Found during triage/review of OSS-Fuzz issue 472529274

Fixes: VqowcCmeBsvg

Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavformat/wtvdec.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavformat/wtvdec.c b/libavformat/wtvdec.c
index 621870e66f..30e605cd25 100644
--- a/libavformat/wtvdec.c
+++ b/libavformat/wtvdec.c
@@ -193,7 +193,7 @@ static AVIOContext * wtvfile_open_sector(unsigned 
first_sector, uint64_t length,
         for (i = 0; i < nb_sectors1; i++) {
             if (seek_by_sector(s->pb, sectors1[i], 0) < 0)
                 break;
-            wf->nb_sectors += read_ints(s->pb, wf->sectors + i * 
WTV_SECTOR_SIZE / 4, WTV_SECTOR_SIZE / 4);
+            wf->nb_sectors += read_ints(s->pb, wf->sectors + wf->nb_sectors, 
WTV_SECTOR_SIZE / 4);
         }
     } else {
         av_log(s, AV_LOG_ERROR, "unsupported file allocation table depth 
(0x%x)\n", depth);
-- 
2.52.0


>From 1a14b01db3a40119e5935eaaddcb47b17bed72e3 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 06:38:14 +0200
Subject: [PATCH 24/41] avcodec/cbs_h266: allocate the VPS profile_tier_level
 array on demand

Allocate the array for the number of PTLs the VPS signals, like the
extension data. This shrinks H266RawVPS to 41 KB.

Out of memory Replicated through API

Found during triage/review of OSS-Fuzz issue 529105369

Fixes: out of memory
Fixes: b0PmzXuS2hhp/vps10k.bin / gen_vps.py
Fixes: b0PmzXuS2hhp

Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/cbs_h266.c                 | 14 ++++++++++++--
 libavcodec/cbs_h266.h                 |  3 ++-
 libavcodec/cbs_h266_syntax_template.c |  3 +++
 3 files changed, 17 insertions(+), 3 deletions(-)

diff --git a/libavcodec/cbs_h266.c b/libavcodec/cbs_h266.c
index 8c92b4f557..f0782c814b 100644
--- a/libavcodec/cbs_h266.c
+++ b/libavcodec/cbs_h266.c
@@ -143,7 +143,7 @@ static int FUNC_H266(name) args
                                         AV_INPUT_BUFFER_PADDING_SIZE); \
         if (!name ## _ref) \
             return AVERROR(ENOMEM); \
-        name = name ## _ref->data; \
+        name = (void *)name ## _ref->data; \
     } while (0)
 
 #define FUNC(name) FUNC_H266(name)
@@ -760,7 +760,17 @@ static void cbs_h266_free_sei(AVRefStructOpaque unused, 
void *content)
 static CodedBitstreamUnitTypeDescriptor cbs_h266_unit_types[] = {
     CBS_UNIT_TYPE_INTERNAL_REF(VVC_DCI_NUT, H266RawDCI, extension_data.data),
     CBS_UNIT_TYPE_INTERNAL_REF(VVC_OPI_NUT, H266RawOPI, extension_data.data),
-    CBS_UNIT_TYPE_INTERNAL_REF(VVC_VPS_NUT, H266RawVPS, extension_data.data),
+    {
+        .nb_unit_types     = 1,
+        .unit_type.list[0] = VVC_VPS_NUT,
+        .content_type      = CBS_CONTENT_TYPE_INTERNAL_REFS,
+        .content_size      = sizeof(H266RawVPS),
+        .type.ref          = {
+            .nb_offsets = 2,
+            .offsets    = { offsetof(H266RawVPS, vps_profile_tier_level),
+                            offsetof(H266RawVPS, extension_data.data) }
+        },
+    },
     {
         .nb_unit_types     = 1,
         .unit_type.list[0] = VVC_SPS_NUT,
diff --git a/libavcodec/cbs_h266.h b/libavcodec/cbs_h266.h
index 2751ef5dd0..4833048479 100644
--- a/libavcodec/cbs_h266.h
+++ b/libavcodec/cbs_h266.h
@@ -280,7 +280,8 @@ typedef struct H266RawVPS {
     uint8_t  vps_num_ptls_minus1;
     uint8_t  vps_pt_present_flag[VVC_MAX_PTLS];
     uint8_t  vps_ptl_max_tid[VVC_MAX_PTLS];
-    H266RawProfileTierLevel vps_profile_tier_level[VVC_MAX_PTLS];
+    H266RawProfileTierLevel *vps_profile_tier_level;
+    AVBufferRef             *vps_profile_tier_level_ref;
     uint8_t  vps_ols_ptl_idx[VVC_MAX_TOTAL_NUM_OLSS];
 
     uint16_t vps_num_dpb_params_minus1;
diff --git a/libavcodec/cbs_h266_syntax_template.c 
b/libavcodec/cbs_h266_syntax_template.c
index c7152d4e2c..a1a5c4405a 100644
--- a/libavcodec/cbs_h266_syntax_template.c
+++ b/libavcodec/cbs_h266_syntax_template.c
@@ -930,6 +930,9 @@ static int FUNC(vps) (CodedBitstreamContext *ctx, RWContext 
*rw,
             return AVERROR_INVALIDDATA;
     }
 
+    allocate(current->vps_profile_tier_level,
+             (current->vps_num_ptls_minus1 + 1) *
+             sizeof(*current->vps_profile_tier_level));
     for (i = 0; i <= current->vps_num_ptls_minus1; i++) {
         CHECK(FUNC(profile_tier_level) (ctx, rw,
                                         current->vps_profile_tier_level + i,
-- 
2.52.0


>From 3d698ca0c1d8840ec2e3ce278ff50252b8b41ea4 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 06:45:00 +0200
Subject: [PATCH 25/41] avformat/sbgdec: compute the extradata size of the
 intervals in 64 bits

Signed integer overflow Replicated through UnModified FFmpeg with UBSAN, only 
with -max_alloc raised above its default

Found during triage/review of OSS-Fuzz issue 474457174

Fixes: tETfcRw9ov1Y/a.sbg / gen_a.py
Fixes: tETfcRw9ov1Y

Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavformat/sbgdec.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libavformat/sbgdec.c b/libavformat/sbgdec.c
index 0792520723..fb5593511b 100644
--- a/libavformat/sbgdec.c
+++ b/libavformat/sbgdec.c
@@ -1347,15 +1347,15 @@ static int generate_intervals(void *log, struct 
sbg_script *s, int sample_rate,
 static int encode_intervals(struct sbg_script *s, AVCodecParameters *par,
                             struct ws_intervals *inter)
 {
-    int i, edata_size = 4, ret;
+    int i, ret;
+    int64_t edata_size = 4;
     uint8_t *edata;
 
-    for (i = 0; i < inter->nb_inter; i++) {
+    for (i = 0; i < inter->nb_inter; i++)
         edata_size += inter->inter[i].type == WS_SINE  ? 44 :
                       inter->inter[i].type == WS_NOISE ? 32 : 0;
-        if (edata_size < 0)
-            return AVERROR(ENOMEM);
-    }
+    if (edata_size > INT_MAX)
+        return AVERROR(ENOMEM);
     if ((ret = ff_alloc_extradata(par, edata_size)) < 0)
         return ret;
     edata = par->extradata;
-- 
2.52.0


>From cac569e9b9f639318acc2c11c9fe5a9aba6f97fc Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 06:45:00 +0200
Subject: [PATCH 26/41] avformat/sbgdec: bound the number of events a script
 expands to

Out of memory Replicated through API

Found during triage/review of OSS-Fuzz issue 474457174

Fixes: out of memory
Fixes: Cw8GY4kWak6W/b24.sbg / gen_b.py
Fixes: Cw8GY4kWak6W

Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavformat/sbgdec.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/libavformat/sbgdec.c b/libavformat/sbgdec.c
index fb5593511b..49ccc6eeec 100644
--- a/libavformat/sbgdec.c
+++ b/libavformat/sbgdec.c
@@ -945,7 +945,8 @@ static int expand_timestamps(void *log, struct sbg_script 
*s)
 }
 
 static int expand_tseq(void *log, struct sbg_script *s, int *nb_ev_max,
-                       int64_t t0, struct sbg_script_tseq *tseq)
+                       int64_t max_events, int64_t t0,
+                       struct sbg_script_tseq *tseq)
 {
     int i, r;
     struct sbg_script_definition *def;
@@ -975,11 +976,15 @@ static int expand_tseq(void *log, struct sbg_script *s, 
int *nb_ev_max,
     if (def->type == 'B') {
         be = s->block_tseq + def->elements;
         for (i = 0; i < def->nb_elements; i++) {
-            r = expand_tseq(log, s, nb_ev_max, t0, &be[i]);
+            r = expand_tseq(log, s, nb_ev_max, max_events, t0, &be[i]);
             if (r < 0)
                 return r;
         }
     } else {
+        if (s->nb_events >= max_events) {
+            av_log(log, AV_LOG_ERROR, "Too many events\n");
+            return AVERROR_INVALIDDATA;
+        }
         ev = alloc_array_elem((void **)&s->events, sizeof(*ev),
                               &s->nb_events, nb_ev_max);
         if (!ev)
@@ -993,7 +998,7 @@ static int expand_tseq(void *log, struct sbg_script *s, int 
*nb_ev_max,
     return 0;
 }
 
-static int expand_script(void *log, struct sbg_script *s)
+static int expand_script(void *log, struct sbg_script *s, int64_t max_events)
 {
     int i, r, nb_events_max = 0;
 
@@ -1001,7 +1006,7 @@ static int expand_script(void *log, struct sbg_script *s)
     if (r < 0)
         return r;
     for (i = 0; i < s->nb_tseq; i++) {
-        r = expand_tseq(log, s, &nb_events_max, 0, &s->tseq[i]);
+        r = expand_tseq(log, s, &nb_events_max, max_events, 0, &s->tseq[i]);
         if (r < 0)
             return r;
     }
@@ -1426,7 +1431,7 @@ static av_cold int sbg_read_header(AVFormatContext *avf)
     if (script.opt_mix)
         av_log(avf, AV_LOG_WARNING, "Mix feature not implemented: "
                "-m is ignored and mix channels will be silent.\n");
-    r = expand_script(avf, &script);
+    r = expand_script(avf, &script, 4 * (int64_t)bprint.len);
     if (r < 0)
         goto fail2;
     av_bprint_finalize(&bprint, NULL);
-- 
2.52.0


>From 482576cfa02fef342890b23537b1259279a62d1c Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 06:45:00 +0200
Subject: [PATCH 27/41] avformat/sbgdec: fix the timestamps of the events
 expanded from blocks

Found during triage/review of OSS-Fuzz issue 474457174

Fixes: QLpKsyxye22s

Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavformat/sbgdec.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libavformat/sbgdec.c b/libavformat/sbgdec.c
index 49ccc6eeec..99e44e30dd 100644
--- a/libavformat/sbgdec.c
+++ b/libavformat/sbgdec.c
@@ -549,7 +549,7 @@ static int parse_time_sequence(struct sbg_parser *p, int 
inblock)
 
     if (av_sat_add64(p->current_time.t, rel_ts) != p->current_time.t + 
(uint64_t)rel_ts)
         return AVERROR_INVALIDDATA;
-    ts.t    = p->current_time.t + rel_ts;
+    ts.t    = inblock ? rel_ts : p->current_time.t + rel_ts;
     r = parse_fade(p, &fade);
     if (r < 0)
         return r;
@@ -989,7 +989,7 @@ static int expand_tseq(void *log, struct sbg_script *s, int 
*nb_ev_max,
                               &s->nb_events, nb_ev_max);
         if (!ev)
             return AVERROR(ENOMEM);
-        ev->ts          = tseq->ts.t;
+        ev->ts          = t0;
         ev->elements    = def->elements;
         ev->nb_elements = def->nb_elements;
         ev->fade        = tseq->fade;
-- 
2.52.0


>From bba43a9de1bf561f4e35681c26c031e24867b211 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 06:46:25 +0200
Subject: [PATCH 28/41] avutil/dict: copy dictionaries without duplicate keys
 in linear time

Timeout Replicated through API

Found during triage/review of OSS-Fuzz issue 561208791

Fixes: Timeout
Fixes: W8DJK4OR7cgk/flat300.bin / gen_flat.py
Fixes: W8DJK4OR7cgk

Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavutil/dict.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/libavutil/dict.c b/libavutil/dict.c
index fafb454fd3..2a24102f06 100644
--- a/libavutil/dict.c
+++ b/libavutil/dict.c
@@ -32,6 +32,7 @@
 struct AVDictionary {
     int count;
     AVDictionaryEntry *elems;
+    int duplicate_keys;
 };
 
 int av_dict_count(const AVDictionary *m)
@@ -154,6 +155,8 @@ int av_dict_set(AVDictionary **pm, const char *key, const 
char *value,
         m->elems[m->count].key = copy_key;
         m->elems[m->count].value = copy_value;
         m->count++;
+        if (flags & (AV_DICT_MATCH_CASE | AV_DICT_IGNORE_SUFFIX | 
AV_DICT_MULTIKEY))
+            m->duplicate_keys = 1;
     } else {
         err = 0;
         goto end;
@@ -248,6 +251,29 @@ int av_dict_copy(AVDictionary **dst, const AVDictionary 
*src, int flags)
 {
     const AVDictionaryEntry *t = NULL;
 
+    if (!*dst && src && src->count && !src->duplicate_keys &&
+        !(flags & AV_DICT_IGNORE_SUFFIX)) {
+        AVDictionary *m = av_mallocz(sizeof(*m));
+        if (!m)
+            return AVERROR(ENOMEM);
+        m->elems = av_calloc(src->count, sizeof(*m->elems));
+        if (!m->elems) {
+            av_free(m);
+            return AVERROR(ENOMEM);
+        }
+        *dst = m;
+        for (int i = 0; i < src->count; i++) {
+            m->elems[i].key   = av_strdup(src->elems[i].key);
+            m->elems[i].value = av_strdup(src->elems[i].value);
+            m->count++;
+            if (!m->elems[i].key || !m->elems[i].value) {
+                av_dict_free(dst);
+                return AVERROR(ENOMEM);
+            }
+        }
+        return 0;
+    }
+
     while ((t = av_dict_iterate(src, t))) {
         int ret = av_dict_set(dst, t->key, t->value, flags);
         if (ret < 0)
-- 
2.52.0


>From 0e3465bf686be5c933cd172b2754a816c79a2473 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 06:48:07 +0200
Subject: [PATCH 29/41] avformat/mov: compute the time to sample position of an
 edit directly

find_prev_closest_index() walked the time to sample table from the
first sample up to the sample an edit starts at, for every edit of a
track with composition offsets, so the index rebuild cost
elst_count * nb_samples. An 800 kB file with 50000 edits over 200000
samples took 58 seconds to open.

Timeout Replicated through UnModified FFmpeg

Found during triage/review of OSS-Fuzz issue 476170159

Fixes: Timeout
Fixes: 6w5v8aNoJQB4/slow.mp4 / genmp4.py
Fixes: 6w5v8aNoJQB4

Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavformat/mov.c | 11 +----------
 1 file changed, 1 insertion(+), 10 deletions(-)

diff --git a/libavformat/mov.c b/libavformat/mov.c
index a031a7d8eb..8d3adb4304 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -4269,17 +4269,8 @@ static int find_prev_closest_index(AVStream *st,
         av_assert0(tts_index);
         av_assert0(tts_sample);
         // Find out the ctts_index for the found frame.
-        *tts_index = 0;
+        *tts_index = FFMIN(*index, tts_count);
         *tts_sample = 0;
-        for (int64_t index_tts_count = 0; index_tts_count < *index; 
index_tts_count++) {
-            if (*tts_index < tts_count) {
-                (*tts_sample)++;
-                if (tts_data[*tts_index].count == *tts_sample) {
-                    (*tts_index)++;
-                    *tts_sample = 0;
-                }
-            }
-        }
 
         while (*index >= 0 && (*tts_index) >= 0 && (*tts_index) < tts_count) {
             // Find a "key frame" with PTS <= timestamp_pts (So that we can 
decode B-frames correctly).
-- 
2.52.0


>From 6a14fabd7017019db2845287a4ad2f41f14e4ce5 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 06:48:07 +0200
Subject: [PATCH 30/41] avformat/mov: saturate the audio pre-roll search time
 of an edit

The search for the frame one second before an audio edit subtracted the
time scale from the edit media time, which overflows for media times
near INT64_MIN. Any result below the first index entry is clamped to it
anyway, so saturating the subtraction keeps the search unchanged.

Signed integer overflow Replicated through API with UBSAN

Found during triage/review of OSS-Fuzz issue 476170159

Fixes: signed integer overflow: -9223372036854775808 - 1000 cannot be 
represented in type 'int64_t'
Fixes: b9W8hJVNRZGN/neg-audio-min.mp4
Fixes: b9W8hJVNRZGN

Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavformat/mov.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavformat/mov.c b/libavformat/mov.c
index 8d3adb4304..1b211c448f 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -4617,7 +4617,7 @@ static void mov_fix_index(MOVContext *mov, AVStream *st)
             // Audio decoders like AAC need need a decoder delay samples 
previous to the current sample,
             // to correctly decode this frame. Hence for audio we seek to a 
frame 1 sec. before the
             // edit_list_media_time to cover the decoder delay.
-            search_timestamp = FFMAX(search_timestamp - msc->time_scale, 
e_old[0].timestamp);
+            search_timestamp = FFMAX(av_sat_sub64(search_timestamp, 
msc->time_scale), e_old[0].timestamp);
         }
 
         if (find_prev_closest_index(st, e_old, nb_old, tts_data_old, 
tts_count_old, search_timestamp, 0,
-- 
2.52.0


>From 68520265c8291ae676e0e97fa6045edc9262d672 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 06:48:07 +0200
Subject: [PATCH 31/41] avformat/mov: keep the duration of edits with a
 negative media time

The overflow check of the rescaled edit duration added the media time as
an unsigned value, so a negative media time wrapped the sum and the
duration of any edit starting more than its duration before composition
time 0 was zeroed, dropping the edit. The media time of an edit is a
composition time (ISO/IEC 14496-12 8.6.6.3) and composition times are
negative when signed composition offsets place the first frames before
zero (8.6.1.3), with the smallest one recommended to match the start of
the first edit (8.6.1.4).

Found during triage/review of OSS-Fuzz issue 476170159

Fixes: dDfkF1EeYsUC
Regression since: 2882d30e3acfc3155e2be11db653c7c721f94f34

Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavformat/mov.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavformat/mov.c b/libavformat/mov.c
index 1b211c448f..d23dd449d5 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -4197,7 +4197,7 @@ static int get_edit_list_entry(MOVContext *mov,
                                      global_timescale);
 
     if (*edit_list_duration < 0 ||
-        *edit_list_duration + (uint64_t)*edit_list_media_time > INT64_MAX)
+        *edit_list_media_time > INT64_MAX - *edit_list_duration)
         *edit_list_duration = 0;
 
     return 1;
-- 
2.52.0


>From 25abfebd8cfd80829240a99e835ae2f86411318e Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 06:48:07 +0200
Subject: [PATCH 32/41] avformat/mov: skip empty edits in the middle of an edit
 list

Only empty edits at the start of the list were skipped; an empty edit
after a non-empty one was replayed as media starting at time -1, so the
first frames of the track were emitted again where the presentation has
a gap (ISO/IEC 14496-12 8.6.6.3). Skip every empty edit; its duration
still advances the timeline so the following edit starts after the gap,
while only the leading ones keep counting towards the start time.

Found during triage/review of OSS-Fuzz issue 476170159

Fixes: qqcLfY5ZRyHL
Regression since: 97f47fd601d0e380109f501e4aabff71ffbad052

Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavformat/mov.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libavformat/mov.c b/libavformat/mov.c
index d23dd449d5..46a6d0f46b 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -4589,8 +4589,9 @@ static void mov_fix_index(MOVContext *mov, AVStream *st)
             break;
         }
         num_discarded_begin = 0;
-        if (!found_non_empty_edit && edit_list_media_time == -1) {
-            empty_edits_sum_duration += edit_list_duration;
+        if (edit_list_media_time == -1) {
+            if (!found_non_empty_edit)
+                empty_edits_sum_duration += edit_list_duration;
             continue;
         }
         found_non_empty_edit = 1;
-- 
2.52.0


>From 1fd37f0292ab883902fe9215cd0ccceb5cfe3724 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 06:48:07 +0200
Subject: [PATCH 33/41] avformat/mov: bound the sample count of a track by the
 input size

A stsz atom with a constant sample size declares the number of samples
without a table backing it, so a 20 kB file can claim 33 million
samples. mov_build_index() then creates an index entry for each of
them, which takes seconds and hundreds of megabytes although the data
of all but a handful of these samples would lie beyond the end of the
file.

Timeout Replicated through API

Fixes: Timeout
Fixes: 
471593733/clusterfuzz-testcase-minimized-ffmpeg_dem_MOV_fuzzer-6064488551088128
Fixes: RmtfUx4MsoOs

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavformat/mov.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/libavformat/mov.c b/libavformat/mov.c
index 46a6d0f46b..dd9fb84197 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -5046,6 +5046,13 @@ static void mov_build_index(MOVContext *mov, AVStream 
*st)
             return;
         if (sc->sample_count >= UINT_MAX / sizeof(*sti->index_entries) - 
sti->nb_index_entries)
             return;
+        int64_t input_size = avio_size(mov->fc->pb);
+        if (input_size > 0 && sc->sample_count > input_size &&
+            !(mov->enable_drefs && sc->dref_id-1 < sc->drefs_count && 
sc->drefs[sc->dref_id-1].path)) {
+            av_log(mov->fc, AV_LOG_ERROR, "sample count %u exceeds the 
%"PRId64" "
+                   "samples the input can hold\n", sc->sample_count, 
input_size);
+            return;
+        }
         if (av_reallocp_array(&sti->index_entries,
                               sti->nb_index_entries + sc->sample_count,
                               sizeof(*sti->index_entries)) < 0) {
-- 
2.52.0


>From ba09130005a3b076609f8461b07cf575a8abead8 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 06:48:33 +0200
Subject: [PATCH 34/41] avformat/asfdec_o: reject Simple Index Objects whose
 size does not fit an int64

Timeout Replicated through API

Fixes: Timeout
Fixes: 
472415045/clusterfuzz-testcase-minimized-ffmpeg_dem_ASF_O_fuzzer-5931601155653632
Fixes: nsrEzoEJYM73

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavformat/asfdec_o.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavformat/asfdec_o.c b/libavformat/asfdec_o.c
index f3d3d06891..4961c1617a 100644
--- a/libavformat/asfdec_o.c
+++ b/libavformat/asfdec_o.c
@@ -841,7 +841,7 @@ static int asf_read_simple_index(AVFormatContext *s, const 
GUIDParseTable *g)
     int64_t offset;
     uint64_t size = avio_rl64(pb);
 
-    if (size < 24)
+    if (size < 24 || size > INT64_MAX)
         return AVERROR_INVALIDDATA;
 
     // simple index objects should be ordered by stream number, this loop 
tries to find
-- 
2.52.0


>From afde0f130fb96510aa8956aab071981b8e8884b8 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 06:48:33 +0200
Subject: [PATCH 35/41] avformat/asfdec_o: reject packets positioned beyond
 INT64_MAX/2

Signed integer overflow Replicated through API with UBSAN

Fixes: signed integer overflow: -9223372036854754902 - 29780 cannot be 
represented in type 'int64_t'
Fixes: 
490256472/clusterfuzz-testcase-minimized-ffmpeg_dem_ASF_O_fuzzer-5108938455711744
Fixes: bbbW1GCPwyFw
Regression since: 736e9e69d5dbbe1d81885dfef59917eb915d2f96

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavformat/asfdec_o.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libavformat/asfdec_o.c b/libavformat/asfdec_o.c
index 4961c1617a..420324114f 100644
--- a/libavformat/asfdec_o.c
+++ b/libavformat/asfdec_o.c
@@ -1225,7 +1225,7 @@ static int asf_read_packet_header(AVFormatContext *s)
 
     asf->packet_offset = avio_tell(pb);
     if (asf->packet_offset > INT64_MAX/2)
-        asf->packet_offset = 0;
+        return AVERROR_INVALIDDATA;
     error_flags = avio_r8(pb); // read Error Correction Flags
     if (error_flags & ASF_PACKET_FLAG_ERROR_CORRECTION_PRESENT) {
         if (!(error_flags & ASF_ERROR_CORRECTION_LENGTH_TYPE)) {
@@ -1309,7 +1309,8 @@ static int asf_read_packet(AVFormatContext *s, AVPacket 
*pkt)
         return AVERROR_EOF;
     while (!pb->eof_reached) {
         if (asf->state == PARSE_PACKET_HEADER) {
-            asf_read_packet_header(s);
+            if ((ret = asf_read_packet_header(s)) < 0)
+                return ret;
             if (pb->eof_reached)
                 break;
             if (!asf->nb_mult_left)
-- 
2.52.0


>From 67e6a0b7d24880fd799b94fac4c9d516d777cd29 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 06:49:06 +0200
Subject: [PATCH 36/41] avcodec/proresenc_kostya: keep the worst-case slice
 size free before encoding a slice

Assertion failure Replicated through Modified FFmpeg (fuzzer harness built with 
--disable-asm) and through UnModified FFmpeg with a 10-bit noise input and 
-bits_per_mb 128

Fixes: assertion failure
Fixes: 
471605669/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_PRORES_KS_fuzzer-5471300752834560
Fixes: Jmc4pJGx6NWH

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/proresenc_kostya.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/libavcodec/proresenc_kostya.c b/libavcodec/proresenc_kostya.c
index 847a47bd2c..65346ca170 100644
--- a/libavcodec/proresenc_kostya.c
+++ b/libavcodec/proresenc_kostya.c
@@ -762,10 +762,14 @@ static int encode_frame(AVCodecContext *avctx, AVPacket 
*pkt,
     int frame_size, picture_size, slice_size;
     int pkt_size, ret;
     int max_slice_size = (ctx->frame_size_upper_bound - 200) / 
(ctx->pictures_per_frame * ctx->slices_per_picture + 1);
+    int slice_size_upper_bound = ctx->mbs_per_slice * (ctx->chroma_factor == 
CFACTOR_Y444 ? 768 : 512) * 8;
     uint8_t frame_flags;
 
+    if (ctx->alpha_bits)
+        slice_size_upper_bound += ctx->mbs_per_slice * 256 * (1 + 
ctx->alpha_bits + 1) + 7 >> 3;
+
     ctx->pic = pic;
-    pkt_size = ctx->frame_size_upper_bound;
+    pkt_size = ctx->frame_size_upper_bound + slice_size_upper_bound;
 
     if ((ret = ff_alloc_packet(avctx, pkt, pkt_size + 
FF_INPUT_BUFFER_MIN_SIZE)) < 0)
         return ret;
@@ -839,7 +843,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket 
*pkt,
                 bytestream_put_byte(&buf, slice_hdr_size << 3);
                 slice_hdr = buf;
                 buf += slice_hdr_size - 1;
-                if (pkt_size <= buf - orig_buf + 2 * max_slice_size) {
+                if (pkt_size - (buf - orig_buf) < slice_size_upper_bound) {
                     uint8_t *start = pkt->data;
                     // Recompute new size according to max_slice_size
                     // and deduce delta
@@ -847,7 +851,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket 
*pkt,
                                 ctx->slices_per_picture + 1) *
                                 max_slice_size - pkt_size;
 
-                    delta = FFMAX(delta, 2 * max_slice_size);
+                    delta = FFMAX(delta, slice_size_upper_bound);
                     ctx->frame_size_upper_bound += delta;
 
                     if (!ctx->warn) {
-- 
2.52.0


>From 852289976878c564b39a3b3d577e1d4f30eae9c2 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 07:15:39 +0200
Subject: [PATCH 37/41] avformat/asfdec_o: truncate a media object whose
 payload data ends at EOF

Use of uninitialized memory Replicated through UnModified FFmpeg with Valgrind

Found during triage/review of OSS-Fuzz issues 472415045 and 490256472

Fixes: use of uninitialized memory
Fixes: h3BedIQL9KeL

Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavformat/asfdec_o.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/libavformat/asfdec_o.c b/libavformat/asfdec_o.c
index 420324114f..cb721993b2 100644
--- a/libavformat/asfdec_o.c
+++ b/libavformat/asfdec_o.c
@@ -1077,6 +1077,11 @@ static int asf_read_multiple_payload(AVFormatContext *s, 
AVPacket *pkt,
             ff_asfcrypt_dec(s->key, p, ret);
         avio_skip(pb, skip);
         asf_pkt->size_left -= pay_len;
+        if (ret < pay_len) {
+            av_shrink_packet(asf_pkt->avpkt, p - asf_pkt->avpkt->data + ret);
+            asf_pkt->data_size = asf_pkt->avpkt->size;
+            asf_pkt->size_left = 0;
+        }
         asf->nb_mult_left--;
     }
 
@@ -1128,6 +1133,11 @@ static int asf_read_single_payload(AVFormatContext *s, 
ASFPacket *asf_pkt)
         return ret;
     if (s->key && s->keylen == 20)
             ff_asfcrypt_dec(s->key, p, ret);
+    if (ret < size) {
+        av_shrink_packet(asf_pkt->avpkt, p - asf_pkt->avpkt->data + ret);
+        asf_pkt->data_size = asf_pkt->avpkt->size;
+        asf_pkt->size_left = 0;
+    }
     if (asf->packet_size_internal)
         avio_skip(pb, asf->packet_size - asf->packet_size_internal);
     avio_skip(pb, asf->pad_len); // skip padding
-- 
2.52.0


>From 88bfdf02e28c955264b2f3f47619192ee29e42bd Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 07:15:39 +0200
Subject: [PATCH 38/41] avformat/asfdec_o: deinterleave spread audio completely

Use of uninitialized memory Replicated through UnModified FFmpeg with Valgrind

Found during triage/review of OSS-Fuzz issues 472415045 and 490256472

Fixes: use of uninitialized memory
Fixes: mPhE3ApFVs7s

Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavformat/asfdec_o.c | 17 +++++------------
 1 file changed, 5 insertions(+), 12 deletions(-)

diff --git a/libavformat/asfdec_o.c b/libavformat/asfdec_o.c
index cb721993b2..2748609b9e 100644
--- a/libavformat/asfdec_o.c
+++ b/libavformat/asfdec_o.c
@@ -672,6 +672,8 @@ static int asf_read_stream_properties(AVFormatContext *s, 
const GUIDParseTable *
                 asf_st->virtual_chunk_len = avio_rl16(pb);
                 if (!asf_st->virtual_chunk_len || !asf_st->virtual_pkt_len)
                     return AVERROR_INVALIDDATA;
+                if (asf_st->virtual_pkt_len % asf_st->virtual_chunk_len)
+                    asf_st->span = 1;
                 avio_skip(pb, err_data_len - 5);
             } else
                 avio_skip(pb, err_data_len - 1);
@@ -1279,17 +1281,9 @@ static int asf_deinterleave(AVFormatContext *s, 
ASFPacket *asf_pkt, int st_num)
         return AVERROR(ENOMEM);
     memset(data + asf_pkt->data_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
 
-    while (asf_pkt->data_size >= asf_st->span * pkt_len + pos) {
-        if (pos >= asf_pkt->data_size) {
-            break;
-        }
-        for (l = 0; l < pkt_len; l++) {
-            if (pos >= asf_pkt->data_size) {
-                break;
-            }
+    while (asf_pkt->data_size - pos >= asf_st->span * pkt_len) {
+        for (l = 0; l < nchunks; l++) {
             for (j = 0; j < asf_st->span; j++) {
-                if ((pos + chunk_len) >= asf_pkt->data_size)
-                    break;
                 memcpy(data + pos,
                        p + (j * nchunks + l) * chunk_len,
                        chunk_len);
@@ -1297,9 +1291,8 @@ static int asf_deinterleave(AVFormatContext *s, ASFPacket 
*asf_pkt, int st_num)
             }
         }
         p += asf_st->span * pkt_len;
-        if (p > asf_pkt->avpkt->data + asf_pkt->data_size)
-            break;
     }
+    memcpy(data + pos, p, asf_pkt->data_size - pos);
     av_packet_unref(asf_pkt->avpkt);
     ret = av_packet_from_data(asf_pkt->avpkt, data, asf_pkt->data_size);
     if (ret < 0)
-- 
2.52.0


>From 895042b9f1444ac1e97e9a8df71bb170058e7b94 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 18:05:42 +0200
Subject: [PATCH 39/41] avcodec/txd: reject unsupported d3d formats before
 allocating the frame

Timeout Replicated through Modified FFmpeg (fuzzer harness built with 
--disable-asm)

Fixes: Timeout
Fixes: 
505670915/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_TXD_fuzzer-5106192354705408
Fixes: qiaJQYzDp7vx

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/txd.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libavcodec/txd.c b/libavcodec/txd.c
index 24a6b6b5d3..2f175fc696 100644
--- a/libavcodec/txd.c
+++ b/libavcodec/txd.c
@@ -81,9 +81,14 @@ static int txd_decode_frame(AVCodecContext *avctx, AVFrame 
*p,
         case TXD_DXT3:
             if (bytestream2_get_bytes_left(&gb) < AV_CEIL_RSHIFT(w, 2) * 
AV_CEIL_RSHIFT(h, 2) * 16 + 4)
                 return AVERROR_INVALIDDATA;
+            break;
+        default:
+            goto unsupported;
         }
     } else if (depth == 32) {
         avctx->pix_fmt = AV_PIX_FMT_RGBA;
+        if (d3d_format != 0x15 && d3d_format != 0x16)
+            goto unsupported;
         if (bytestream2_get_bytes_left(&gb) < h * w * 4)
             return AVERROR_INVALIDDATA;
     } else {
-- 
2.52.0


>From ace9c0299dafd4e6ce2cedc393268c8366b20f68 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 18:05:42 +0200
Subject: [PATCH 40/41] avcodec/elbg: keep the total error in 64 bits

Signed integer overflow Replicated through Modified FFmpeg (fuzzer harness 
built with --disable-asm) with UBSAN

Fixes: signed integer overflow: 2147483647 - -106589 cannot be represented in 
type 'int'
Fixes: 
472729757/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_CINEPAK_fuzzer-5712538546536448
Fixes: fwDAxlQNlnpn
Regression since: ddc176d0eed7bd140c134c2b0923b575b13570c0

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/elbg.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libavcodec/elbg.c b/libavcodec/elbg.c
index 84043af4fd..34d03d416e 100644
--- a/libavcodec/elbg.c
+++ b/libavcodec/elbg.c
@@ -45,7 +45,7 @@ typedef struct cell_s {
  * ELBG internal data
  */
 typedef struct ELBGContext {
-    int error;
+    int64_t error;
     int dim;
     int num_cb;
     int *codebook;
@@ -368,9 +368,9 @@ static void do_elbg(ELBGContext *restrict elbg, int 
*points, int numpoints,
     int *const size_part = elbg->size_part;
     int i, j, steps = 0;
     int best_idx = 0;
-    int last_error;
+    int64_t last_error;
 
-    elbg->error = INT_MAX;
+    elbg->error = INT64_MAX;
     elbg->points = points;
 
     do {
@@ -398,7 +398,7 @@ static void do_elbg(ELBGContext *restrict elbg, int 
*points, int numpoints,
                 }
             }
             elbg->nearest_cb[i] = best_idx;
-            elbg->error = (elbg->error >= INT_MAX - best_dist) ? INT_MAX : 
elbg->error + best_dist;
+            elbg->error += best_dist;
             elbg->utility[elbg->nearest_cb[i]] = 
(elbg->utility[elbg->nearest_cb[i]] >= INT_MAX - best_dist) ?
                                                   INT_MAX : 
elbg->utility[elbg->nearest_cb[i]] + best_dist;
             free_cells->index = i;
-- 
2.52.0


>From 3bca1fd734d693754e630e83cffd4882bfc8df8d Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 10 Oct 2026 17:57:35 +0200
Subject: [PATCH 41/41] avcodec/ratecontrol: keep the wanted bits inside the
 int64 range in both directions

Signed integer overflow Replicated through Modified FFmpeg (fuzzer harness 
built with --disable-asm) with UBSAN

Fixes: signed integer overflow: 167640 - -9223372036854775808 cannot be 
represented in type 'int64_t'
Fixes: 9.22337e+18 is outside the range of representable values of type 'long'
Fixes: 
565995849/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_SNOW_fuzzer-5651323392163840
Fixes: tYZSxpyV9Sjx
Regression since: 7147c3c911f4ebf5bd1c21444423d8f78c6541ea

Found-by: continuous fuzzing process 
https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/ratecontrol.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libavcodec/ratecontrol.c b/libavcodec/ratecontrol.c
index a3ada799b5..397a235821 100644
--- a/libavcodec/ratecontrol.c
+++ b/libavcodec/ratecontrol.c
@@ -971,14 +971,14 @@ float ff_rate_estimate_qscale(MPVMainEncContext *const m, 
int dry_run)
             wanted_bits_double = m->bit_rate * (double)picture_number / fps;
         else
             wanted_bits_double = m->bit_rate * (double)dts_pic->f->pts / fps;
-        if (wanted_bits_double > INT64_MAX) {
+        if (wanted_bits_double >= 0x1p63) {
             av_log(s->c.avctx, AV_LOG_WARNING, "Bits exceed 64bit range\n");
             wanted_bits = INT64_MAX;
         } else
-            wanted_bits = (int64_t)wanted_bits_double;
+            wanted_bits = FFMAX(wanted_bits_double, INT64_MIN);
     }
 
-    diff = m->total_bits - wanted_bits;
+    diff = (double)m->total_bits - wanted_bits;
     br_compensation = (a->bit_rate_tolerance - diff) / a->bit_rate_tolerance;
     if (br_compensation <= 0.0)
         br_compensation = 0.001;
-- 
2.52.0

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

Reply via email to