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