Commit 69a0ca1b for libheif
commit 69a0ca1bed228e5766c298db8ee86b5569bba71a
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Fri May 15 20:07:26 2026 +0200
pass a tightened heif_security_limits to the decoder plugins with a maximum image size just above the ispe size (#1798)
diff --git a/libheif/image-items/image_item.cc b/libheif/image-items/image_item.cc
index be374c40..2a8b0346 100644
--- a/libheif/image-items/image_item.cc
+++ b/libheif/image-items/image_item.cc
@@ -1142,8 +1142,15 @@ Result<std::shared_ptr<HeifPixelImage>> ImageItem::decode_compressed_image(const
decoder->set_data_extent(std::move(extent));
- return decoder->decode_single_frame_from_compressed_data(options,
- get_context()->get_security_limits());
+ // Tighten max_image_size_pixels for this decode so a decoder plugin (e.g.
+ // dav1d) cannot allocate buffers far larger than the ispe-declared size
+ // when the codec bitstream lies about its dimensions.
+ heif_security_limits tightened = tighten_image_size_limit_for_ispe(
+ get_context()->get_security_limits(),
+ get_width(), get_height(),
+ max_coding_unit_size_for_codec(get_compression_format()));
+
+ return decoder->decode_single_frame_from_compressed_data(options, &tightened);
}
diff --git a/libheif/image-items/tiled.cc b/libheif/image-items/tiled.cc
index eb04b06f..67768264 100644
--- a/libheif/image-items/tiled.cc
+++ b/libheif/image-items/tiled.cc
@@ -1011,8 +1011,13 @@ ImageItem_Tiled::decode_grid_tile(const heif_decoding_options& options, uint32_t
m_tile_decoder->set_data_extent(std::move(*extentResult));
- return m_tile_decoder->decode_single_frame_from_compressed_data(options,
- get_context()->get_security_limits());
+ uint32_t tw = 0, th = 0;
+ get_tile_size(tw, th);
+ heif_security_limits tightened = tighten_image_size_limit_for_ispe(
+ get_context()->get_security_limits(), tw, th,
+ max_coding_unit_size_for_codec(m_tile_decoder->get_compression_format()));
+
+ return m_tile_decoder->decode_single_frame_from_compressed_data(options, &tightened);
}
diff --git a/libheif/security_limits.cc b/libheif/security_limits.cc
index ed8241c6..c0074239 100644
--- a/libheif/security_limits.cc
+++ b/libheif/security_limits.cc
@@ -64,6 +64,42 @@ heif_security_limits disabled_security_limits{
};
+uint32_t max_coding_unit_size_for_codec(heif_compression_format format)
+{
+ switch (format) {
+ case heif_compression_AV1: return 128; // AV1 max superblock
+ case heif_compression_VVC: return 128; // VVC max CTU
+ case heif_compression_HEVC: return 64; // HEVC max CTU
+ case heif_compression_AVC: return 16; // H.264 macroblock
+ case heif_compression_JPEG: return 16; // JPEG MCU (4:2:0)
+ case heif_compression_JPEG2000: return 64;
+ case heif_compression_HTJ2K: return 64;
+ default: return 0;
+ }
+}
+
+
+heif_security_limits tighten_image_size_limit_for_ispe(const heif_security_limits* base,
+ uint32_t ispe_width,
+ uint32_t ispe_height,
+ uint32_t coding_unit_size)
+{
+ heif_security_limits result = *base;
+
+ if (ispe_width == 0 || ispe_height == 0) {
+ return result;
+ }
+
+ uint64_t allowed = (static_cast<uint64_t>(ispe_width) + coding_unit_size) *
+ (static_cast<uint64_t>(ispe_height) + coding_unit_size);
+
+ if (result.max_image_size_pixels == 0 || allowed < result.max_image_size_pixels) {
+ result.max_image_size_pixels = allowed;
+ }
+ return result;
+}
+
+
Error check_for_valid_image_size(const heif_security_limits* limits, uint32_t width, uint32_t height)
{
uint64_t maximum_image_size_limit = limits->max_image_size_pixels;
diff --git a/libheif/security_limits.h b/libheif/security_limits.h
index 6845008b..9daeb7e4 100644
--- a/libheif/security_limits.h
+++ b/libheif/security_limits.h
@@ -41,6 +41,26 @@ static const int MAX_FRACTION_VALUE = 0x10000;
Error check_for_valid_image_size(const heif_security_limits* limits, uint32_t width, uint32_t height);
+// Maximum coding-unit size (in pixels) that the given codec may pad a coded
+// frame up to. Used as the margin for tighten_image_size_limit_for_ispe.
+// Returns 0 for codecs without coding-unit padding (e.g. uncompressed).
+uint32_t max_coding_unit_size_for_codec(heif_compression_format format);
+
+// Return a copy of `base` with max_image_size_pixels lowered to a value
+// just above the declared image size. This is used to bound how much memory
+// a codec plugin may allocate for an image whose internal (codec-declared)
+// dimensions exceed the file-declared (ispe) dimensions — without us having
+// to parse the codec bitstream ourselves.
+//
+// `coding_unit_size` is the maximum coding-unit size of the target codec
+// (e.g. 128 for AV1/VVC, 64 for HEVC, 16 for AVC). The allowed coded
+// dimensions are (ispe + coding_unit_size) in each axis, since a codec may
+// pad the coded frame up to a coding-unit boundary.
+heif_security_limits tighten_image_size_limit_for_ispe(const heif_security_limits* base,
+ uint32_t ispe_width,
+ uint32_t ispe_height,
+ uint32_t coding_unit_size);
+
class TotalMemoryTracker
{