new file mode 100644
@@ -0,0 +1,189 @@
+From 49e188ca7a6a81fd1c7d5e76254308c82cbcb5c3 Mon Sep 17 00:00:00 2001
+From: Dirk Farin <dirk.farin@gmail.com>
+Date: Thu, 25 Jun 2026 19:58:57 +0200
+Subject: [PATCH] Fix clap transform double-application in image tiling
+ (GHSA-jc8f-p23p-5hjg)
+
+The base ImageItem::get_heif_image_tiling() returned the already
+transformed m_width/m_height, but process_image_transformations_on_tiling()
+applies the transformative properties (irot, imir, clap) itself. This
+applied every transform twice. For a clap that rounds the image down to
+zero, the second application passed 0 into Box_clap::left_rounded(), where
+`image_width - 1U` underflowed to UINT32_MAX and overflowed the Fraction
+constructor (assert abort in debug builds, corrupt crop in release builds).
+The grid, unc and tiled overrides already return coded dimensions, so the
+base class was the lone outlier.
+
+Fixes, in three layers:
+
+ - image_item.cc: base get_heif_image_tiling() now reports coded (ispe)
+ dimensions when available, matching the other overrides, so transforms
+ are applied exactly once. This also fixes a silent irot/imir
+ double-transform on the same path.
+ - context.cc: reject a clap that rounds a dimension to zero or less at
+ parse time, mirroring the existing ispe zero-size check.
+ - box.cc: guard left_rounded()/top_rounded() against a zero image
+ dimension as defense in depth.
+
+Add tests/clap_zero_size.cc covering the hardened clap helpers.
+
+(cherry picked from commit f01870c1d7323a3003796d58eba7fff502be994c)
+
+CVE: CVE-2026-62289
+Upstream-Status: Backport [https://github.com/strukturag/libheif/commit/f01870c1d7323a3003796d58eba7fff502be994c]
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ libheif/box.cc | 11 ++++++++
+ libheif/context.cc | 12 +++++++--
+ libheif/image-items/image_item.cc | 23 ++++++++++++++---
+ tests/CMakeLists.txt | 1 +
+ tests/clap_zero_size.cc | 42 +++++++++++++++++++++++++++++++
+ 5 files changed, 83 insertions(+), 6 deletions(-)
+ create mode 100644 tests/clap_zero_size.cc
+
+diff --git a/libheif/box.cc b/libheif/box.cc
+index 76ba0f0a..57912ab0 100644
+--- a/libheif/box.cc
++++ b/libheif/box.cc
+@@ -3592,6 +3592,12 @@ int Box_clap::left_rounded(uint32_t image_width) const
+
+ // left = horizOff + (width-1)/2 - (clapWidth-1)/2
+
++ // Guard against image_width==0: `image_width - 1U` would underflow to
++ // UINT32_MAX and overflow the Fraction (GHSA-jc8f-p23p-5hjg).
++ if (image_width == 0) {
++ return 0;
++ }
++
+ Fraction pcX = m_horizontal_offset + Fraction(image_width - 1U, 2U);
+ Fraction left = pcX - (m_clean_aperture_width - 1) / 2;
+
+@@ -3607,6 +3613,11 @@ int Box_clap::right_rounded(uint32_t image_width) const
+
+ int Box_clap::top_rounded(uint32_t image_height) const
+ {
++ // Guard against image_height==0 underflowing the Fraction (see left_rounded).
++ if (image_height == 0) {
++ return 0;
++ }
++
+ Fraction pcY = m_vertical_offset + Fraction(image_height - 1U, 2U);
+ Fraction top = pcY - (m_clean_aperture_height - 1) / 2;
+
+diff --git a/libheif/context.cc b/libheif/context.cc
+index a1bcc268..a3371207 100644
+--- a/libheif/context.cc
++++ b/libheif/context.cc
+@@ -644,8 +644,16 @@ Error HeifContext::interpret_heif_file_images()
+ for (const auto& prop : properties) {
+ auto clap = std::dynamic_pointer_cast<Box_clap>(prop);
+ if (clap) {
+- image->set_resolution(clap->get_width_rounded(),
+- clap->get_height_rounded());
++ int clap_width = clap->get_width_rounded();
++ int clap_height = clap->get_height_rounded();
++ if (clap_width <= 0 || clap_height <= 0) {
++ return {heif_error_Invalid_input,
++ heif_suberror_Invalid_clean_aperture,
++ "Clean aperture (clap) reduces image to zero size"};
++ }
++
++ image->set_resolution(static_cast<uint32_t>(clap_width),
++ static_cast<uint32_t>(clap_height));
+
+ if (image->has_intrinsic_matrix()) {
+ image->get_intrinsic_matrix().apply_clap(clap.get(), image->get_width(), image->get_height());
+diff --git a/libheif/image-items/image_item.cc b/libheif/image-items/image_item.cc
+index e803107f..d05536e1 100644
+--- a/libheif/image-items/image_item.cc
++++ b/libheif/image-items/image_item.cc
+@@ -967,10 +967,25 @@ heif_image_tiling ImageItem::get_heif_image_tiling() const
+ tiling.num_columns = 1;
+ tiling.num_rows = 1;
+
+- tiling.tile_width = m_width;
+- tiling.tile_height = m_height;
+- tiling.image_width = m_width;
+- tiling.image_height = m_height;
++ // Report the coded (pre-transformation) dimensions here. The caller applies
++ // the transformative properties (irot, imir, clap) via
++ // process_image_transformations_on_tiling(), so handing it the already
++ // transformed m_width/m_height would apply them a second time. For a clap
++ // that shrinks the image to zero this double application underflowed inside
++ // Box_clap::left_rounded() (GHSA-jc8f-p23p-5hjg); for irot/imir it silently
++ // produced wrong dimensions. The grid/unc/tiled overrides likewise report
++ // coded dimensions.
++ uint32_t coded_width = m_width;
++ uint32_t coded_height = m_height;
++ if (has_ispe_resolution()) {
++ coded_width = get_ispe_width();
++ coded_height = get_ispe_height();
++ }
++
++ tiling.tile_width = coded_width;
++ tiling.tile_height = coded_height;
++ tiling.image_width = coded_width;
++ tiling.image_height = coded_height;
+
+ tiling.top_offset = 0;
+ tiling.left_offset = 0;
+diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
+index d8fdfd8b..b52bc202 100644
+--- a/tests/CMakeLists.txt
++++ b/tests/CMakeLists.txt
+@@ -38,6 +38,7 @@ if (WITH_REDUCED_VISIBILITY)
+ else()
+ add_libheif_test(bitstream_tests)
+ add_libheif_test(box_equals)
++ add_libheif_test(clap_zero_size)
+ add_libheif_test(conversion)
+ add_libheif_test(idat)
+ add_libheif_test(jpeg2000)
+diff --git a/tests/clap_zero_size.cc b/tests/clap_zero_size.cc
+new file mode 100644
+index 00000000..eafc1258
+--- /dev/null
++++ b/tests/clap_zero_size.cc
+@@ -0,0 +1,42 @@
++/*
++ libheif clean aperture (clap) zero-size unit tests
++
++ MIT License
++
++ Copyright (c) 2026 Dirk Farin <dirk.farin@gmail.com>
++
++ Permission is hereby granted, free of charge, to any person obtaining a copy
++ of this software and associated documentation files (the "Software"), to deal
++ in the Software without restriction, including without limitation the rights
++ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ copies of the Software, and to permit persons to whom the Software is
++ furnished to do so, subject to the following conditions:
++
++ The above copyright notice and this permission notice shall be included in all
++ copies or substantial portions of the Software.
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ SOFTWARE.
++*/
++
++#include "catch_amalgamated.hpp"
++#include "box.h"
++
++// Regression test for GHSA-jc8f-p23p-5hjg: passing a zero image dimension to
++// the clap rounding helpers used to underflow `image_width - 1U` to UINT32_MAX,
++// which overflowed the Fraction constructor (assert abort in debug builds,
++// corrupt crop in release builds). They must now return 0 without aborting.
++TEST_CASE("clap rounding with zero image size") {
++ std::shared_ptr<Box_clap> clap = std::make_shared<Box_clap>();
++ clap->set(100, 200, 150, 250); // clap 100x200 inside a 150x250 image
++
++ REQUIRE(clap->left_rounded(0) == 0);
++ REQUIRE(clap->right_rounded(0) == 99); // clapWidth - 1 + left(0)
++ REQUIRE(clap->top_rounded(0) == 0);
++ REQUIRE(clap->bottom_rounded(0) == 199); // clapHeight - 1 + top(0)
++}
@@ -14,6 +14,7 @@ SRC_URI = "git://github.com/strukturag/libheif.git;protocol=https;branch=master;
file://CVE-2026-32741.patch \
file://CVE-2026-41071-1.patch \
file://CVE-2026-41071-2.patch \
+ file://CVE-2026-62289.patch \
"
SRCREV = "62f1b8c76ed4d8305071fdacbe74ef9717bacac5"