Commit 38d73faa for libheif

commit 38d73faaeff4d77366af8b67c3781ec44329d67a
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Thu May 28 14:58:18 2026 +0200

    add MemoryHandle::alloc() overload with int overflow checking

diff --git a/libheif/codecs/uncompressed/unc_boxes.cc b/libheif/codecs/uncompressed/unc_boxes.cc
index 4e938104..714a9348 100644
--- a/libheif/codecs/uncompressed/unc_boxes.cc
+++ b/libheif/codecs/uncompressed/unc_boxes.cc
@@ -1345,14 +1345,7 @@ Error Box_snuc::parse(BitstreamRange& range, const heif_security_limits* limits)
             "snuc image dimensions exceed security limit."};
   }

-  // Prevent size_t overflow when computing alloc size (matters on 32-bit systems)
-  if (std::numeric_limits<size_t>::max() / num_pixels < 2 * sizeof(float)) {
-    return {heif_error_Invalid_input,
-            heif_suberror_Security_limit_exceeded,
-            "snuc image memory size exceeds max integer size."};
-  }
-
-  Error err = m_memory_handle.alloc(2 * sizeof(float) * num_pixels, limits, "snuc box");
+  Error err = m_memory_handle.alloc(num_pixels, 2 * sizeof(float), limits, "snuc box");
   if (err) {
     return err;
   }
diff --git a/libheif/region.cc b/libheif/region.cc
index b318c3d7..8db2dbce 100644
--- a/libheif/region.cc
+++ b/libheif/region.cc
@@ -358,7 +358,7 @@ Error RegionGeometry_Polygon::parse(const std::vector<uint8_t>& data,
     };
   }

-  if (auto err = m_memory_handle.alloc(numPoints * sizeof(Point), limits, "region polygon")) {
+  if (auto err = m_memory_handle.alloc(numPoints, sizeof(Point), limits, "region polygon")) {
     return err;
   }

diff --git a/libheif/security_limits.cc b/libheif/security_limits.cc
index 1a85b3ed..4809306f 100644
--- a/libheif/security_limits.cc
+++ b/libheif/security_limits.cc
@@ -196,6 +196,27 @@ size_t TotalMemoryTracker::get_max_total_memory_used() const
 }


+Error MemoryHandle::alloc(size_t count, size_t element_size,
+                          const heif_security_limits* limits_context,
+                          const char* reason_description)
+{
+  if (element_size != 0 && count > SIZE_MAX / element_size) {
+    std::stringstream sstr;
+    if (reason_description) {
+      sstr << "Allocation size overflow computing " << count << " * " << element_size
+           << " for " << reason_description;
+    }
+    else {
+      sstr << "Allocation size overflow computing " << count << " * " << element_size;
+    }
+    return {heif_error_Memory_allocation_error,
+            heif_suberror_Security_limit_exceeded,
+            sstr.str()};
+  }
+  return alloc(count * element_size, limits_context, reason_description);
+}
+
+
 Error MemoryHandle::alloc(size_t memory_amount, const heif_security_limits* limits_context,
                           const char* reason_description)
 {
diff --git a/libheif/security_limits.h b/libheif/security_limits.h
index a8abb4d2..2db08e83 100644
--- a/libheif/security_limits.h
+++ b/libheif/security_limits.h
@@ -86,6 +86,12 @@ public:

   Error alloc(size_t memory_amount, const heif_security_limits* limits_context, const char* reason_description);

+  // calloc-style overload: checks `count * element_size` for size_t overflow before allocating.
+  // Use this when allocating an array whose total size is count*element_size, to avoid silent
+  // truncation on 32-bit builds when count is near UINT32_MAX.
+  Error alloc(size_t count, size_t element_size,
+              const heif_security_limits* limits_context, const char* reason_description);
+
   void free();

   void free(size_t memory_amount);
diff --git a/libheif/sequences/seq_boxes.cc b/libheif/sequences/seq_boxes.cc
index 54eaca5a..69900856 100644
--- a/libheif/sequences/seq_boxes.cc
+++ b/libheif/sequences/seq_boxes.cc
@@ -561,7 +561,7 @@ Error Box_stts::parse(BitstreamRange& range, const heif_security_limits* limits)
     };
   }

-  if (auto err = m_memory_handle.alloc(entry_count * sizeof(TimeToSample),
+  if (auto err = m_memory_handle.alloc(entry_count, sizeof(TimeToSample),
                                        limits, "the 'stts' table")) {
     return err;
   }
@@ -693,7 +693,7 @@ Error Box_ctts::parse(BitstreamRange& range, const heif_security_limits* limits)
     };
   }

-  if (auto err = m_memory_handle.alloc(entry_count * sizeof(OffsetToSample),
+  if (auto err = m_memory_handle.alloc(entry_count, sizeof(OffsetToSample),
                                        limits, "the 'ctts' table")) {
     return err;
   }
@@ -871,7 +871,7 @@ Error Box_stsc::parse(BitstreamRange& range, const heif_security_limits* limits)
   }


-  if (auto err = m_memory_handle.alloc(entry_count * sizeof(SampleToChunk),
+  if (auto err = m_memory_handle.alloc(entry_count, sizeof(SampleToChunk),
                                        limits, "the 'stsc' table")) {
     return err;
   }
@@ -1086,8 +1086,8 @@ Error Box_stsz::parse(BitstreamRange& range, const heif_security_limits* limits)
   if (m_fixed_sample_size == 0) {
     // check required memory

-    uint64_t mem_size = m_sample_count * sizeof(uint32_t);
-    if (auto err = m_memory_handle.alloc(mem_size, limits, "the 'stsz' table")) {
+    if (auto err = m_memory_handle.alloc(m_sample_count, sizeof(uint32_t),
+                                         limits, "the 'stsz' table")) {
       return err;
     }

@@ -1194,8 +1194,8 @@ Error Box_stss::parse(BitstreamRange& range, const heif_security_limits* limits)

   // check required memory

-  uint64_t mem_size = sample_count * sizeof(uint32_t);
-  if (auto err = m_memory_handle.alloc(mem_size, limits, "the 'stss' table")) {
+  if (auto err = m_memory_handle.alloc(sample_count, sizeof(uint32_t),
+                                       limits, "the 'stss' table")) {
     return err;
   }

@@ -1611,7 +1611,7 @@ Error Box_sbgp::parse(BitstreamRange& range, const heif_security_limits* limits)
   }

   uint32_t count = range.read32();
-  if (auto err = m_memory_handle.alloc(count * sizeof(Entry),
+  if (auto err = m_memory_handle.alloc(count, sizeof(Entry),
                                        limits, "the 'sample to group' table")) {
     return err;
   }
@@ -1750,7 +1750,7 @@ Error Box_sgpd::parse(BitstreamRange& range, const heif_security_limits* limits)

   }

-  if (auto err = m_memory_handle.alloc(static_cast<uint64_t>(entry_count) * sizeof(Entry),
+  if (auto err = m_memory_handle.alloc(entry_count, sizeof(Entry),
                                        limits, "the 'sgpd' table")) {
     return err;
   }
@@ -2131,10 +2131,8 @@ Error Box_saio::parse(BitstreamRange& range, const heif_security_limits* limits)
     };
   }

-  // check required memory
-  uint64_t mem_size = num_chunks * sizeof(uint64_t);
-
-  if (auto err = m_memory_handle.alloc(mem_size, limits, "the 'saio' table")) {
+  if (auto err = m_memory_handle.alloc(num_chunks, sizeof(uint64_t),
+                                       limits, "the 'saio' table")) {
     return err;
   }