Commit c25ff89b for libheif

commit c25ff89b245680584b7edfdc0757b4c1f21b9302
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Thu Mar 19 23:51:43 2026 +0100

    decoder-tiff: make number of tiles computation overflow safe

diff --git a/heifio/decoder_tiff.cc b/heifio/decoder_tiff.cc
index e3195971..fda05b22 100644
--- a/heifio/decoder_tiff.cc
+++ b/heifio/decoder_tiff.cc
@@ -30,6 +30,7 @@
 #include <utility>
 #include <vector>
 #include <algorithm>
+#include <limits>

 extern "C" {
 #include <tiff.h>
@@ -312,6 +313,11 @@ heif_error getImageWidthAndHeight(TIFF *tif, uint32_t &width, uint32_t &height)
       .message = "Can not read width and/or height from TIFF image."};
     return err;
   }
+
+  if (width == 0 || height == 0) {
+    return {heif_error_Invalid_input, heif_suberror_Unspecified, "Zero TIFF image size is invalid."};
+  }
+
   return heif_error_ok;
 }

@@ -961,6 +967,11 @@ static heif_error readTiledContiguous(TIFF* tif, uint32_t width, uint32_t height
               "Only 8-bit YCbCr TIFF is supported."};
     }

+    if (width > std::numeric_limits<int>::max() || height > std::numeric_limits<int>::max()) {
+      return {heif_error_Invalid_input, heif_suberror_Unspecified, "TIFF image size exceeds maximum supported by libheif."};
+    }
+
+
     heif_chroma chroma = ycbcrChroma(ycbcr);
     heif_error err = heif_image_create((int)width, (int)height, heif_colorspace_YCbCr, chroma, out_image);
     if (err.code != heif_error_Ok) return err;
@@ -979,8 +990,8 @@ static heif_error readTiledContiguous(TIFF* tif, uint32_t width, uint32_t height
     tmsize_t tile_buf_size = TIFFTileSize(tif);
     std::vector<uint8_t> tile_buf(tile_buf_size);

-    uint32_t n_cols = (width + tile_width - 1) / tile_width;
-    uint32_t n_rows = (height + tile_height - 1) / tile_height;
+    uint32_t n_cols = (width - 1) / tile_width + 1;
+    uint32_t n_rows = (height - 1) / tile_height + 1;

     for (uint32_t ty = 0; ty < n_rows; ty++) {
       for (uint32_t tx = 0; tx < n_cols; tx++) {
@@ -1039,8 +1050,8 @@ static heif_error readTiledContiguous(TIFF* tif, uint32_t width, uint32_t height
     tmsize_t tile_buf_size = TIFFTileSize(tif);
     std::vector<uint8_t> tile_buf(tile_buf_size);

-    uint32_t n_cols = (width + tile_width - 1) / tile_width;
-    uint32_t n_rows = (height + tile_height - 1) / tile_height;
+    uint32_t n_cols = (width - 1) / tile_width + 1;
+    uint32_t n_rows = (height - 1) / tile_height + 1;

     for (uint32_t ty = 0; ty < n_rows; ty++) {
       for (uint32_t tx = 0; tx < n_cols; tx++) {
@@ -1106,8 +1117,8 @@ static heif_error readTiledContiguous(TIFF* tif, uint32_t width, uint32_t height
     tmsize_t tile_buf_size = TIFFTileSize(tif);
     std::vector<uint8_t> tile_buf(tile_buf_size);

-    uint32_t n_cols = (width + tile_width - 1) / tile_width;
-    uint32_t n_rows = (height + tile_height - 1) / tile_height;
+    uint32_t n_cols = (width - 1) / tile_width + 1;
+    uint32_t n_rows = (height - 1) / tile_height + 1;

     for (uint32_t ty = 0; ty < n_rows; ty++) {
       for (uint32_t tx = 0; tx < n_cols; tx++) {
@@ -1156,8 +1167,8 @@ static heif_error readTiledContiguous(TIFF* tif, uint32_t width, uint32_t height
   tmsize_t tile_buf_size = TIFFTileSize(tif);
   std::vector<uint8_t> tile_buf(tile_buf_size);

-  uint32_t n_cols = (width + tile_width - 1) / tile_width;
-  uint32_t n_rows = (height + tile_height - 1) / tile_height;
+  uint32_t n_cols = (width - 1) / tile_width + 1;
+  uint32_t n_rows = (height - 1) / tile_height + 1;

   for (uint32_t ty = 0; ty < n_rows; ty++) {
     for (uint32_t tx = 0; tx < n_cols; tx++) {
@@ -1272,8 +1283,8 @@ static heif_error readTiledSeparate(TIFF* tif, uint32_t width, uint32_t height,
   tmsize_t tile_buf_size = TIFFTileSize(tif);
   std::vector<uint8_t> tile_buf(tile_buf_size);

-  uint32_t n_cols = (width + tile_width - 1) / tile_width;
-  uint32_t n_rows = (height + tile_height - 1) / tile_height;
+  uint32_t n_cols = (width - 1) / tile_width + 1;
+  uint32_t n_rows = (height - 1) / tile_height + 1;

   for (uint16_t s = 0; s < outSpp; s++) {
     for (uint32_t ty = 0; ty < n_rows; ty++) {
@@ -1365,6 +1376,10 @@ heif_error loadTIFF(const char* filename, int output_bit_depth, InputImage *inpu
       return {heif_error_Invalid_input, heif_suberror_Unspecified, "Cannot read TIFF tile dimensions"};
     }

+    if (tile_width == 0 || tile_height == 0) {
+      return {heif_error_Invalid_input, heif_suberror_Unspecified, "Invalid TIFF tile dimensions"};
+    }
+
     switch (config) {
       case PLANARCONFIG_CONTIG:
         err = readTiledContiguous(tif, width, height, tile_width, tile_height, samplesPerPixel, hasAlpha, bps, effectiveOutputBitDepth, sampleFormat, &image);
@@ -1479,8 +1494,13 @@ std::unique_ptr<TiledTiffReader> TiledTiffReader::open(const char* filename, hei
     return nullptr;
   }

-  reader->m_n_columns = (reader->m_image_width + reader->m_tile_width - 1) / reader->m_tile_width;
-  reader->m_n_rows = (reader->m_image_height + reader->m_tile_height - 1) / reader->m_tile_height;
+  if (reader->m_tile_width == 0 || reader->m_tile_height == 0) {
+    *out_err = {heif_error_Invalid_input, heif_suberror_Unspecified, "Invalid TIFF tile dimensions"};
+    return nullptr;
+  }
+
+  reader->m_n_columns = (reader->m_image_width - 1) / reader->m_tile_width + 1;
+  reader->m_n_rows = (reader->m_image_height - 1) / reader->m_tile_height + 1;

   // Detect overview directories (reduced-resolution images)
   tdir_t n_dirs = TIFFNumberOfDirectories(tif);
@@ -1516,6 +1536,14 @@ std::unique_ptr<TiledTiffReader> TiledTiffReader::open(const char* filename, hei
       continue;
     }

+    if (ov_width == 0 || ov_height == 0) {
+      continue;
+    }
+
+    if (ov_tw == 0 || ov_th == 0) {
+      continue;
+    }
+
     reader->m_overviews.push_back({d, ov_width, ov_height, ov_tw, ov_th});
   }

@@ -1548,6 +1576,14 @@ bool TiledTiffReader::setDirectory(uint32_t dir_index)
     return false;
   }

+  if (m_image_width == 0 || m_image_height == 0) {
+    return false;
+  }
+
+  if (m_tile_width == 0 || m_tile_height == 0) {
+    return false;
+  }
+
   uint16_t bps;
   heif_error err = validateTiffFormat(tif, m_samples_per_pixel, bps, m_planar_config, m_has_alpha, m_sample_format);
   if (err.code != heif_error_Ok) {
@@ -1561,8 +1597,8 @@ bool TiledTiffReader::setDirectory(uint32_t dir_index)
     m_ycbcr.is_ycbcr = false;
   }

-  m_n_columns = (m_image_width + m_tile_width - 1) / m_tile_width;
-  m_n_rows = (m_image_height + m_tile_height - 1) / m_tile_height;
+  m_n_columns = (m_image_width - 1) / m_tile_width + 1;
+  m_n_rows = (m_image_height - 1) / m_tile_height + 1;

   return true;
 }