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);