Commit b182ac87 for libheif

commit b182ac87d5f45a4c0098c26e0ea381b23425dd8a
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Sat Mar 28 00:10:07 2026 +0100

    check validity of number of tilC tiles against overflow and security limits

diff --git a/libheif/api/libheif/heif_context.cc b/libheif/api/libheif/heif_context.cc
index f0985061..b45595c2 100644
--- a/libheif/api/libheif/heif_context.cc
+++ b/libheif/api/libheif/heif_context.cc
@@ -270,7 +270,10 @@ heif_error heif_context_write(heif_context* ctx,
   }

   StreamWriter swriter;
-  ctx->context->write(swriter);
+  Error err = ctx->context->write(swriter);
+  if (err) {
+    return err.error_struct(ctx->context.get());
+  }

   const auto& data = swriter.get_data();
   heif_error writer_error = writer->write(ctx, data.data(), data.size(), userdata);
diff --git a/libheif/context.cc b/libheif/context.cc
index 943eb40f..8b7841c3 100644
--- a/libheif/context.cc
+++ b/libheif/context.cc
@@ -328,7 +328,7 @@ static uint64_t rescale(uint64_t duration, uint32_t old_base, uint32_t new_base)
 }


-void HeifContext::write(StreamWriter& writer)
+Error HeifContext::write(StreamWriter& writer)
 {
   // --- finalize some parameters

@@ -389,7 +389,9 @@ void HeifContext::write(StreamWriter& writer)
   for (auto& region : m_region_items) {
     std::vector<uint8_t> data_array;
     Error err = region->encode(data_array);
-    // TODO: err
+    if (err) {
+      return err;
+    }

     m_heif_file->append_iloc_data(region->item_id, data_array, 0);
   }
@@ -412,7 +414,10 @@ void HeifContext::write(StreamWriter& writer)
   // --- post-process images

   for (auto& img : m_all_images) {
-    img.second->process_before_write();
+    Error err = img.second->process_before_write();
+    if (err) {
+      return err;
+    }
   }

   // --- sort item properties
@@ -452,6 +457,8 @@ void HeifContext::write(StreamWriter& writer)
   // --- write to file

   m_heif_file->write(writer);
+
+  return {};
 }

 std::string HeifContext::debug_dump_boxes() const
diff --git a/libheif/context.h b/libheif/context.h
index de1bb859..0cc58034 100644
--- a/libheif/context.h
+++ b/libheif/context.h
@@ -136,7 +136,7 @@ public:

   // === writing ===

-  void write(StreamWriter& writer);
+  [[nodiscard]] Error write(StreamWriter& writer);

   // Create all boxes necessary for an empty HEIF file.
   // Note that this is no valid HEIF file, since some boxes (e.g. pitm) are generated, but
diff --git a/libheif/image-items/image_item.h b/libheif/image-items/image_item.h
index 8c0abe8e..389ff747 100644
--- a/libheif/image-items/image_item.h
+++ b/libheif/image-items/image_item.h
@@ -170,7 +170,7 @@ public:

   Error postprocess_coded_image_colorspace(heif_colorspace* inout_colorspace, heif_chroma* inout_chroma) const;

-  virtual void process_before_write() { }
+  virtual Error process_before_write() { return {}; }

   // -- thumbnails

diff --git a/libheif/image-items/tiled.cc b/libheif/image-items/tiled.cc
index 5eef9af8..4ae467ef 100644
--- a/libheif/image-items/tiled.cc
+++ b/libheif/image-items/tiled.cc
@@ -42,7 +42,7 @@ static uint64_t readvec(const std::vector<uint8_t>& data, size_t& ptr, int len)
 }


-uint64_t number_of_tiles(const heif_tiled_image_parameters& params)
+Result<uint64_t> number_of_tiles(const heif_tiled_image_parameters& params, const heif_security_limits* limits)
 {
   uint64_t nTiles = nTiles_h(params) * static_cast<uint64_t>(nTiles_v(params));

@@ -52,7 +52,32 @@ uint64_t number_of_tiles(const heif_tiled_image_parameters& params)
       break;
     }

+    if (params.extra_dimensions[i] != 0 &&
+        nTiles > UINT64_MAX / params.extra_dimensions[i]) {
+        return Error{
+          heif_error_Unsupported_filetype,
+          heif_suberror_Unspecified,
+          "Number of tiles exceeds uint64 maximum."
+        };
+    }
+
+    if (params.extra_dimensions[i] == 0) {
+      return Error{
+        heif_error_Unsupported_filetype,
+        heif_suberror_Unspecified,
+        "Zero extra dimension size."
+      };
+    }
+
     nTiles *= params.extra_dimensions[i];
+
+    if (limits && nTiles > limits->max_number_of_tiles) {
+      return Error{
+        heif_error_Unsupported_filetype,
+        heif_suberror_Security_limit_exceeded,
+        "Number of tiles exceeds security limit"
+      };
+    }
   }

   return nTiles;
@@ -305,15 +330,12 @@ Error TiledHeader::set_parameters(const heif_tiled_image_parameters& params)
 {
   m_parameters = params;

-  auto max_tiles = heif_get_global_security_limits()->max_number_of_tiles;
-
-  if (max_tiles && number_of_tiles(params) > max_tiles) {
-    return {heif_error_Unsupported_filetype,
-            heif_suberror_Security_limit_exceeded,
-            "Number of tiles exceeds security limit"};
+  Result<uint64_t> num_tiles_result = number_of_tiles(params, heif_get_global_security_limits());
+  if (auto err = num_tiles_result.error()) {
+    return err;
   }

-  m_offsets.resize(number_of_tiles(params));
+  m_offsets.resize(*num_tiles_result);

   for (auto& tile: m_offsets) {
     tile.offset = TILD_OFFSET_NOT_LOADED;
@@ -325,16 +347,12 @@ Error TiledHeader::set_parameters(const heif_tiled_image_parameters& params)

 Error TiledHeader::read_full_offset_table(const std::shared_ptr<HeifFile>& file, heif_item_id tild_id, const heif_security_limits* limits)
 {
-  auto max_tiles = heif_get_global_security_limits()->max_number_of_tiles;
-
-  uint64_t nTiles = number_of_tiles(m_parameters);
-  if (max_tiles && nTiles > max_tiles) {
-    return {heif_error_Invalid_input,
-            heif_suberror_Security_limit_exceeded,
-            "Number of tiles exceeds security limit."};
+  Result<uint64_t> nTiles_result = number_of_tiles(m_parameters, limits);
+  if (auto err = nTiles_result.error()) {
+    return err;
   }

-  return read_offset_table_range(file, tild_id, 0, nTiles);
+  return read_offset_table_range(file, tild_id, 0, *nTiles_result);
 }


@@ -440,12 +458,16 @@ void writevec(uint8_t* data, size_t& idx, I value, int len)
 }


-std::vector<uint8_t> TiledHeader::write_offset_table()
+Result<std::vector<uint8_t>> TiledHeader::write_offset_table()
 {
-  uint64_t nTiles = number_of_tiles(m_parameters);
+  Result<uint64_t> nTiles_result = number_of_tiles(m_parameters, nullptr);
+  if (auto err = nTiles_result.error()) {
+    return err;
+  }
+

   int offset_entry_size = (m_parameters.offset_field_length + m_parameters.size_field_length) / 8;
-  uint64_t size = nTiles * offset_entry_size;
+  uint64_t size = *nTiles_result * offset_entry_size;

   std::vector<uint8_t> data;
   data.resize(size);
@@ -642,14 +664,11 @@ ImageItem_Tiled::add_new_tiled_item(HeifContext* ctx, const heif_tiled_image_par
                                     const heif_encoder* encoder,
                                     const heif_encoding_options* encoding_options)
 {
-  auto max_tild_tiles = ctx->get_security_limits()->max_number_of_tiles;
-  if (max_tild_tiles && number_of_tiles(*parameters) > max_tild_tiles) {
-    return Error{heif_error_Usage_error,
-                 heif_suberror_Security_limit_exceeded,
-                 "Number of tiles exceeds security limit."};
+  Result<uint64_t> num_tiles_result = number_of_tiles(*parameters, ctx->get_security_limits());
+  if (auto err = num_tiles_result.error()) {
+    return err;
   }

-
   // Create 'tili' Item

   auto file = ctx->get_heif_file();
@@ -685,10 +704,13 @@ ImageItem_Tiled::add_new_tiled_item(HeifContext* ctx, const heif_tiled_image_par
   tild_header.set_parameters(*parameters);
   tild_header.set_compression_format(encoder->plugin->compression_format);

-  std::vector<uint8_t> header_data = tild_header.write_offset_table();
+  Result<std::vector<uint8_t>> header_data_result = tild_header.write_offset_table();
+  if (auto err = header_data_result.error()) {
+    return err;
+  }

   const int construction_method = 0; // 0=mdat 1=idat
-  file->append_iloc_data(tild_id, header_data, construction_method);
+  file->append_iloc_data(tild_id, *header_data_result, construction_method);


   if (parameters->image_width > 0xFFFFFFFF || parameters->image_height > 0xFFFFFFFF) {
@@ -711,7 +733,7 @@ ImageItem_Tiled::add_new_tiled_item(HeifContext* ctx, const heif_tiled_image_par
 #endif

   tild_image->set_tild_header(tild_header);
-  tild_image->set_next_tild_position(header_data.size());
+  tild_image->set_next_tild_position(header_data_result->size());

   // Set Brands
   //m_heif_file->set_brand(encoder->plugin->compression_format,
@@ -828,14 +850,19 @@ Error ImageItem_Tiled::add_image_tile(uint32_t tile_x, uint32_t tile_y,
 }


-void ImageItem_Tiled::process_before_write()
+Error ImageItem_Tiled::process_before_write()
 {
   // overwrite offsets

   const int construction_method = 0; // 0=mdat 1=idat

-  std::vector<uint8_t> header_data = m_tild_header.write_offset_table();
-  get_file()->replace_iloc_data(get_id(), 0, header_data, construction_method);
+  Result<std::vector<uint8_t>> header_data_result = m_tild_header.write_offset_table();
+  if (auto err = header_data_result.error()) {
+    return err;
+  }
+
+  get_file()->replace_iloc_data(get_id(), 0, *header_data_result, construction_method);
+  return {};
 }


diff --git a/libheif/image-items/tiled.h b/libheif/image-items/tiled.h
index 19ba8d20..6ced023a 100644
--- a/libheif/image-items/tiled.h
+++ b/libheif/image-items/tiled.h
@@ -33,7 +33,7 @@
 #include <set>


-uint64_t number_of_tiles(const heif_tiled_image_parameters& params);
+Result<uint64_t> number_of_tiles(const heif_tiled_image_parameters& params, const heif_security_limits* limits);

 uint32_t nTiles_h(const heif_tiled_image_parameters& params);

@@ -103,7 +103,7 @@ public:
   Error read_offset_table_range(const std::shared_ptr<HeifFile>& file, heif_item_id tild_id,
                                 uint64_t start, uint64_t end);

-  std::vector<uint8_t> write_offset_table();
+  Result<std::vector<uint8_t>> write_offset_table();

   std::string dump() const;

@@ -167,7 +167,7 @@ public:

   Error initialize_decoder() override;

-  void process_before_write() override;
+  Error process_before_write() override;

   Error get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const override;