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]
