Commit 5e8fcffc for libheif

commit 5e8fcffc16a3a2cf17584a2400f7386146f830f0
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Thu Jun 25 20:54:39 2026 +0200

    Fix heap OOB read in tili offset table parsing (GHSA-2h34-fcv6-jqvh)

    The experimental tiled-image (tili) decode path could read past the end
    of a heap buffer when parsing the per-tile offset/size table.

    Two complementary fixes:

    1. box.cc: Box_iloc::read_data discarded the Error returned by
       Box_idat::read_data for construction_method 1 (idat), then ran
       "size -= extent.length" unconditionally. By combining one valid and
       one out-of-range extent whose lengths sum to the requested size, the
       post-loop "limited_size && size > 0" shortfall guard was bypassed and
       read_data returned Ok with a short buffer. The idat error is now
       propagated, matching the construction_method 0 branch.

    2. tiled.cc: TiledHeader::read_offset_table_range trusted the returned
       buffer length and fed it to readvec(), which indexes data[ptr++]
       without bounds checks. Add a "data.size() < size_to_read" guard before
       the read loop as defense in depth.

    The bug is only reachable in builds with ENABLE_EXPERIMENTAL_FEATURES=ON
    and only through the per-tile decode API (heif_image_handle_decode_image_tile).
    Read-only (CWE-125): heap info disclosure and/or crash, no OOB write.

    Reported by Youngjin Kong.

diff --git a/libheif/box.cc b/libheif/box.cc
index 68e956e6..74ea8718 100644
--- a/libheif/box.cc
+++ b/libheif/box.cc
@@ -1920,10 +1920,13 @@ Error Box_iloc::read_data(heif_item_id item_id,
                 "idat box referenced in iref box is not present in file"};
       }

-      idat->read_data(istr,
-                      extent.offset + item->base_offset,
-                      extent.length,
-                      *dest, limits);
+      Error err = idat->read_data(istr,
+                                  extent.offset + item->base_offset,
+                                  extent.length,
+                                  *dest, limits);
+      if (err) {
+        return err;
+      }

       size -= extent.length;
     }
diff --git a/libheif/image-items/tiled.cc b/libheif/image-items/tiled.cc
index 946392ec..ec081fe3 100644
--- a/libheif/image-items/tiled.cc
+++ b/libheif/image-items/tiled.cc
@@ -397,6 +397,13 @@ Error TiledHeader::read_offset_table_range(const std::shared_ptr<HeifFile>& file
     return err;
   }

+  // Make sure we actually received as much data as we are about to parse. The
+  // returned buffer may be shorter than requested (e.g. truncated iloc/idat
+  // extents), and readvec() does not bounds-check its input.
+  if (data.size() < size_to_read) {
+    return eofError;
+  }
+
   size_t idx = 0;
   for (uint64_t i = start; i < end; i++) {
     m_offsets[i].offset = readvec(data, idx, m_parameters.offset_field_length / 8);