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]
