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

The HEIF ICC profile path in `mov_read_iprp_atom` / its `colr/prof`
handler does not bound the number of ICC copies performed per
input file. The patch adds a `heif_icc_profile_items` counter to
MOVContext (in libavformat/isom.h) and compares it to
`c->fc->max_streams` (defaulting to 1000 when unset). On exceed,
the patch logs a warning and skips further copies; it does not
return AVERROR_INVALIDDATA. This was a deliberate choice: HEIF
files with a few stray ICC profiles past the cap should not have
their entire demux fail; the surviving items still produce a
usable extraction.

Cap HEIF ICC profile copies at max_streams (default 1000). I chose
max_streams as the budget rather than a fresh constant because it
is the existing knob operators tune when ingesting HEIF with many
items, and it ties the bound to a parameter the caller already
controls. The patch consists of two hunks: one in libavformat/
isom.h adding `unsigned heif_icc_profile_items;` to MOVContext
(placed after the AVClass entry to preserve AVOption alignment),
and one in libavformat/mov.c adding the counter check + log line.

Found-by: Claude (Anthropic). Human-verified and reported by
Omkhar Arasaratnam <[email protected]>.
Signed-off-by: Omkhar Arasaratnam <[email protected]>


>From e698a6acb7b07cdd147b471ae6f04f6820d50c31 Mon Sep 17 00:00:00 2001
From: Omkhar Arasaratnam <[email protected]>
Date: Thu, 21 May 2026 00:00:00 +0000
Subject: [PATCH] avformat/mov: cap HEIF ICC profile copies via max_streams to
 bound CPU and memory

The HEIF ICC profile path in `mov_read_iprp_atom` / its `colr/prof`
handler does not bound the number of ICC copies performed per
input file. The patch adds a `heif_icc_profile_items` counter to
MOVContext (in libavformat/isom.h) and compares it to
`c->fc->max_streams` (defaulting to 1000 when unset). On exceed,
the patch logs a warning and skips further copies; it does not
return AVERROR_INVALIDDATA. This was a deliberate choice: HEIF
files with a few stray ICC profiles past the cap should not have
their entire demux fail; the surviving items still produce a
usable extraction.

Cap HEIF ICC profile copies at max_streams (default 1000). I chose
max_streams as the budget rather than a fresh constant because it
is the existing knob operators tune when ingesting HEIF with many
items, and it ties the bound to a parameter the caller already
controls. The patch consists of two hunks: one in libavformat/
isom.h adding `unsigned heif_icc_profile_items;` to MOVContext
(placed after the AVClass entry to preserve AVOption alignment),
and one in libavformat/mov.c adding the counter check + log line.

Found-by: Claude (Anthropic). Human-verified and reported by
Omkhar Arasaratnam <[email protected]>.
Signed-off-by: Omkhar Arasaratnam <[email protected]>
---
 libavformat/isom.h |  1 +
 libavformat/mov.c  | 12 ++++++++++++
 2 files changed, 13 insertions(+)

diff --git a/libavformat/isom.h b/libavformat/isom.h
index 9b5437fb16..971ea8565c 100644
--- a/libavformat/isom.h
+++ b/libavformat/isom.h
@@ -325,6 +325,7 @@ typedef struct HEIFGrid {
 
 typedef struct MOVContext {
     const AVClass *class; ///< class for private options
+    unsigned heif_icc_profile_items; ///< F0027: per-file HEIF ICC profile 
copy counter bounded by max_streams
     AVFormatContext *fc;
     int time_scale;
     int64_t duration;     ///< duration of the longest track
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 1cda7a1d05..d74f0f5638 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -2143,6 +2143,17 @@ static int mov_read_colr(MOVContext *c, AVIOContext *pb, 
MOVAtom atom)
                 return AVERROR(ENOMEM);
             icc_profile = sd->data;
         } else {
+            /* Bound the aggregate ICC profile allocations: an ipma map can
+             * associate one ipco prof property with many no-stream HEIF
+             * items. Without a cap, a small input drives N * profile_size
+             * allocations. Use max_streams as the budget (default 1000). */
+            unsigned icc_cap = c->fc->max_streams ? c->fc->max_streams : 1000;
+            if (c->heif_icc_profile_items >= icc_cap) {
+                av_log(c->fc, AV_LOG_WARNING,
+                       "HEIF ICC profile copies exceed cap %u; ignoring 
further items\n",
+                       icc_cap);
+                return 0;
+            }
             av_freep(&item->icc_profile);
             icc_profile = item->icc_profile = av_malloc(atom.size - 4);
             if (!icc_profile) {
@@ -2150,6 +2161,7 @@ static int mov_read_colr(MOVContext *c, AVIOContext *pb, 
MOVAtom atom)
                 return AVERROR(ENOMEM);
             }
             item->icc_profile_size = atom.size - 4;
+            c->heif_icc_profile_items++;
         }
         ret = ffio_read_size(pb, icc_profile, atom.size - 4);
         if (ret < 0)
-- 
2.52.0

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

Reply via email to