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]

Reply via email to