PR #25408 opened by michaelni URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25408 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25408.patch
>From a719dd7bec147170c4310160a5d3c6a5da46611d Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Tue, 20 Oct 2026 03:04:00 +0200 Subject: [PATCH 1/5] avcodec/dovi_rpuenc: do not divide by a zero frame rate denominator Fixes: TPu5aVjgBh7t/testcase/crafted.hevc Fixes: TPu5aVjgBh7t Fixes: AISLE-2026-0150-00819 Fixes: division by zero Division by zero Replicated through UnModified FFmpeg with UBSAN Found-by: Joshua Rogers (AISLE) Signed-off-by: Michael Niedermayer <[email protected]> --- libavcodec/dovi_rpuenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavcodec/dovi_rpuenc.c b/libavcodec/dovi_rpuenc.c index 4bd87f496fe..61a7702d4fd 100644 --- a/libavcodec/dovi_rpuenc.c +++ b/libavcodec/dovi_rpuenc.c @@ -183,7 +183,7 @@ static av_cold int dovi_configure_ext(DOVIContext *s, enum AVCodecID codec_id, } pps = width * height; - if (framerate.num) { + if (framerate.num > 0 && framerate.den > 0) { pps = pps * framerate.num / framerate.den; } else { pps *= 25; /* sanity fallback */ -- 2.54.0 >From bcf74decd5451d315a195c9bff7bc0d8f10b6013 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Tue, 20 Oct 2026 03:04:57 +0200 Subject: [PATCH 2/5] avcodec/dovi_rpuenc: compress the DM metadata without previous extension blocks The dereference of dm by the same compression path was fixed by 971ea9d6a2. Fixes: LQ1pE3FHdz2l/testcase/crafted.hevc / gen.py Fixes: LQ1pE3FHdz2l Fixes: AISLE-2026-0150-00361 Fixes: NULL pointer dereference NULL pointer dereference Replicated through UnModified FFmpeg with ASAN Found-by: Joshua Rogers (AISLE) Signed-off-by: Michael Niedermayer <[email protected]> --- libavcodec/dovi_rpuenc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libavcodec/dovi_rpuenc.c b/libavcodec/dovi_rpuenc.c index 61a7702d4fd..f413df064fc 100644 --- a/libavcodec/dovi_rpuenc.c +++ b/libavcodec/dovi_rpuenc.c @@ -303,7 +303,7 @@ static int cmp_dm_level0(const AVDOVIColorMetadata *dm1, /* Tries to reuse the static ext blocks. May reorder `ext->dm_static` */ static int try_reuse_ext(DOVIExt *ext, const AVDOVIMetadata *metadata) { - int i, j, idx = 0; + int i, j, idx = 0, num_static = ext ? ext->num_static : 0; for (i = 0; i < metadata->num_ext_blocks; i++) { const AVDOVIDmData *dm = av_dovi_get_ext(metadata, i); @@ -311,7 +311,7 @@ static int try_reuse_ext(DOVIExt *ext, const AVDOVIMetadata *metadata) continue; /* Find the first matching ext block and move it to [idx] */ - for (j = idx; j < ext->num_static; j++) { + for (j = idx; j < num_static; j++) { if (!memcmp(&ext->dm_static[j], dm, sizeof(*dm))) { if (j != idx) FFSWAP(AVDOVIDmData, ext->dm_static[j], ext->dm_static[idx]); @@ -320,7 +320,7 @@ static int try_reuse_ext(DOVIExt *ext, const AVDOVIMetadata *metadata) } } - if (j == ext->num_static) { + if (j == num_static) { /* Found no matching ext block */ return 0; } @@ -328,7 +328,7 @@ static int try_reuse_ext(DOVIExt *ext, const AVDOVIMetadata *metadata) /* If idx is less than ext->num_static, then there are extra unmatched * ext blocks inside ext->dm_static */ - return idx == ext->num_static; + return idx == num_static; } static inline void put_ue_coef(PutBitContext *pb, const AVDOVIRpuDataHeader *hdr, -- 2.54.0 >From eb9d5fd51e31632885501463f0b9d6c460df4ffa Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Tue, 20 Oct 2026 03:16:27 +0200 Subject: [PATCH 3/5] avcodec/exif: bound the value bytes of an IFD tree by the size of the EXIF buffer AISLE-2026-0150-00875 describes the same overlapping entries amplified further by av_exif_ifd_to_dict(), which formats the values bounded here. Fixes: 7ReYNupbuA78/testcase/evil.tiff / gen.py Fixes: 7ReYNupbuA78 Fixes: AISLE-2026-0150-00031 Fixes: memory exhaustion Memory exhaustion Replicated through UnModified FFmpeg with ASAN Found-by: Joshua Rogers (AISLE) The report suggested a cumulative allocation budget per EXIF buffer. Signed-off-by: Michael Niedermayer <[email protected]> --- libavcodec/exif.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/libavcodec/exif.c b/libavcodec/exif.c index 515235a6aaa..63248d7186a 100644 --- a/libavcodec/exif.c +++ b/libavcodec/exif.c @@ -270,10 +270,14 @@ static inline void tput64(PutByteContext *pb, const int le, const uint64_t value le ? bytestream2_put_le64(pb, value) : bytestream2_put_be64(pb, value); } -static int exif_read_values(void *logctx, GetByteContext *gb, int le, AVExifEntry *entry) +static int exif_read_values(void *logctx, GetByteContext *gb, int le, + int *bytes_left, AVExifEntry *entry) { - if (exif_sizes[entry->type] * entry->count > bytestream2_get_bytes_left(gb)) + int payload_size = exif_sizes[entry->type] * entry->count; + + if (payload_size > bytestream2_get_bytes_left(gb) || payload_size > *bytes_left) return AVERROR_INVALIDDATA; + *bytes_left -= payload_size; switch (entry->type) { case AV_TIFF_SHORT: @@ -478,12 +482,12 @@ static int exif_get_makernote_offset(GetByteContext *gb) } static int exif_parse_ifd_list(void *logctx, GetByteContext *gb, int le, - int depth, int *entries_left, + int depth, int *entries_left, int *bytes_left, AVExifMetadata *ifd, int guess); static int exif_decode_tag(void *logctx, GetByteContext *gb, int le, - int depth, int *entries_left, int guess, - AVExifEntry *entry) + int depth, int *entries_left, int *bytes_left, + int guess, AVExifEntry *entry) { int ret = 0, makernote_offset = -1, tell, is_ifd, count; enum AVTiffDataType type; @@ -536,7 +540,7 @@ 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, entries_left, + ret = exif_parse_ifd_list(logctx, gb, le, depth + 1, entries_left, bytes_left, &entry->value.ifd, guess || entry->id == MAKERNOTE_TAG); if (ret < 0 && entry->id == MAKERNOTE_TAG) { /* @@ -554,7 +558,7 @@ static int exif_decode_tag(void *logctx, GetByteContext *gb, int le, entry->type = type == AV_TIFF_IFD ? AV_TIFF_UNDEFINED : type; entry->count = count; bytestream2_seek(gb, count * exif_sizes[type] > 4 ? payload : tell + 8, SEEK_SET); - ret = exif_read_values(logctx, gb, le, entry); + ret = exif_read_values(logctx, gb, le, &bytes_left[guess], entry); } end: @@ -564,7 +568,7 @@ end: } static int exif_parse_ifd_list(void *logctx, GetByteContext *gb, int le, - int depth, int *entries_left, + int depth, int *entries_left, int *bytes_left, AVExifMetadata *ifd, int guess) { uint32_t entries; @@ -629,7 +633,8 @@ 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, entries_left, guess, &ifd->entries[i]); + ret = exif_decode_tag(logctx, gb, le, depth, entries_left, bytes_left, + guess, &ifd->entries[i]); if (ret < 0) goto end; } @@ -946,7 +951,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; int entries_left[2] = { size / BASE_TAG_SIZE, size / BASE_TAG_SIZE }; - ret = exif_parse_ifd_list(logctx, &gbytes, le, 0, entries_left, ifd, 0); + int bytes_left[2] = { size, size }; + ret = exif_parse_ifd_list(logctx, &gbytes, le, 0, entries_left, bytes_left, ifd, 0); if (ret < 0) { av_log(logctx, AV_LOG_ERROR, "error decoding EXIF data: %s\n", av_err2str(ret)); return ret; @@ -966,7 +972,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, entries_left, &extra_ifd, 1); + ret = exif_parse_ifd_list(logctx, &gbytes, le, 0, entries_left, bytes_left, &extra_ifd, 1); if (ret < 0) { av_exif_free(&extra_ifd); break; -- 2.54.0 >From afe417a76e59cb8bd119ee2c6e8808e9cf26d3f9 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Tue, 20 Oct 2026 03:22:01 +0200 Subject: [PATCH 4/5] avcodec/bsf/lcevc_metadata: write the VUI into the NAL unit holding the global config Fixes: u1TxsBfg36kp/testcase/two_b.mp4 / gen_au.py gen_mp4.py Fixes: u1TxsBfg36kp Fixes: AISLE-2026-0150-00084 Fixes: assertion failure Assertion failure Replicated through UnModified FFmpeg Found-by: Joshua Rogers (AISLE) The report suggested returning the NAL unit together with the found block and inserting into its list, and replacing the assertion by a runtime check. Signed-off-by: Michael Niedermayer <[email protected]> --- libavcodec/bsf/lcevc_metadata.c | 5 +++-- libavcodec/cbs_lcevc.c | 16 ++++------------ libavcodec/cbs_lcevc.h | 13 +++++++------ libavcodec/lcevcdec.c | 2 +- 4 files changed, 15 insertions(+), 21 deletions(-) diff --git a/libavcodec/bsf/lcevc_metadata.c b/libavcodec/bsf/lcevc_metadata.c index 4ca4cd6b3d2..ac9f459d4a6 100644 --- a/libavcodec/bsf/lcevc_metadata.c +++ b/libavcodec/bsf/lcevc_metadata.c @@ -51,11 +51,12 @@ static int lcevc_metadata_handle_vui(AVBSFContext *bsf, LCEVCRawProcessBlock *block = NULL; LCEVCRawAdditionalInfo *ai = &ctx->ai; LCEVCRawVUI *vui = &ai->vui; + CodedBitstreamUnit *unit; int position, err; position = ff_cbs_lcevc_find_process_block(ctx->common.output, au, LCEVC_PAYLOAD_TYPE_GLOBAL_CONFIG, - &block); + &unit, &block); if (position < 0) return 0; @@ -100,7 +101,7 @@ static int lcevc_metadata_handle_vui(AVBSFContext *bsf, vui->chroma_sample_loc_type_bottom_field = ctx->chroma_sample_loc_type; } - err = ff_cbs_lcevc_add_process_block(ctx->common.output, au, position + 1, + err = ff_cbs_lcevc_add_process_block(ctx->common.output, unit, position + 1, LCEVC_PAYLOAD_TYPE_ADDITIONAL_INFO, ai, NULL); if (err < 0) diff --git a/libavcodec/cbs_lcevc.c b/libavcodec/cbs_lcevc.c index f13e717aa3c..673c2a56023 100644 --- a/libavcodec/cbs_lcevc.c +++ b/libavcodec/cbs_lcevc.c @@ -608,14 +608,13 @@ static int cbs_lcevc_get_process_block_list(CodedBitstreamContext *ctx, } int ff_cbs_lcevc_add_process_block(CodedBitstreamContext *ctx, - CodedBitstreamFragment *au, + CodedBitstreamUnit *unit, int position, uint32_t payload_type, void *payload_data, void *payload_ref) { const LCEVCProcessBlockTypeDescriptor *desc; - CodedBitstreamUnit *unit = NULL; LCEVCRawProcessBlockList *list; LCEVCRawProcessBlock *block; int err; @@ -624,16 +623,6 @@ int ff_cbs_lcevc_add_process_block(CodedBitstreamContext *ctx, if (!desc) return AVERROR(EINVAL); - for (int i = 0; i < au->nb_units; i++) { - if (au->units[i].type == LCEVC_NON_IDR_NUT || - au->units[i].type == LCEVC_IDR_NUT) { - unit = &au->units[i]; - break; - } - } - if (!unit) - return AVERROR(EINVAL); - // Find the block list inside the codec-dependent unit. err = cbs_lcevc_get_process_block_list(ctx, unit, &list); if (err < 0) @@ -662,6 +651,7 @@ int ff_cbs_lcevc_add_process_block(CodedBitstreamContext *ctx, int ff_cbs_lcevc_find_process_block(CodedBitstreamContext *ctx, CodedBitstreamFragment *au, uint32_t payload_type, + CodedBitstreamUnit **block_unit, LCEVCRawProcessBlock **iter) { int err, found; @@ -684,6 +674,8 @@ int ff_cbs_lcevc_find_process_block(CodedBitstreamContext *ctx, if (block->payload_type == payload_type) { if (!*iter || found) { *iter = block; + if (block_unit) + *block_unit = unit; return j; } if (block == *iter) diff --git a/libavcodec/cbs_lcevc.h b/libavcodec/cbs_lcevc.h index 371c7912e08..5197da1f02f 100644 --- a/libavcodec/cbs_lcevc.h +++ b/libavcodec/cbs_lcevc.h @@ -272,16 +272,15 @@ int ff_cbs_lcevc_list_add(LCEVCRawProcessBlockList *list, int position); void ff_cbs_lcevc_free_process_block_list(LCEVCRawProcessBlockList *list); /** - * Add a process block to an access unit. - * - * An existing NAL unit of type IDR or NON_IDR are required. + * Add a process block to a NAL unit of type IDR or NON_IDR, at the given + * position of its block list or at the end if position is -1. * * If set, payload_ref must be a RefStruct reference backing payload_data. * This function creates a new reference to payload_ref in this case. * If payload_ref is NULL, the new message will not be reference counted. */ int ff_cbs_lcevc_add_process_block(CodedBitstreamContext *ctx, - CodedBitstreamFragment *au, + CodedBitstreamUnit *unit, int position, uint32_t payload_type, void *payload_data, @@ -290,12 +289,14 @@ int ff_cbs_lcevc_add_process_block(CodedBitstreamContext *ctx, /** * Iterate over blocks with the given payload type in an access unit. * - * Set block to NULL in the first call. Returns 0 while more blocks - * are available, AVERROR(ENOENT) when all blocks have been found. + * Set block to NULL in the first call. Returns the position of the block in + * the block list of its NAL unit, which is stored in block_unit if that is + * not NULL, or AVERROR(ENOENT) when all blocks have been found. */ int ff_cbs_lcevc_find_process_block(CodedBitstreamContext *ctx, CodedBitstreamFragment *au, uint32_t payload_type, + CodedBitstreamUnit **block_unit, LCEVCRawProcessBlock **block); /** diff --git a/libavcodec/lcevcdec.c b/libavcodec/lcevcdec.c index b79401f98d7..705efd8e85d 100644 --- a/libavcodec/lcevcdec.c +++ b/libavcodec/lcevcdec.c @@ -504,7 +504,7 @@ int ff_lcevc_parse_frame(FFLCEVCContext *lcevc, const AVFrame *frame, } ret = ff_cbs_lcevc_find_process_block(lcevc->cbc, lcevc->frag, - LCEVC_PAYLOAD_TYPE_GLOBAL_CONFIG, &block); + LCEVC_PAYLOAD_TYPE_GLOBAL_CONFIG, NULL, &block); if (ret < 0) { ret = 0; goto end; -- 2.54.0 >From fc8d6b62011acfdcfc222b2363cac5a71ac84437 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Tue, 20 Oct 2026 03:23:26 +0200 Subject: [PATCH 5/5] avcodec/lcevcdec: check the enhanced picture size against max_pixels Fixes: W5cs7fsOfb24/testcase/lcevc_16000x16000.h264 / gen.py Fixes: W5cs7fsOfb24 Fixes: AISLE-2026-0150-00838 Fixes: max_pixels bypass max_pixels bypass Replicated through UnModified FFmpeg Found-by: Joshua Rogers (AISLE) Signed-off-by: Michael Niedermayer <[email protected]> --- libavcodec/decode.c | 4 ++-- libavcodec/lcevcdec.c | 5 +++-- libavcodec/lcevcdec.h | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/libavcodec/decode.c b/libavcodec/decode.c index 6584c5b5caf..5ff4f4e4179 100644 --- a/libavcodec/decode.c +++ b/libavcodec/decode.c @@ -1661,7 +1661,7 @@ int ff_decode_frame_props(AVCodecContext *avctx, AVFrame *frame) av_frame_get_side_data(frame, AV_FRAME_DATA_LCEVC); if (dc->lcevc.frame) { - ret = ff_lcevc_parse_frame(dc->lcevc.ctx, frame, &dc->lcevc.format, + ret = ff_lcevc_parse_frame(dc->lcevc.ctx, avctx, frame, &dc->lcevc.format, &dc->lcevc.width, &dc->lcevc.height); if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE)) return ret; @@ -1738,7 +1738,7 @@ int ff_attach_decode_data(AVCodecContext *avctx, AVFrame *frame) av_frame_get_side_data(frame, AV_FRAME_DATA_LCEVC); if (dc->lcevc.frame) { - int ret = ff_lcevc_parse_frame(dc->lcevc.ctx, frame, &dc->lcevc.format, + int ret = ff_lcevc_parse_frame(dc->lcevc.ctx, avctx, frame, &dc->lcevc.format, &dc->lcevc.width, &dc->lcevc.height); if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE)) return ret; diff --git a/libavcodec/lcevcdec.c b/libavcodec/lcevcdec.c index 705efd8e85d..6d20aa0e3f4 100644 --- a/libavcodec/lcevcdec.c +++ b/libavcodec/lcevcdec.c @@ -489,7 +489,8 @@ int ff_lcevc_process(void *logctx, AVFrame *frame) return 0; } -int ff_lcevc_parse_frame(FFLCEVCContext *lcevc, const AVFrame *frame, +int ff_lcevc_parse_frame(FFLCEVCContext *lcevc, AVCodecContext *avctx, + const AVFrame *frame, enum AVPixelFormat *format, int *width, int *height) { LCEVCRawProcessBlock *block = NULL; @@ -521,7 +522,7 @@ int ff_lcevc_parse_frame(FFLCEVCContext *lcevc, const AVFrame *frame, *height = gc->custom_resolution_height; } - ret = 0; + ret = av_image_check_size2(*width, *height, avctx->max_pixels, *format, 0, avctx); end: ff_cbs_fragment_reset(lcevc->frag); diff --git a/libavcodec/lcevcdec.h b/libavcodec/lcevcdec.h index b757acc3f9b..a3633f3c84c 100644 --- a/libavcodec/lcevcdec.h +++ b/libavcodec/lcevcdec.h @@ -65,7 +65,8 @@ typedef struct FFLCEVCFrame { int ff_lcevc_alloc(FFLCEVCContext **plcevc, AVDictionary **options, int loglevel); int ff_lcevc_process(void *logctx, struct AVFrame *frame); -int ff_lcevc_parse_frame(FFLCEVCContext *lcevc, const struct AVFrame *frame, +int ff_lcevc_parse_frame(FFLCEVCContext *lcevc, struct AVCodecContext *avctx, + const struct AVFrame *frame, enum AVPixelFormat *format, int *width, int *height); #endif /* AVCODEC_LCEVCDEC_H */ -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
