Commit ac6d2d91 for libheif

commit ac6d2d91be17f730cba833e29791488392f65169
Author: d-polikhranidi <76728763+d-polikhranidi@users.noreply.github.com>
Date:   Wed Jul 1 07:54:14 2026 +0300

    Fix: parse 'meta' box with size 0 (extends to end of file)

    A top-level 'meta' box with size 0 -- which per ISO/IEC 14496-12 clause 4.2
    means the box extends to the end of the file (legal for the last box) -- was
    rejected outright ("Cannot read meta box with unspecified size"), a
    previously-marked TODO in file_layout.cc. Such files are read and rendered by
    libavif (Chromium's AVIF decoder), so libheif-based pipelines (ImageMagick,
    libvips, sharp) fail on input that browsers display fine.

    Resolve size == 0 to the file size via request_range() and parse normally,
    matching the end-of-file handling libheif already uses for other boxes. This
    is bounded by the file size, so there is no unbounded read.

    Adds a regression test (tests/file_layout.cc "meta box with size 0") and a
    498-byte fixture (tests/data/meta_size_zero.avif, from libavif's test corpus).

    Signed-off-by: d-polikhranidi <76728763+d-polikhranidi@users.noreply.github.com>

diff --git a/libheif/file_layout.cc b/libheif/file_layout.cc
index 042a4190..d66c20a6 100644
--- a/libheif/file_layout.cc
+++ b/libheif/file_layout.cc
@@ -128,20 +128,29 @@ Error FileLayout::read(const std::shared_ptr<StreamReader>& stream, const heif_s

     if (box_header.get_short_type() == fourcc("meta")) {
       const uint64_t meta_box_start = next_box_start;
-      if (box_header.get_box_size() == 0) {
-        // TODO: get file-size from stream and compute box size
-        return {heif_error_Invalid_input,
-                heif_suberror_No_meta_box,
-                "Cannot read meta box with unspecified size"};
+      uint64_t end_of_meta_box;
+      if (box_header.get_box_size() == BoxHeader::size_until_end_of_file) {
+        // A box size of 0 means the box extends to the end of the file
+        // (ISO/IEC 14496-12 clause 4.2), which is legal for the last box.
+        // Resolve it to the file size.
+        end_of_meta_box = m_stream_reader->request_range(meta_box_start,
+                                                         std::numeric_limits<uint64_t>::max());
+        m_max_length = end_of_meta_box;
+        if (end_of_meta_box <= meta_box_start) {
+          return {heif_error_Invalid_input,
+                  heif_suberror_No_meta_box,
+                  "Cannot read meta box with unspecified size"};
+        }
       }
-
-      uint64_t end_of_meta_box = box_header.get_box_size();
-      if (end_of_meta_box > std::numeric_limits<uint64_t>::max() - meta_box_start) {
-        return {heif_error_Invalid_input,
-                heif_suberror_No_meta_box,
-                "Cannot read meta box with invalid size"};
+      else {
+        end_of_meta_box = box_header.get_box_size();
+        if (end_of_meta_box > std::numeric_limits<uint64_t>::max() - meta_box_start) {
+          return {heif_error_Invalid_input,
+                  heif_suberror_No_meta_box,
+                  "Cannot read meta box with invalid size"};
+        }
+        end_of_meta_box += meta_box_start;
       }
-      end_of_meta_box += meta_box_start;
       if (m_max_length < end_of_meta_box) {
         m_max_length = m_stream_reader->request_range(meta_box_start, end_of_meta_box);
       }
diff --git a/tests/data/meta_size_zero.avif b/tests/data/meta_size_zero.avif
new file mode 100644
index 00000000..bf78bfd9
Binary files /dev/null and b/tests/data/meta_size_zero.avif differ
diff --git a/tests/file_layout.cc b/tests/file_layout.cc
index 4daf3322..39c48403 100644
--- a/tests/file_layout.cc
+++ b/tests/file_layout.cc
@@ -44,6 +44,20 @@ TEST_CASE("parse file layout") {
   Error err = file.read(reader, heif_get_global_security_limits());

   REQUIRE(err.error_code == heif_error_Ok);
+}
+
+
+TEST_CASE("meta box with size 0 (extends to end of file)") {
+  // The 'meta' box uses a box size of 0, which per ISO/IEC 14496-12 clause 4.2
+  // means it extends to the end of the file (legal for the last box). In this
+  // file 'meta' also appears after the media data rather than first. It must
+  // parse successfully (libavif / Chromium read and render this file).
+  auto istr = std::unique_ptr<std::istream>(new std::ifstream(tests_data_directory + "/meta_size_zero.avif", std::ios::binary));
+  auto reader = std::make_shared<StreamReader_istream>(std::move(istr));

-  // TODO: read file where 'meta' box is not the first one after 'ftyp'
+  FileLayout file;
+  Error err = file.read(reader, heif_get_global_security_limits());
+
+  REQUIRE(err.error_code == heif_error_Ok);
+  REQUIRE(file.get_meta_box() != nullptr);
 }