Commit 5f67d12a for libheif

commit 5f67d12aa51312217ade3100745010339c4f2fcc
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Thu May 28 15:11:40 2026 +0200

    detect integer overflow during Chunk construction

diff --git a/libheif/sequences/chunk.cc b/libheif/sequences/chunk.cc
index 967a1c78..48d3c05e 100644
--- a/libheif/sequences/chunk.cc
+++ b/libheif/sequences/chunk.cc
@@ -72,9 +72,28 @@ Chunk::Chunk(HeifContext* ctx, uint32_t track_id, heif_compression_format format
 }


+std::shared_ptr<Chunk> Chunk::create(HeifContext* ctx, uint32_t track_id,
+                                     uint32_t first_sample, uint32_t num_samples,
+                                     uint64_t file_offset,
+                                     const std::shared_ptr<const Box_stsz>& stsz)
+{
+  bool success;
+  auto chunk = std::shared_ptr<Chunk>(new Chunk(ctx, track_id, first_sample,
+                                                num_samples, file_offset, stsz,
+                                                success));
+  if (!success) {
+    return nullptr;
+  }
+  return chunk;
+}
+
+
 Chunk::Chunk(HeifContext* ctx, uint32_t track_id,
-             uint32_t first_sample, uint32_t num_samples, uint64_t file_offset, const std::shared_ptr<const Box_stsz>& stsz)
+             uint32_t first_sample, uint32_t num_samples, uint64_t file_offset,
+             const std::shared_ptr<const Box_stsz>& stsz, bool& success)
 {
+  success = false;
+
   m_ctx = ctx;
   m_track_id = track_id;

@@ -94,10 +113,20 @@ Chunk::Chunk(HeifContext* ctx, uint32_t track_id,
       range.size = stsz->get_sample_sizes()[first_sample + i];
     }

+    // Detect uint64_t wrap when advancing the running offset. Cumulative chunk
+    // size can exceed UINT64_MAX with a malformed stsz (uint32_t sample size ×
+    // uint32_t sample count) starting from a large co64 chunk offset. Stop
+    // building the chunk; create() will discard the partially-built object.
+    if (file_offset > UINT64_MAX - range.size) {
+      return;
+    }
+
     m_sample_ranges.push_back(range);

     file_offset += range.size;
   }
+
+  success = true;
 }


diff --git a/libheif/sequences/chunk.h b/libheif/sequences/chunk.h
index 510650ef..66f472f2 100644
--- a/libheif/sequences/chunk.h
+++ b/libheif/sequences/chunk.h
@@ -36,8 +36,12 @@ class Chunk
 public:
   Chunk(HeifContext* ctx, uint32_t track_id, heif_compression_format format);

-  Chunk(HeifContext* ctx, uint32_t track_id,
-        uint32_t first_sample, uint32_t num_samples, uint64_t file_offset, const std::shared_ptr<const Box_stsz>& sample_sizes);
+  // Returns nullptr if the chunk cannot be constructed validly (e.g. the
+  // running file offset would wrap uint64_t).
+  static std::shared_ptr<Chunk> create(HeifContext* ctx, uint32_t track_id,
+                                       uint32_t first_sample, uint32_t num_samples,
+                                       uint64_t file_offset,
+                                       const std::shared_ptr<const Box_stsz>& sample_sizes);

   virtual ~Chunk() = default;

@@ -56,6 +60,13 @@ public:
   void set_decoder(std::shared_ptr<class Decoder> dec) { m_decoder = dec; }

 private:
+  // Sets `success` to false if the chunk cannot be constructed validly
+  // (e.g. the running file offset would wrap uint64_t). Otherwise leaves it
+  // unchanged.
+  Chunk(HeifContext* ctx, uint32_t track_id,
+        uint32_t first_sample, uint32_t num_samples, uint64_t file_offset,
+        const std::shared_ptr<const Box_stsz>& sample_sizes, bool& success);
+
   HeifContext* m_ctx = nullptr;
   uint32_t m_track_id = 0;

diff --git a/libheif/sequences/track.cc b/libheif/sequences/track.cc
index ea5034fd..9a7aabc1 100644
--- a/libheif/sequences/track.cc
+++ b/libheif/sequences/track.cc
@@ -400,10 +400,18 @@ Error Track::load(const std::shared_ptr<Box_trak>& trak_box)
       };
     }

-    auto chunk = std::make_shared<Chunk>(m_heif_context, m_id,
-                                         current_sample_idx, sampleToChunk.samples_per_chunk,
-                                         m_stco->get_offsets()[chunk_idx],
-                                         m_stsz);
+    auto chunk = Chunk::create(m_heif_context, m_id,
+                               current_sample_idx, sampleToChunk.samples_per_chunk,
+                               m_stco->get_offsets()[chunk_idx],
+                               m_stsz);
+
+    if (!chunk) {
+      return {
+        heif_error_Invalid_input,
+        heif_suberror_Unspecified,
+        "Chunk file offset overflows 64-bit range."
+      };
+    }

     if (auto visualSampleDescription = std::dynamic_pointer_cast<const Box_VisualSampleEntry>(sample_description)) {
       if (chunk_idx > 0 && (int32_t) sampleToChunk.sample_description_index == previous_sample_description_index) {