Commit ebcad8ed for libheif

commit ebcad8ed1084f08f084efe5e3db7d1733f083f7b
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Mon Feb 23 01:33:34 2026 +0100

    unci: more valid input checking

diff --git a/libheif/codecs/uncompressed/unc_decoder.cc b/libheif/codecs/uncompressed/unc_decoder.cc
index cce88611..f58e0a54 100644
--- a/libheif/codecs/uncompressed/unc_decoder.cc
+++ b/libheif/codecs/uncompressed/unc_decoder.cc
@@ -43,10 +43,10 @@
 unc_decoder::unc_decoder(uint32_t width, uint32_t height,
                          const std::shared_ptr<const Box_cmpd>& cmpd,
                          const std::shared_ptr<const Box_uncC>& uncC)
-    : m_width(width),
-      m_height(height),
-      m_cmpd(cmpd),
-      m_uncC(uncC)
+  : m_width(width),
+    m_height(height),
+    m_cmpd(cmpd),
+    m_uncC(uncC)
 {
   m_tile_height = m_height / m_uncC->get_number_of_tile_rows();
   m_tile_width = m_width / m_uncC->get_number_of_tile_columns();
@@ -97,11 +97,11 @@ Error unc_decoder::fetch_tile_data(const DataExtent& dataExtent,


 const Error unc_decoder::get_compressed_image_data_uncompressed(const DataExtent& dataExtent,
-                                                                 const UncompressedImageCodec::unci_properties& properties,
-                                                                 std::vector<uint8_t>* data,
-                                                                 uint64_t range_start_offset, uint64_t range_size,
-                                                                 uint32_t tile_idx,
-                                                                 const Box_iloc::Item* item) const
+                                                                const UncompressedImageCodec::unci_properties& properties,
+                                                                std::vector<uint8_t>* data,
+                                                                uint64_t range_start_offset, uint64_t range_size,
+                                                                uint32_t tile_idx,
+                                                                const Box_iloc::Item* item) const
 {
   // --- get codec configuration

@@ -123,15 +123,17 @@ const Error unc_decoder::get_compressed_image_data_uncompressed(const DataExtent
   if (icef_box && cmpC_box->get_compressed_unit_type() == heif_cmpC_compressed_unit_type_image_tile) {
     const auto& units = icef_box->get_units();
     if (tile_idx >= units.size()) {
-      return {heif_error_Invalid_input,
-              heif_suberror_Unspecified,
-              "no icef-box entry for tile index"};
+      return {
+        heif_error_Invalid_input,
+        heif_suberror_Unspecified,
+        "no icef-box entry for tile index"
+      };
     }

     const auto unit = units[tile_idx];

     // get data needed for one tile
-    Result<std::vector<uint8_t>> readingResult = dataExtent.read_data(unit.unit_offset, unit.unit_size);
+    Result<std::vector<uint8_t> > readingResult = dataExtent.read_data(unit.unit_offset, unit.unit_size);
     if (!readingResult) {
       return readingResult.error();
     }
@@ -178,9 +180,11 @@ const Error unc_decoder::get_compressed_image_data_uncompressed(const DataExtent
     }

     if (range_start_offset + range_size > data->size()) {
-      return {heif_error_Invalid_input,
-              heif_suberror_Unspecified,
-              "Data range out of existing range"};
+      return {
+        heif_error_Invalid_input,
+        heif_suberror_Unspecified,
+        "Data range out of existing range"
+      };
     }

     // cut out the range that we actually need
@@ -205,9 +209,11 @@ const Error unc_decoder::get_compressed_image_data_uncompressed(const DataExtent
     *data = std::move(*dataResult);

     if (range_start_offset + range_size > data->size()) {
-      return {heif_error_Invalid_input,
-              heif_suberror_Unspecified,
-              "Data range out of existing range"};
+      return {
+        heif_error_Invalid_input,
+        heif_suberror_Unspecified,
+        "Data range out of existing range"
+      };
     }

     // cut out the range that we actually need
@@ -219,7 +225,7 @@ const Error unc_decoder::get_compressed_image_data_uncompressed(const DataExtent
 }


-Result<std::vector<uint8_t>> unc_decoder::do_decompress_data(std::shared_ptr<const Box_cmpC>& cmpC_box,
+Result<std::vector<uint8_t> > unc_decoder::do_decompress_data(std::shared_ptr<const Box_cmpC>& cmpC_box,
                                                               std::vector<uint8_t> compressed_data) const
 {
   if (cmpC_box->get_compression_type() == fourcc("brot")) {
@@ -227,10 +233,10 @@ Result<std::vector<uint8_t>> unc_decoder::do_decompress_data(std::shared_ptr<con
     return decompress_brotli(compressed_data);
 #else
     std::stringstream sstr;
-  sstr << "cannot decode unci item with brotli compression - not enabled" << std::endl;
-  return Error(heif_error_Unsupported_feature,
-               heif_suberror_Unsupported_generic_compression_method,
-               sstr.str());
+    sstr << "cannot decode unci item with brotli compression - not enabled" << std::endl;
+    return Error(heif_error_Unsupported_feature,
+                 heif_suberror_Unsupported_generic_compression_method,
+                 sstr.str());
 #endif
   }
   else if (cmpC_box->get_compression_type() == fourcc("zlib")) {
@@ -338,7 +344,7 @@ bool unc_decoder_factory::check_common_requirements(const std::shared_ptr<const

 Error check_hard_limits(const std::shared_ptr<const Box_uncC>& uncC)
 {
-  const auto& components =uncC->get_components();
+  const auto& components = uncC->get_components();

   for (const auto& component : components) {
     switch (component.component_format) {
@@ -368,14 +374,34 @@ Error check_hard_limits(const std::shared_ptr<const Box_uncC>& uncC)
     }
   }

+  if (uncC->get_interleave_type() != 1 &&
+      uncC->get_interleave_type() != 5 &&
+      uncC->get_pixel_size() != 0) {
+        return Error{heif_error_Invalid_input, heif_suberror_Unspecified, "uncC pixel_size must be 0 for interleave_types other than 1 or 5."};
+  }
+
+  if (uncC->get_interleave_type() == interleave_mode_pixel &&
+      uncC->get_pixel_size() != 0) {
+    uint32_t total_pixel_bits = 0;
+    for (const auto& component : uncC->get_components()) {
+      total_pixel_bits += component.component_bit_depth;
+    }
+
+    // TODO: we do not consider padding or block sizes yet
+    uint32_t PixelBytes = (total_pixel_bits + 7)/8;
+    if (PixelBytes > uncC->get_pixel_size()) {
+      return Error{heif_error_Invalid_input, heif_suberror_Unspecified, "uncC pixel_size smaller than sum of component sizes."};
+    }
+  }
+
   return {};
 }


-Result<std::unique_ptr<unc_decoder>> unc_decoder_factory::get_unc_decoder(
-    uint32_t width, uint32_t height,
-    const std::shared_ptr<const Box_cmpd>& cmpd,
-    const std::shared_ptr<const Box_uncC>& uncC)
+Result<std::unique_ptr<unc_decoder> > unc_decoder_factory::get_unc_decoder(
+  uint32_t width, uint32_t height,
+  const std::shared_ptr<const Box_cmpd>& cmpd,
+  const std::shared_ptr<const Box_uncC>& uncC)
 {
   static unc_decoder_factory_component_interleave dec_component;
   static unc_decoder_factory_bytealign_component_interleave dec_bytealign_component;
@@ -403,10 +429,10 @@ Result<std::unique_ptr<unc_decoder>> unc_decoder_factory::get_unc_decoder(

 // --- decode orchestration ---

-Result<std::shared_ptr<HeifPixelImage>> unc_decoder::decode_full_image(
-    const UncompressedImageCodec::unci_properties& properties,
-    const DataExtent& extent,
-    const heif_security_limits* limits)
+Result<std::shared_ptr<HeifPixelImage> > unc_decoder::decode_full_image(
+  const UncompressedImageCodec::unci_properties& properties,
+  const DataExtent& extent,
+  const heif_security_limits* limits)
 {
   const std::shared_ptr<const Box_ispe>& ispe = properties.ispe;
   const std::shared_ptr<const Box_cmpd>& cmpd = properties.cmpd;
@@ -421,7 +447,7 @@ Result<std::shared_ptr<HeifPixelImage>> unc_decoder::decode_full_image(
     return {global_limit_error};
   }

-  Result<std::shared_ptr<HeifPixelImage>> createImgResult = UncompressedImageCodec::create_image(cmpd, uncC, width, height, limits);
+  Result<std::shared_ptr<HeifPixelImage> > createImgResult = UncompressedImageCodec::create_image(cmpd, uncC, width, height, limits);
   if (!createImgResult) {
     return createImgResult.error();
   }