Commit b8065513 for libheif
commit b806551305b982302cc48e04601d747b63d9c8aa
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Mon Sep 21 00:45:07 2026 +0200
Allow an item to be referenced repeatedly within one 'iref' entry
Box_iref rejected every reference entry that lists the same to_item_ID
twice ("'iref' has double references"), both when parsing and when
writing. Neither ISO/IEC 14496-12 nor ISO/IEC 23008-12 forbids this, and
derived images rely on it: an 'iovl' that places the same input image at
two positions references it twice in its 'dimg' entry, one offset per
reference. The HEIF conformance file C021 does exactly that and could not
be opened. The check also prevented heif_context_add_overlay_image() from
writing such an overlay.
The check was added in 55dfd3da as a fuzzing sanity check with inverted
logic; f9906dd2 (#983) corrected the inversion instead of questioning the
rule, so it has rejected valid files since v1.17.0. All consumers of the
reference lists iterate them positionally, and the decode traversal state
is copied per path, so a repeated input is handled correctly without it.
diff --git a/libheif/box.cc b/libheif/box.cc
index 504e26e6..d096afe9 100644
--- a/libheif/box.cc
+++ b/libheif/box.cc
@@ -4045,11 +4045,10 @@ Error Box_iref::parse(BitstreamRange& range, const heif_security_limits* limits)
}
- // --- check for duplicate references
-
- if (auto error = check_for_double_references()) {
- return error;
- }
+ // Note: the same item may be listed several times within one reference entry.
+ // ISO/IEC 14496-12 does not forbid it, and derived images rely on it: an 'iovl'
+ // that places the same input image at two positions references it twice in its
+ // 'dimg' entry (one offset per reference). Conformance file C021 does this.
#if 0
@@ -4148,26 +4147,6 @@ Error Box_iref::parse(BitstreamRange& range, const heif_security_limits* limits)
}
-Error Box_iref::check_for_double_references() const
-{
- for (const auto& ref : m_references) {
- std::set<heif_item_id> to_ids;
- for (const auto to_id : ref.to_item_ID) {
- if (to_ids.find(to_id) == to_ids.end()) {
- to_ids.insert(to_id);
- }
- else {
- return {heif_error_Invalid_input,
- heif_suberror_Unspecified,
- "'iref' has double references"};
- }
- }
- }
-
- return Error::Ok;
-}
-
-
void Box_iref::derive_box_version()
{
uint8_t version = 0;
@@ -4192,10 +4171,6 @@ void Box_iref::derive_box_version()
Error Box_iref::write(StreamWriter& writer) const
{
- if (auto error = check_for_double_references()) {
- return error;
- }
-
size_t box_start = reserve_box_header_space(writer);
int id_size = ((get_version() == 0) ? 2 : 4);
diff --git a/libheif/box.h b/libheif/box.h
index 424aebd1..a686fd7c 100644
--- a/libheif/box.h
+++ b/libheif/box.h
@@ -1105,8 +1105,6 @@ protected:
void derive_box_version() override;
- Error check_for_double_references() const;
-
private:
std::vector<Reference> m_references;
diff --git a/tests/overlay_amplification.cc b/tests/overlay_amplification.cc
index 52af7187..f56e6a8a 100644
--- a/tests/overlay_amplification.cc
+++ b/tests/overlay_amplification.cc
@@ -202,8 +202,9 @@ std::vector<uint8_t> build_file(const std::vector<Item>& items, uint16_t primary
// Build the exponential amplification gadget: a chain of `depth` overlays where
// each overlay composites the next one twice, through two distinct 'iden' items
-// (a direct double reference would be rejected by Box_iref). Without the fix,
-// decoding the primary overlay decodes the base 2^depth times.
+// (listing the same item twice in one 'dimg' entry is legal too, but the two
+// items keep the gadget independent of that). Without the fix, decoding the
+// primary overlay decodes the base 2^depth times.
//
// Item layout: id 1 = base ('mski'); then per level k = 0..depth-1:
// iovl_k = 2 + 3*k