Commit 09ea4da5 for libheif
commit 09ea4da573985ccc1e47e5d5c7f9d19ae4f6844f
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Sun Dec 28 12:58:20 2025 +0100
check for cyclic item references during decoding instead of throwing errors during parsing
diff --git a/libheif/api/libheif/heif_decoding.cc b/libheif/api/libheif/heif_decoding.cc
index 4ac272ed..6f0b2cd3 100644
--- a/libheif/api/libheif/heif_decoding.cc
+++ b/libheif/api/libheif/heif_decoding.cc
@@ -246,7 +246,7 @@ heif_error heif_decode_image(const heif_image_handle* in_handle,
colorspace,
chroma,
dec_options,
- false, 0, 0);
+ false, 0, 0, {});
if (!decodingResult) {
return decodingResult.error_struct(in_handle->image.get());
diff --git a/libheif/api/libheif/heif_tiling.cc b/libheif/api/libheif/heif_tiling.cc
index d322dbc7..4b874e81 100644
--- a/libheif/api/libheif/heif_tiling.cc
+++ b/libheif/api/libheif/heif_tiling.cc
@@ -109,7 +109,8 @@ heif_error heif_image_handle_decode_image_tile(const heif_image_handle* in_handl
colorspace,
chroma,
*dec_options,
- true, x0, y0);
+ true, x0, y0,
+ {});
heif_decoding_options_free(dec_options);
if (!decodingResult) {
diff --git a/libheif/box.cc b/libheif/box.cc
index f4467f9d..ceafd974 100644
--- a/libheif/box.cc
+++ b/libheif/box.cc
@@ -3700,7 +3700,7 @@ Error Box_iref::parse(BitstreamRange& range, const heif_security_limits* limits)
}
-#if 1
+#if 0
// Note: This input sanity check first did not work as expected.
// Its idea was to prevent infinite recursions while decoding when the input file
// contains cyclic references. However, apparently there are cases where cyclic
@@ -3710,11 +3710,10 @@ Error Box_iref::parse(BitstreamRange& range, const heif_security_limits* limits)
// | reference with type 'auxl' from ID: 2 to IDs: 1
// | reference with type 'prem' from ID: 1 to IDs: 2
//
- // We now only follow 'dimg' references. This should be free from cyclic references.
- //
- // TODO: implement the infinite recursion detection in a different way. E.g. by passing down
- // the already processed item-ids while decoding an image and checking whether the current
- // item has already been decoded before.
+ // We now test for cyclic references during the image decoding.
+ // We pass down the item IDs that have already been seen during the decoding process.
+ // If we try to decode an image IDs that has already been seen previously, we throw an error.
+ // The advantage is that the error only occurs when we are trying to decode the faulty image.
// --- check for cyclic references
diff --git a/libheif/context.cc b/libheif/context.cc
index aa263887..a1bcc268 100644
--- a/libheif/context.cc
+++ b/libheif/context.cc
@@ -1283,7 +1283,8 @@ Result<std::shared_ptr<HeifPixelImage>> HeifContext::decode_image(heif_item_id I
heif_colorspace out_colorspace,
heif_chroma out_chroma,
const heif_decoding_options& options,
- bool decode_only_tile, uint32_t tx, uint32_t ty) const
+ bool decode_only_tile, uint32_t tx, uint32_t ty,
+ std::set<heif_item_id> processed_ids) const
{
std::shared_ptr<ImageItem> imgitem;
if (m_all_images.contains(ID)) {
@@ -1296,7 +1297,7 @@ Result<std::shared_ptr<HeifPixelImage>> HeifContext::decode_image(heif_item_id I
}
- auto decodingResult = imgitem->decode_image(options, decode_only_tile, tx, ty);
+ auto decodingResult = imgitem->decode_image(options, decode_only_tile, tx, ty, processed_ids);
if (!decodingResult) {
return decodingResult.error();
}
diff --git a/libheif/context.h b/libheif/context.h
index af1359cc..45554459 100644
--- a/libheif/context.h
+++ b/libheif/context.h
@@ -115,7 +115,8 @@ public:
heif_colorspace out_colorspace,
heif_chroma out_chroma,
const heif_decoding_options& options,
- bool decode_only_tile, uint32_t tx, uint32_t ty) const;
+ bool decode_only_tile, uint32_t tx, uint32_t ty,
+ std::set<heif_item_id> processed_ids) const;
Result<std::shared_ptr<HeifPixelImage>> convert_to_output_colorspace(std::shared_ptr<HeifPixelImage> img,
heif_colorspace out_colorspace,
diff --git a/libheif/image-items/grid.cc b/libheif/image-items/grid.cc
index a4251557..45a61d60 100644
--- a/libheif/image-items/grid.cc
+++ b/libheif/image-items/grid.cc
@@ -207,13 +207,23 @@ Error ImageItem_Grid::read_grid_spec()
Result<std::shared_ptr<HeifPixelImage>> ImageItem_Grid::decode_compressed_image(const heif_decoding_options& options,
- bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const
+ bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0,
+ std::set<heif_item_id> processed_ids) const
{
+ if (processed_ids.contains(get_id())) {
+ return Error{heif_error_Invalid_input,
+ heif_suberror_Unspecified,
+ "'iref' has cyclic references"};
+ }
+
+ processed_ids.insert(get_id());
+
+
if (decode_tile_only) {
- return decode_grid_tile(options, tile_x0, tile_y0);
+ return decode_grid_tile(options, tile_x0, tile_y0, processed_ids);
}
else {
- return decode_full_grid_image(options);
+ return decode_full_grid_image(options, processed_ids);
}
}
@@ -230,7 +240,7 @@ static void wait_for_jobs(std::deque<std::future<Error> >* jobs) {
}
#endif
-Result<std::shared_ptr<HeifPixelImage>> ImageItem_Grid::decode_full_grid_image(const heif_decoding_options& options) const
+Result<std::shared_ptr<HeifPixelImage>> ImageItem_Grid::decode_full_grid_image(const heif_decoding_options& options, std::set<heif_item_id> processed_ids) const
{
std::shared_ptr<HeifPixelImage> img; // the decoded image
@@ -369,7 +379,7 @@ Result<std::shared_ptr<HeifPixelImage>> ImageItem_Grid::decode_full_grid_image(c
}
}
- err = decode_and_paste_tile_image(tileID, x0, y0, img, options, progress_counter, warnings);
+ err = decode_and_paste_tile_image(tileID, x0, y0, img, options, progress_counter, warnings, processed_ids);
if (err) {
return err;
}
@@ -417,7 +427,7 @@ Result<std::shared_ptr<HeifPixelImage>> ImageItem_Grid::decode_full_grid_image(c
errs.push_back(std::async(std::launch::async,
&ImageItem_Grid::decode_and_paste_tile_image, this,
data.tileID, data.x_origin, data.y_origin, std::ref(img), options,
- std::ref(progress_counter), warnings));
+ std::ref(progress_counter), warnings, processed_ids));
}
// check for decoding errors in remaining tiles
@@ -464,7 +474,8 @@ Error ImageItem_Grid::decode_and_paste_tile_image(heif_item_id tileID, uint32_t
std::shared_ptr<HeifPixelImage>& inout_image,
const heif_decoding_options& options,
int& progress_counter,
- std::shared_ptr<std::vector<Error> > warnings) const
+ std::shared_ptr<std::vector<Error> > warnings,
+ std::set<heif_item_id> processed_ids) const
{
std::shared_ptr<HeifPixelImage> tile_img;
#if ENABLE_PARALLEL_TILE_DECODING
@@ -489,7 +500,7 @@ Error ImageItem_Grid::decode_and_paste_tile_image(heif_item_id tileID, uint32_t
return error;
}
- auto decodeResult = tileItem->decode_image(options, false, 0, 0);
+ auto decodeResult = tileItem->decode_image(options, false, 0, 0, processed_ids);
if (!decodeResult) {
if (!options.strict_decoding) {
// We ignore broken tiles.
@@ -556,7 +567,8 @@ Error ImageItem_Grid::decode_and_paste_tile_image(heif_item_id tileID, uint32_t
}
-Result<std::shared_ptr<HeifPixelImage>> ImageItem_Grid::decode_grid_tile(const heif_decoding_options& options, uint32_t tx, uint32_t ty) const
+Result<std::shared_ptr<HeifPixelImage>> ImageItem_Grid::decode_grid_tile(const heif_decoding_options& options, uint32_t tx, uint32_t ty,
+ std::set<heif_item_id> processed_ids) const
{
uint32_t idx = ty * m_grid_spec.get_columns() + tx;
@@ -568,7 +580,7 @@ Result<std::shared_ptr<HeifPixelImage>> ImageItem_Grid::decode_grid_tile(const h
return error;
}
- return tile_item->decode_compressed_image(options, true, tx, ty);
+ return tile_item->decode_compressed_image(options, true, tx, ty, processed_ids);
}
diff --git a/libheif/image-items/grid.h b/libheif/image-items/grid.h
index e6bcaa45..f82ce11a 100644
--- a/libheif/image-items/grid.h
+++ b/libheif/image-items/grid.h
@@ -130,7 +130,8 @@ public:
}
Result<std::shared_ptr<HeifPixelImage>> decode_compressed_image(const heif_decoding_options& options,
- bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const override;
+ bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0,
+ std::set<heif_item_id> processed_ids) const override;
heif_brand2 get_compatible_brand() const override;
@@ -161,14 +162,15 @@ private:
Error read_grid_spec();
- Result<std::shared_ptr<HeifPixelImage>> decode_full_grid_image(const heif_decoding_options& options) const;
+ Result<std::shared_ptr<HeifPixelImage>> decode_full_grid_image(const heif_decoding_options& options, std::set<heif_item_id> processed_ids) const;
- Result<std::shared_ptr<HeifPixelImage>> decode_grid_tile(const heif_decoding_options& options, uint32_t tx, uint32_t ty) const;
+ Result<std::shared_ptr<HeifPixelImage>> decode_grid_tile(const heif_decoding_options& options, uint32_t tx, uint32_t ty, std::set<heif_item_id> processed_ids) const;
Error decode_and_paste_tile_image(heif_item_id tileID, uint32_t x0, uint32_t y0,
std::shared_ptr<HeifPixelImage>& inout_image,
const heif_decoding_options& options, int& progress_counter,
- std::shared_ptr<std::vector<Error> > warnings) const;
+ std::shared_ptr<std::vector<Error> > warnings,
+ std::set<heif_item_id> processed_ids) const;
};
diff --git a/libheif/image-items/iden.cc b/libheif/image-items/iden.cc
index f11b8869..8e771cfa 100644
--- a/libheif/image-items/iden.cc
+++ b/libheif/image-items/iden.cc
@@ -36,8 +36,18 @@ ImageItem_iden::ImageItem_iden(HeifContext* ctx, heif_item_id id)
Result<std::shared_ptr<HeifPixelImage>> ImageItem_iden::decode_compressed_image(const heif_decoding_options& options,
- bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const
+ bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0,
+ std::set<heif_item_id> processed_ids) const
{
+ if (processed_ids.contains(get_id())) {
+ return Error{heif_error_Invalid_input,
+ heif_suberror_Unspecified,
+ "'iref' has cyclic references"};
+ }
+
+ processed_ids.insert(get_id());
+
+
std::shared_ptr<HeifPixelImage> img;
// find the ID of the image this image is derived from
@@ -52,12 +62,18 @@ Result<std::shared_ptr<HeifPixelImage>> ImageItem_iden::decode_compressed_image(
std::vector<heif_item_id> image_references = iref_box->get_references(get_id(), fourcc("dimg"));
- if ((int) image_references.size() != 1) {
+ if (image_references.size() > 1) {
return Error(heif_error_Invalid_input,
heif_suberror_Unspecified,
"'iden' image with more than one reference image");
}
+ if (image_references.empty()) {
+ return Error(heif_error_Invalid_input,
+ heif_suberror_Unspecified,
+ "'iden' image without 'dimg' reference");
+ }
+
heif_item_id reference_image_id = image_references[0];
@@ -77,7 +93,7 @@ Result<std::shared_ptr<HeifPixelImage>> ImageItem_iden::decode_compressed_image(
return error;
}
- return imgitem->decode_image(options, decode_tile_only, tile_x0, tile_y0);
+ return imgitem->decode_image(options, decode_tile_only, tile_x0, tile_y0, processed_ids);
}
diff --git a/libheif/image-items/iden.h b/libheif/image-items/iden.h
index ff91aa6c..dcde1862 100644
--- a/libheif/image-items/iden.h
+++ b/libheif/image-items/iden.h
@@ -59,7 +59,8 @@ public:
}
Result<std::shared_ptr<HeifPixelImage>> decode_compressed_image(const heif_decoding_options& options,
- bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const override;
+ bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0,
+ std::set<heif_item_id> processed_ids) const override;
heif_brand2 get_compatible_brand() const override;
diff --git a/libheif/image-items/image_item.cc b/libheif/image-items/image_item.cc
index 84eab178..dc16a374 100644
--- a/libheif/image-items/image_item.cc
+++ b/libheif/image-items/image_item.cc
@@ -679,7 +679,8 @@ void ImageItem::set_color_profile_icc(const std::shared_ptr<const color_profile_
Result<std::shared_ptr<HeifPixelImage>> ImageItem::decode_image(const heif_decoding_options& options,
- bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const
+ bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0,
+ std::set<heif_item_id> processed_ids) const
{
// --- check whether image size (according to 'ispe') exceeds maximum
@@ -704,7 +705,7 @@ Result<std::shared_ptr<HeifPixelImage>> ImageItem::decode_image(const heif_decod
// --- decode image
- Result<std::shared_ptr<HeifPixelImage>> decodingResult = decode_compressed_image(options, decode_tile_only, tile_x0, tile_y0);
+ Result<std::shared_ptr<HeifPixelImage>> decodingResult = decode_compressed_image(options, decode_tile_only, tile_x0, tile_y0, processed_ids);
if (!decodingResult) {
return decodingResult.error();
}
@@ -802,7 +803,7 @@ Result<std::shared_ptr<HeifPixelImage>> ImageItem::decode_image(const heif_decod
return alpha_image->get_item_error();
}
- auto alphaDecodingResult = alpha_image->decode_image(options, decode_tile_only, tile_x0, tile_y0);
+ auto alphaDecodingResult = alpha_image->decode_image(options, decode_tile_only, tile_x0, tile_y0, processed_ids);
if (!alphaDecodingResult) {
return alphaDecodingResult.error();
}
@@ -927,8 +928,18 @@ Result<std::vector<uint8_t>> ImageItem::read_bitstream_configuration_data_overri
#endif
Result<std::shared_ptr<HeifPixelImage>> ImageItem::decode_compressed_image(const heif_decoding_options& options,
- bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const
+ bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0,
+ std::set<heif_item_id> processed_ids) const
{
+ if (processed_ids.contains(m_id)) {
+ return Error{heif_error_Invalid_input,
+ heif_suberror_Unspecified,
+ "'iref' has cyclic references"};
+ }
+
+ processed_ids.insert(m_id);
+
+
DataExtent extent;
extent.set_from_image_item(get_file(), get_id());
diff --git a/libheif/image-items/image_item.h b/libheif/image-items/image_item.h
index cac1f283..a5ac0ac3 100644
--- a/libheif/image-items/image_item.h
+++ b/libheif/image-items/image_item.h
@@ -305,11 +305,13 @@ public:
virtual Result<std::shared_ptr<HeifPixelImage>> decode_image(const heif_decoding_options& options,
bool decode_tile_only, uint32_t tile_x0,
- uint32_t tile_y0) const;
+ uint32_t tile_y0,
+ std::set<heif_item_id> processed_ids) const;
virtual Result<std::shared_ptr<HeifPixelImage>> decode_compressed_image(const heif_decoding_options& options,
bool decode_tile_only, uint32_t tile_x0,
- uint32_t tile_y0) const;
+ uint32_t tile_y0,
+ std::set<heif_item_id> processed_ids) const;
Result<std::vector<std::shared_ptr<Box>>> get_properties() const;
@@ -464,14 +466,15 @@ public:
Result<std::shared_ptr<HeifPixelImage>> decode_image(const heif_decoding_options& options,
bool decode_tile_only, uint32_t tile_x0,
- uint32_t tile_y0) const override
+ uint32_t tile_y0,
+ std::set<heif_item_id> processed_ids) const override
{
return m_item_error;
}
Result<std::shared_ptr<HeifPixelImage>> decode_compressed_image(const heif_decoding_options& options,
bool decode_tile_only, uint32_t tile_x0,
- uint32_t tile_y0) const override
+ uint32_t tile_y0, std::set<heif_item_id> processed_ids) const override
{
return m_item_error;
}
diff --git a/libheif/image-items/mask_image.cc b/libheif/image-items/mask_image.cc
index 206efe5c..328d1797 100644
--- a/libheif/image-items/mask_image.cc
+++ b/libheif/image-items/mask_image.cc
@@ -128,7 +128,8 @@ Error MaskImageCodec::decode_mask_image(const HeifContext* context,
Result<std::shared_ptr<HeifPixelImage>> ImageItem_mask::decode_compressed_image(const heif_decoding_options& options,
- bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const
+ bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0,
+ std::set<heif_item_id> processed_ids) const
{
std::shared_ptr<HeifPixelImage> img;
diff --git a/libheif/image-items/mask_image.h b/libheif/image-items/mask_image.h
index b1f58f81..b748d57e 100644
--- a/libheif/image-items/mask_image.h
+++ b/libheif/image-items/mask_image.h
@@ -98,7 +98,8 @@ public:
int get_chroma_bits_per_pixel() const override { return 0; }
Result<std::shared_ptr<HeifPixelImage>> decode_compressed_image(const heif_decoding_options& options,
- bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const override;
+ bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0,
+ std::set<heif_item_id> processed_ids) const override;
Result<Encoder::CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
heif_encoder* encoder,
diff --git a/libheif/image-items/overlay.cc b/libheif/image-items/overlay.cc
index 1f5d3a92..168eb0a8 100644
--- a/libheif/image-items/overlay.cc
+++ b/libheif/image-items/overlay.cc
@@ -273,14 +273,25 @@ Error ImageItem_Overlay::read_overlay_spec()
Result<std::shared_ptr<HeifPixelImage>> ImageItem_Overlay::decode_compressed_image(const heif_decoding_options& options,
- bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const
+ bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0,
+ std::set<heif_item_id> processed_ids) const
{
- return decode_overlay_image(options);
+ return decode_overlay_image(options, processed_ids);
}
-Result<std::shared_ptr<HeifPixelImage>> ImageItem_Overlay::decode_overlay_image(const heif_decoding_options& options) const
+Result<std::shared_ptr<HeifPixelImage>> ImageItem_Overlay::decode_overlay_image(const heif_decoding_options& options,
+ std::set<heif_item_id> processed_ids) const
{
+ if (processed_ids.contains(get_id())) {
+ return Error{heif_error_Invalid_input,
+ heif_suberror_Unspecified,
+ "'iref' has cyclic references"};
+ }
+
+ processed_ids.insert(get_id());
+
+
std::shared_ptr<HeifPixelImage> img;
uint32_t w = m_overlay_spec.get_canvas_width();
@@ -332,7 +343,7 @@ Result<std::shared_ptr<HeifPixelImage>> ImageItem_Overlay::decode_overlay_image(
return error;
}
- auto decodeResult = imgItem->decode_image(options, false, 0,0);
+ auto decodeResult = imgItem->decode_image(options, false, 0,0, processed_ids);
if (!decodeResult) {
return decodeResult.error();
}
diff --git a/libheif/image-items/overlay.h b/libheif/image-items/overlay.h
index 8c6e6138..fbaa0c8a 100644
--- a/libheif/image-items/overlay.h
+++ b/libheif/image-items/overlay.h
@@ -117,7 +117,8 @@ public:
}
Result<std::shared_ptr<HeifPixelImage>> decode_compressed_image(const heif_decoding_options& options,
- bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const override;
+ bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0,
+ std::set<heif_item_id> processed_ids) const override;
// --- iovl specific
@@ -130,7 +131,8 @@ private:
Error read_overlay_spec();
- Result<std::shared_ptr<HeifPixelImage>> decode_overlay_image(const heif_decoding_options& options) const;
+ Result<std::shared_ptr<HeifPixelImage>> decode_overlay_image(const heif_decoding_options& options,
+ std::set<heif_item_id> processed_ids) const;
};
diff --git a/libheif/image-items/tiled.cc b/libheif/image-items/tiled.cc
index 95b00bc1..dce157cc 100644
--- a/libheif/image-items/tiled.cc
+++ b/libheif/image-items/tiled.cc
@@ -759,7 +759,8 @@ void ImageItem_Tiled::process_before_write()
Result<std::shared_ptr<HeifPixelImage>>
ImageItem_Tiled::decode_compressed_image(const heif_decoding_options& options,
- bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const
+ bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0,
+ std::set<heif_item_id> processed_ids) const
{
if (decode_tile_only) {
return decode_grid_tile(options, tile_x0, tile_y0);
diff --git a/libheif/image-items/tiled.h b/libheif/image-items/tiled.h
index 66d3ecc2..a9863ad6 100644
--- a/libheif/image-items/tiled.h
+++ b/libheif/image-items/tiled.h
@@ -181,7 +181,8 @@ public:
}
Result<std::shared_ptr<HeifPixelImage>> decode_compressed_image(const heif_decoding_options& options,
- bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const override;
+ bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0,
+ std::set<heif_item_id> processed_ids) const override;
heif_brand2 get_compatible_brand() const override;
diff --git a/libheif/image-items/unc_image.cc b/libheif/image-items/unc_image.cc
index 55ada00d..a0616d65 100644
--- a/libheif/image-items/unc_image.cc
+++ b/libheif/image-items/unc_image.cc
@@ -76,7 +76,8 @@ ImageItem_uncompressed::ImageItem_uncompressed(HeifContext* ctx)
Result<std::shared_ptr<HeifPixelImage>> ImageItem_uncompressed::decode_compressed_image(const heif_decoding_options& options,
- bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const
+ bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0,
+ std::set<heif_item_id> processed_ids) const
{
std::shared_ptr<HeifPixelImage> img;
diff --git a/libheif/image-items/unc_image.h b/libheif/image-items/unc_image.h
index 7d02b7fd..a4d8b130 100644
--- a/libheif/image-items/unc_image.h
+++ b/libheif/image-items/unc_image.h
@@ -60,7 +60,8 @@ public:
// Code from encode_uncompressed_image() has been moved to here.
Result<std::shared_ptr<HeifPixelImage>> decode_compressed_image(const heif_decoding_options& options,
- bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const override;
+ bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0,
+ std::set<heif_item_id> processed_ids) const override;
heif_image_tiling get_heif_image_tiling() const override;