libtiff: TIFFWriteDirectory: Avoid overwriting following data if an IFD is enlarged.

From 4c809ccf1672a94bc83533715809133a7bc946a5 Mon Sep 17 00:00:00 2001
From: Su Laus <[EMAIL REDACTED]>
Date: Tue, 26 Mar 2024 22:17:04 +0000
Subject: [PATCH] TIFFWriteDirectory: Avoid overwriting following data if an
 IFD is enlarged.

Avoid overwriting other data if an IFD that has already been written is enlarged. If entries are to be changed in an IFD that has already been written to the file, the memory area can be overwritten as long as the IFD data volume has not increased. Otherwise, the IFD must be written to a different location in the file. Up to now, LibTIFF has simply overwritten the IFD and other data areas in the file.

With this MR, the size of the IFD, and location of IFD data, is tracked and checked if the IFD can be safely overwritten or has to be re-written to another location.
---
 libtiff/tif_dir.c           |   10 +
 libtiff/tif_dir.h           |   24 +
 libtiff/tif_dirread.c       |  226 ++++-
 libtiff/tif_dirwrite.c      |  430 +++++----
 libtiff/tiffiop.h           |    2 +-
 test/CMakeLists.txt         |    5 +
 test/Makefile.am            |    4 +-
 test/test_IFD_enlargement.c | 1820 +++++++++++++++++++++++++++++++++++
 8 files changed, 2349 insertions(+), 172 deletions(-)
 create mode 100644 test/test_IFD_enlargement.c

diff --git a/libtiff/tif_dir.c b/libtiff/tif_dir.c
index bd8e661a..de246d41 100644
--- a/libtiff/tif_dir.c
+++ b/libtiff/tif_dir.c
@@ -1652,6 +1652,16 @@ void TIFFFreeDirectory(TIFF *tif)
 
     _TIFFmemset(&(td->td_stripoffset_entry), 0, sizeof(TIFFDirEntry));
     _TIFFmemset(&(td->td_stripbytecount_entry), 0, sizeof(TIFFDirEntry));
+
+    /* Reset some internal parameters for IFD data size checking. */
+    tif->tif_dir.td_dirdatasize_read = 0;
+    tif->tif_dir.td_dirdatasize_write = 0;
+    if (tif->tif_dir.td_dirdatasize_offsets != NULL)
+    {
+        _TIFFfreeExt(tif, tif->tif_dir.td_dirdatasize_offsets);
+        tif->tif_dir.td_dirdatasize_offsets = NULL;
+        tif->tif_dir.td_dirdatasize_Noffsets = 0;
+    }
 }
 #undef CleanupField
 
diff --git a/libtiff/tif_dir.h b/libtiff/tif_dir.h
index 9eaf22f8..4afb1491 100644
--- a/libtiff/tif_dir.h
+++ b/libtiff/tif_dir.h
@@ -65,6 +65,12 @@ typedef struct
                             tif_dirread.c */
 } TIFFDirEntry;
 
+typedef struct
+{
+    uint64_t offset;
+    uint64_t length;
+} TIFFEntryOffsetAndLength; /* auxiliary for evaluating size of IFD data */
+
 /*
  * Internal format of a TIFF directory entry.
  */
@@ -115,6 +121,9 @@ typedef struct
 #ifdef STRIPBYTECOUNTSORTED_UNUSED
     int td_stripbytecountsorted; /* is the bytecount array sorted ascending? */
 #endif
+    /* Be aware that the parameters of td_stripoffset_entry and
+     * td_stripbytecount_entry are swapped but tdir_offset is not
+     * and has to be swapped when used. */
     TIFFDirEntry td_stripoffset_entry;    /* for deferred loading */
     TIFFDirEntry td_stripbytecount_entry; /* for deferred loading */
     uint16_t td_nsubifd;
@@ -135,6 +144,21 @@ typedef struct
 
     unsigned char
         td_deferstrilearraywriting; /* see TIFFDeferStrileArrayWriting() */
+
+    /* LibTIFF writes all data that does not fit into the IFD entries directly
+     * after the IFD tag enty part. When reading, only the IFD data directly and
+     * continuously behind the IFD tags is taken into account for the IFD data
+     * size.*/
+    uint64_t td_dirdatasize_write; /* auxiliary for evaluating size of IFD data
+                                       to be written */
+    uint64_t td_dirdatasize_read;  /* auxiliary for evaluating size of IFD data
+                                       read from file */
+    uint32_t td_dirdatasize_Noffsets; /* auxiliary counter for
+                                         tif_dir.td_dirdatasize_offsets array */
+    TIFFEntryOffsetAndLength
+        *td_dirdatasize_offsets; /* auxiliary array for all offsets of IFD tag
+                                    entries with data outside the IFD tag
+                                    entries. */
 } TIFFDirectory;
 
 /*
diff --git a/libtiff/tif_dirread.c b/libtiff/tif_dirread.c
index d4028e9f..7c877e9b 100644
--- a/libtiff/tif_dirread.c
+++ b/libtiff/tif_dirread.c
@@ -4077,6 +4077,141 @@ static int ByteCountLooksBad(TIFF *tif)
     return 0;
 }
 
+/*
+ * To evaluate the IFD data size when reading, save the offset and data size of
+ * all data that does not fit into the IFD entries themselves.
+ */
+static void EvaluateIFDdatasizeReading(TIFF *tif, TIFFDirEntry *dp)
+{
+    uint64_t datalength = dp->tdir_count * TIFFDataWidth(dp->tdir_type);
+    if (datalength > ((tif->tif_flags & TIFF_BIGTIFF) ? 0x8U : 0x4U))
+    {
+        tif->tif_dir.td_dirdatasize_read += datalength;
+        if (!(tif->tif_flags & TIFF_BIGTIFF))
+        {
+            /* The offset of TIFFDirEntry are not swapped when read in. That has
+             * to be done when used. */
+            uint32_t offset = dp->tdir_offset.toff_long;
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong(&offset);
+            tif->tif_dir
+                .td_dirdatasize_offsets[tif->tif_dir.td_dirdatasize_Noffsets]
+                .offset = (uint64_t)offset;
+        }
+        else
+        {
+            tif->tif_dir
+                .td_dirdatasize_offsets[tif->tif_dir.td_dirdatasize_Noffsets]
+                .offset = dp->tdir_offset.toff_long8;
+            if (tif->tif_flags & TIFF_SWAB)
+                TIFFSwabLong8(
+                    &tif->tif_dir
+                         .td_dirdatasize_offsets[tif->tif_dir
+                                                     .td_dirdatasize_Noffsets]
+                         .offset);
+        }
+        tif->tif_dir
+            .td_dirdatasize_offsets[tif->tif_dir.td_dirdatasize_Noffsets]
+            .length = datalength;
+        tif->tif_dir.td_dirdatasize_Noffsets++;
+    }
+}
+
+/*
+ * Compare function for qsort() sorting TIFFEntryOffsetAndLength array entries.
+ */
+static int cmpTIFFEntryOffsetAndLength(const void *a, const void *b)
+{
+    const TIFFEntryOffsetAndLength *ta = (const TIFFEntryOffsetAndLength *)a;
+    const TIFFEntryOffsetAndLength *tb = (const TIFFEntryOffsetAndLength *)b;
+    /* Compare offsets */
+    if (ta->offset > tb->offset)
+        return 1;
+    else if (ta->offset < tb->offset)
+        return -1;
+    else
+        return 0;
+}
+
+/*
+ * Determine the IFD data size after reading an IFD from the file that can be
+ * overwritten and saving it in tif_dir.td_dirdatasize_read. This data size
+ * includes the IFD entries themselves as well as the data that does not fit
+ * directly into the IFD entries but is located directly after the IFD entries
+ * in the file.
+ */
+static void CalcFinalIFDdatasizeReading(TIFF *tif, uint16_t dircount)
+{
+    /* IFD data size is only needed if file-writing is enabled.
+     * This also avoids the seek() to EOF to determine the file size, which
+     * causes the stdin-streaming-friendly mode of libtiff for GDAL to fail. */
+    if (tif->tif_mode == O_RDONLY)
+        return;
+
+    /* Sort TIFFEntryOffsetAndLength array in ascending order. */
+    qsort(tif->tif_dir.td_dirdatasize_offsets,
+          tif->tif_dir.td_dirdatasize_Noffsets,
+          sizeof(TIFFEntryOffsetAndLength), cmpTIFFEntryOffsetAndLength);
+
+    /* Get offset of end of IFD entry space. */
+    uint64_t IFDendoffset;
+    if (!(tif->tif_flags & TIFF_BIGTIFF))
+        IFDendoffset = tif->tif_diroff + 2 + dircount * 12 + 4;
+    else
+        IFDendoffset = tif->tif_diroff + 8 + dircount * 20 + 8;
+
+    /* Check which offsets are right behind IFD entries. However, LibTIFF
+     * increments the writing address for every external data to an even offset.
+     * Thus gaps of 1 byte can occur. */
+    uint64_t size = 0;
+    uint64_t offset;
+    uint32_t i;
+    for (i = 0; i < tif->tif_dir.td_dirdatasize_Noffsets; i++)
+    {
+        offset = tif->tif_dir.td_dirdatasize_offsets[i].offset;
+        if (offset == IFDendoffset)
+        {
+            size += tif->tif_dir.td_dirdatasize_offsets[i].length;
+            IFDendoffset += tif->tif_dir.td_dirdatasize_offsets[i].length;
+        }
+        else if (offset == IFDendoffset + 1)
+        {
+            /* Add gap byte after previous IFD data set. */
+            size += tif->tif_dir.td_dirdatasize_offsets[i].length + 1;
+            IFDendoffset += tif->tif_dir.td_dirdatasize_offsets[i].length;
+        }
+        else
+        {
+            /* Further data is no more continously after IFD */
+            break;
+        }
+    }
+    /* Check for gap byte of some easy cases. This should cover 90% of cases.
+     * Otherwise, IFD will be re-written even it might be safely overwritten. */
+    if (tif->tif_nextdiroff != 0)
+    {
+        if (tif->tif_nextdiroff == IFDendoffset + 1)
+            size++;
+    }
+    else
+    {
+        /* Check for IFD data ends at EOF. Then IFD can always be safely
+         * overwritten. */
+        offset = TIFFSeekFile(tif, 0, SEEK_END);
+        if (offset == IFDendoffset)
+        {
+            tif->tif_dir.td_dirdatasize_read = UINT64_MAX;
+            return;
+        }
+    }
+
+    /* Finally, add the size of the IFD tag entries themselves. */
+    if (!(tif->tif_flags & TIFF_BIGTIFF))
+        tif->tif_dir.td_dirdatasize_read = 2 + dircount * 12 + 4 + size;
+    else
+        tif->tif_dir.td_dirdatasize_read = 8 + dircount * 20 + 8 + size;
+} /*-- CalcFinalIFDdatasizeReading() --*/
+
 /*
  * Read the next TIFF directory from a file and convert it to the internal
  * format. We read directories sequentially.
@@ -4164,6 +4299,19 @@ int TIFFReadDirectory(TIFF *tif)
     /* free any old stuff and reinit */
     TIFFFreeDirectory(tif);
     TIFFDefaultDirectory(tif);
+
+    /* Allocate arrays for offset values outside IFD entry for IFD data size
+     * checking. Note: Counter are reset within TIFFFreeDirectory(). */
+    tif->tif_dir.td_dirdatasize_offsets =
+        (TIFFEntryOffsetAndLength *)_TIFFmallocExt(
+            tif, dircount * sizeof(TIFFEntryOffsetAndLength));
+    if (tif->tif_dir.td_dirdatasize_offsets == NULL)
+    {
+        TIFFErrorExtR(
+            tif, module,
+            "Failed to allocate memory for counting IFD data size at reading");
+        return 0;
+    }
     /*
      * Electronic Arts writes gray-scale TIFF files
      * without a PlanarConfiguration directory entry.
@@ -4361,6 +4509,7 @@ int TIFFReadDirectory(TIFF *tif)
                         uint16_t value;
                         enum TIFFReadDirEntryErr err;
                         err = TIFFReadDirEntryShort(tif, dp, &value);
+                        EvaluateIFDdatasizeReading(tif, dp);
                         if (err == TIFFReadDirEntryErrCount)
                             err =
                                 TIFFReadDirEntryPersampleShort(tif, dp, &value);
@@ -4391,6 +4540,7 @@ int TIFFReadDirectory(TIFF *tif)
                         err = TIFFReadDirEntryErrCount;
                     else
                         err = TIFFReadDirEntryDoubleArray(tif, dp, &data);
+                    EvaluateIFDdatasizeReading(tif, dp);
                     if (err != TIFFReadDirEntryErrOk)
                     {
                         fip = TIFFFieldWithTag(tif, dp->tdir_tag);
@@ -4410,6 +4560,7 @@ int TIFFReadDirectory(TIFF *tif)
                 break;
                 case TIFFTAG_STRIPOFFSETS:
                 case TIFFTAG_TILEOFFSETS:
+                {
                     switch (dp->tdir_type)
                     {
                         case TIFF_SHORT:
@@ -4432,9 +4583,12 @@ int TIFFReadDirectory(TIFF *tif)
                     }
                     _TIFFmemcpy(&(tif->tif_dir.td_stripoffset_entry), dp,
                                 sizeof(TIFFDirEntry));
-                    break;
+                    EvaluateIFDdatasizeReading(tif, dp);
+                }
+                break;
                 case TIFFTAG_STRIPBYTECOUNTS:
                 case TIFFTAG_TILEBYTECOUNTS:
+                {
                     switch (dp->tdir_type)
                     {
                         case TIFF_SHORT:
@@ -4457,7 +4611,9 @@ int TIFFReadDirectory(TIFF *tif)
                     }
                     _TIFFmemcpy(&(tif->tif_dir.td_stripbytecount_entry), dp,
                                 sizeof(TIFFDirEntry));
-                    break;
+                    EvaluateIFDdatasizeReading(tif, dp);
+                }
+                break;
                 case TIFFTAG_COLORMAP:
                 case TIFFTAG_TRANSFERFUNCTION:
                 {
@@ -4511,6 +4667,7 @@ int TIFFReadDirectory(TIFF *tif)
                         err = TIFFReadDirEntryErrCount;
                     else
                         err = TIFFReadDirEntryShortArray(tif, dp, &value);
+                    EvaluateIFDdatasizeReading(tif, dp);
                     if (err != TIFFReadDirEntryErrOk)
                     {
                         fip = TIFFFieldWithTag(tif, dp->tdir_tag);
@@ -4601,9 +4758,12 @@ int TIFFReadDirectory(TIFF *tif)
                 default:
                     (void)TIFFFetchNormalTag(tif, dp, TRUE);
                     break;
-            }
-        } /* -- if (!dp->tdir_ignore) */
-    }     /* -- for-loop -- */
+            } /* -- switch (dp->tdir_tag) -- */
+        }     /* -- if (!dp->tdir_ignore) */
+    }         /* -- for-loop -- */
+
+    /* Evaluate final IFD data size. */
+    CalcFinalIFDdatasizeReading(tif, dircount);
 
     /*
      * OJPEG hack:
@@ -5109,6 +5269,19 @@ int TIFFReadCustomDirectory(TIFF *tif, toff_t diroff,
     TIFFFreeDirectory(tif);
     _TIFFmemset(&tif->tif_dir, 0, sizeof(TIFFDirectory));
     TIFFReadDirectoryCheckOrder(tif, dir, dircount);
+    /* Allocate arrays for offset values outside IFD entry for IFD data size
+     * checking. Note: Counter are reset within TIFFFreeDirectory(). */
+    tif->tif_dir.td_dirdatasize_offsets =
+        (TIFFEntryOffsetAndLength *)_TIFFmallocExt(
+            tif, dircount * sizeof(TIFFEntryOffsetAndLength));
+    if (tif->tif_dir.td_dirdatasize_offsets == NULL)
+    {
+        TIFFErrorExtR(
+            tif, module,
+            "Failed to allocate memory for counting IFD data size at reading");
+        return 0;
+    }
+
     for (di = 0, dp = dir; di < dircount; di++, dp++)
     {
         TIFFReadDirectoryFindFieldInfo(tif, dp->tdir_tag, &fii);
@@ -5203,6 +5376,9 @@ int TIFFReadCustomDirectory(TIFF *tif, toff_t diroff,
             } /*-- if (!dp->tdir_ignore) */
         }
     }
+    /* Evaluate final IFD data size. */
+    CalcFinalIFDdatasizeReading(tif, dircount);
+
     /* To be able to return from SubIFD or custom-IFD to main-IFD */
     tif->tif_setdirectory_force_absolute = TRUE;
     if (dir)
@@ -6088,6 +6264,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                         }
                     }
                 }
+                EvaluateIFDdatasizeReading(tif, dp);
                 if (mb + 1 < (uint32_t)dp->tdir_count)
                     TIFFWarningExtR(
                         tif, module,
@@ -6215,6 +6392,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
             err = TIFFReadDirEntryLong8(tif, dp, &data);
             if (err == TIFFReadDirEntryErrOk)
             {
+                EvaluateIFDdatasizeReading(tif, dp);
                 if (!TIFFSetField(tif, dp->tdir_tag, data))
                     return (0);
             }
@@ -6228,6 +6406,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
             err = TIFFReadDirEntrySlong8(tif, dp, &data);
             if (err == TIFFReadDirEntryErrOk)
             {
+                EvaluateIFDdatasizeReading(tif, dp);
                 if (!TIFFSetField(tif, dp->tdir_tag, data))
                     return (0);
             }
@@ -6241,6 +6420,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
             err = TIFFReadDirEntryFloat(tif, dp, &data);
             if (err == TIFFReadDirEntryErrOk)
             {
+                EvaluateIFDdatasizeReading(tif, dp);
                 if (!TIFFSetField(tif, dp->tdir_tag, data))
                     return (0);
             }
@@ -6254,6 +6434,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
             err = TIFFReadDirEntryDouble(tif, dp, &data);
             if (err == TIFFReadDirEntryErrOk)
             {
+                EvaluateIFDdatasizeReading(tif, dp);
                 if (!TIFFSetField(tif, dp->tdir_tag, data))
                     return (0);
             }
@@ -6267,6 +6448,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
             err = TIFFReadDirEntryIfd8(tif, dp, &data);
             if (err == TIFFReadDirEntryErrOk)
             {
+                EvaluateIFDdatasizeReading(tif, dp);
                 if (!TIFFSetField(tif, dp->tdir_tag, data))
                     return (0);
             }
@@ -6316,6 +6498,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 err = TIFFReadDirEntryByteArray(tif, dp, &data);
                 if (err == TIFFReadDirEntryErrOk)
                 {
+                    EvaluateIFDdatasizeReading(tif, dp);
                     int m;
                     m = TIFFSetField(tif, dp->tdir_tag, data);
                     if (data != 0)
@@ -6345,6 +6528,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 err = TIFFReadDirEntrySbyteArray(tif, dp, &data);
                 if (err == TIFFReadDirEntryErrOk)
                 {
+                    EvaluateIFDdatasizeReading(tif, dp);
                     int m;
                     m = TIFFSetField(tif, dp->tdir_tag, data);
                     if (data != 0)
@@ -6374,6 +6558,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 err = TIFFReadDirEntryShortArray(tif, dp, &data);
                 if (err == TIFFReadDirEntryErrOk)
                 {
+                    EvaluateIFDdatasizeReading(tif, dp);
                     int m;
                     m = TIFFSetField(tif, dp->tdir_tag, data);
                     if (data != 0)
@@ -6403,6 +6588,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 err = TIFFReadDirEntrySshortArray(tif, dp, &data);
                 if (err == TIFFReadDirEntryErrOk)
                 {
+                    EvaluateIFDdatasizeReading(tif, dp);
                     int m;
                     m = TIFFSetField(tif, dp->tdir_tag, data);
                     if (data != 0)
@@ -6432,6 +6618,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 err = TIFFReadDirEntryLongArray(tif, dp, &data);
                 if (err == TIFFReadDirEntryErrOk)
                 {
+                    EvaluateIFDdatasizeReading(tif, dp);
                     int m;
                     m = TIFFSetField(tif, dp->tdir_tag, data);
                     if (data != 0)
@@ -6461,6 +6648,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 err = TIFFReadDirEntrySlongArray(tif, dp, &data);
                 if (err == TIFFReadDirEntryErrOk)
                 {
+                    EvaluateIFDdatasizeReading(tif, dp);
                     int m;
                     m = TIFFSetField(tif, dp->tdir_tag, data);
                     if (data != 0)
@@ -6490,6 +6678,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 err = TIFFReadDirEntryLong8Array(tif, dp, &data);
                 if (err == TIFFReadDirEntryErrOk)
                 {
+                    EvaluateIFDdatasizeReading(tif, dp);
                     int m;
                     m = TIFFSetField(tif, dp->tdir_tag, data);
                     if (data != 0)
@@ -6519,6 +6708,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 err = TIFFReadDirEntrySlong8Array(tif, dp, &data);
                 if (err == TIFFReadDirEntryErrOk)
                 {
+                    EvaluateIFDdatasizeReading(tif, dp);
                     int m;
                     m = TIFFSetField(tif, dp->tdir_tag, data);
                     if (data != 0)
@@ -6548,6 +6738,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 err = TIFFReadDirEntryFloatArray(tif, dp, &data);
                 if (err == TIFFReadDirEntryErrOk)
                 {
+                    EvaluateIFDdatasizeReading(tif, dp);
                     int m;
                     m = TIFFSetField(tif, dp->tdir_tag, data);
                     if (data != 0)
@@ -6579,6 +6770,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 err = TIFFReadDirEntryDoubleArray(tif, dp, &data);
                 if (err == TIFFReadDirEntryErrOk)
                 {
+                    EvaluateIFDdatasizeReading(tif, dp);
                     int m;
                     m = TIFFSetField(tif, dp->tdir_tag, data);
                     if (data != 0)
@@ -6601,6 +6793,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 err = TIFFReadDirEntryByteArray(tif, dp, &data);
                 if (err == TIFFReadDirEntryErrOk)
                 {
+                    EvaluateIFDdatasizeReading(tif, dp);
                     int m;
                     if (data != 0 && dp->tdir_count > 0 &&
                         data[dp->tdir_count - 1] != '\0')
@@ -6651,6 +6844,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 err = TIFFReadDirEntryByteArray(tif, dp, &data);
                 if (err == TIFFReadDirEntryErrOk)
                 {
+                    EvaluateIFDdatasizeReading(tif, dp);
                     int m;
                     m = TIFFSetField(tif, dp->tdir_tag,
                                      (uint16_t)(dp->tdir_count), data);
@@ -6674,6 +6868,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 err = TIFFReadDirEntrySbyteArray(tif, dp, &data);
                 if (err == TIFFReadDirEntryErrOk)
                 {
+                    EvaluateIFDdatasizeReading(tif, dp);
                     int m;
                     m = TIFFSetField(tif, dp->tdir_tag,
                                      (uint16_t)(dp->tdir_count), data);
@@ -6697,6 +6892,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 err = TIFFReadDirEntryShortArray(tif, dp, &data);
                 if (err == TIFFReadDirEntryErrOk)
                 {
+                    EvaluateIFDdatasizeReading(tif, dp);
                     int m;
                     m = TIFFSetField(tif, dp->tdir_tag,
                                      (uint16_t)(dp->tdir_count), data);
@@ -6720,6 +6916,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 err = TIFFReadDirEntrySshortArray(tif, dp, &data);
                 if (err == TIFFReadDirEntryErrOk)
                 {
+                    EvaluateIFDdatasizeReading(tif, dp);
                     int m;
                     m = TIFFSetField(tif, dp->tdir_tag,
                                      (uint16_t)(dp->tdir_count), data);
@@ -6743,6 +6940,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 err = TIFFReadDirEntryLongArray(tif, dp, &data);
                 if (err == TIFFReadDirEntryErrOk)
                 {
+                    EvaluateIFDdatasizeReading(tif, dp);
                     int m;
                     m = TIFFSetField(tif, dp->tdir_tag,
                                      (uint16_t)(dp->tdir_count), data);
@@ -6766,6 +6964,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 err = TIFFReadDirEntrySlongArray(tif, dp, &data);
                 if (err == TIFFReadDirEntryErrOk)
                 {
+                    EvaluateIFDdatasizeReading(tif, dp);
                     int m;
                     m = TIFFSetField(tif, dp->tdir_tag,
                                      (uint16_t)(dp->tdir_count), data);
@@ -6789,6 +6988,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 err = TIFFReadDirEntryLong8Array(tif, dp, &data);
                 if (err == TIFFReadDirEntryErrOk)
                 {
+                    EvaluateIFDdatasizeReading(tif, dp);
                     int m;
                     m = TIFFSetField(tif, dp->tdir_tag,
                                      (uint16_t)(dp->tdir_count), data);
@@ -6812,6 +7012,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 err = TIFFReadDirEntrySlong8Array(tif, dp, &data);
                 if (err == TIFFReadDirEntryErrOk)
                 {
+                    EvaluateIFDdatasizeReading(tif, dp);
                     int m;
                     m = TIFFSetField(tif, dp->tdir_tag,
                                      (uint16_t)(dp->tdir_count), data);
@@ -6835,6 +7036,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 err = TIFFReadDirEntryFloatArray(tif, dp, &data);
                 if (err == TIFFReadDirEntryErrOk)
                 {
+                    EvaluateIFDdatasizeReading(tif, dp);
                     int m;
                     m = TIFFSetField(tif, dp->tdir_tag,
                                      (uint16_t)(dp->tdir_count), data);
@@ -6858,6 +7060,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 err = TIFFReadDirEntryDoubleArray(tif, dp, &data);
                 if (err == TIFFReadDirEntryErrOk)
                 {
+                    EvaluateIFDdatasizeReading(tif, dp);
                     int m;
                     m = TIFFSetField(tif, dp->tdir_tag,
                                      (uint16_t)(dp->tdir_count), data);
@@ -6881,6 +7084,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
                 err = TIFFReadDirEntryIfd8Array(tif, dp, &data);
                 if (err == TIFFReadDirEntryErrOk)
                 {
+                    EvaluateIFDdatasizeReading(tif, dp);
                     int m;
                     m = TIFFSetField(tif, dp->tdir_tag,
                                      (uint16_t)(dp->tdir_count), data);
@@ -6900,6 +7104,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
             err = TIFFReadDirEntryByteArray(tif, dp, &data);
             if (err == TIFFReadDirEntryErrOk)
             {
+                EvaluateIFDdatasizeReading(tif, dp);
                 int m;
                 if (data != 0 && dp->tdir_count > 0 &&
                     data[dp->tdir_count - 1] != '\0')
@@ -6970,6 +7175,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
             }
             if (err == TIFFReadDirEntryErrOk)
             {
+                EvaluateIFDdatasizeReading(tif, dp);
                 int m;
                 m = TIFFSetField(tif, dp->tdir_tag, count, data);
                 if (data != 0)
@@ -6987,6 +7193,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
             err = TIFFReadDirEntrySbyteArray(tif, dp, &data);
             if (err == TIFFReadDirEntryErrOk)
             {
+                EvaluateIFDdatasizeReading(tif, dp);
                 int m;
                 m = TIFFSetField(tif, dp->tdir_tag, (uint32_t)(dp->tdir_count),
                                  data);
@@ -7005,6 +7212,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
             err = TIFFReadDirEntryShortArray(tif, dp, &data);
             if (err == TIFFReadDirEntryErrOk)
             {
+                EvaluateIFDdatasizeReading(tif, dp);
                 int m;
                 m = TIFFSetField(tif, dp->tdir_tag, (uint32_t)(dp->tdir_count),
                                  data);
@@ -7023,6 +7231,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
             err = TIFFReadDirEntrySshortArray(tif, dp, &data);
             if (err == TIFFReadDirEntryErrOk)
             {
+                EvaluateIFDdatasizeReading(tif, dp);
                 int m;
                 m = TIFFSetField(tif, dp->tdir_tag, (uint32_t)(dp->tdir_count),
                                  data);
@@ -7041,6 +7250,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
             err = TIFFReadDirEntryLongArray(tif, dp, &data);
             if (err == TIFFReadDirEntryErrOk)
             {
+                EvaluateIFDdatasizeReading(tif, dp);
                 int m;
                 m = TIFFSetField(tif, dp->tdir_tag, (uint32_t)(dp->tdir_count),
                                  data);
@@ -7059,6 +7269,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
             err = TIFFReadDirEntrySlongArray(tif, dp, &data);
             if (err == TIFFReadDirEntryErrOk)
             {
+                EvaluateIFDdatasizeReading(tif, dp);
                 int m;
                 m = TIFFSetField(tif, dp->tdir_tag, (uint32_t)(dp->tdir_count),
                                  data);
@@ -7077,6 +7288,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
             err = TIFFReadDirEntryLong8Array(tif, dp, &data);
             if (err == TIFFReadDirEntryErrOk)
             {
+                EvaluateIFDdatasizeReading(tif, dp);
                 int m;
                 m = TIFFSetField(tif, dp->tdir_tag, (uint32_t)(dp->tdir_count),
                                  data);
@@ -7095,6 +7307,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
             err = TIFFReadDirEntrySlong8Array(tif, dp, &data);
             if (err == TIFFReadDirEntryErrOk)
             {
+                EvaluateIFDdatasizeReading(tif, dp);
                 int m;
                 m = TIFFSetField(tif, dp->tdir_tag, (uint32_t)(dp->tdir_count),
                                  data);
@@ -7113,6 +7326,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
             err = TIFFReadDirEntryFloatArray(tif, dp, &data);
             if (err == TIFFReadDirEntryErrOk)
             {
+                EvaluateIFDdatasizeReading(tif, dp);
                 int m;
                 m = TIFFSetField(tif, dp->tdir_tag, (uint32_t)(dp->tdir_count),
                                  data);
@@ -7131,6 +7345,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
             err = TIFFReadDirEntryDoubleArray(tif, dp, &data);
             if (err == TIFFReadDirEntryErrOk)
             {
+                EvaluateIFDdatasizeReading(tif, dp);
                 int m;
                 m = TIFFSetField(tif, dp->tdir_tag, (uint32_t)(dp->tdir_count),
                                  data);
@@ -7149,6 +7364,7 @@ static int TIFFFetchNormalTag(TIFF *tif, TIFFDirEntry *dp, int recover)
             err = TIFFReadDirEntryIfd8Array(tif, dp, &data);
             if (err == TIFFReadDirEntryErrOk)
             {
+                EvaluateIFDdatasizeReading(tif, dp);
                 int m;
                 m = TIFFSetField(tif, dp->tdir_tag, (uint32_t)(dp->tdir_count),
                                  data);
diff --git a/libtiff/tif_dirwrite.c b/libtiff/tif_dirwrite.c
index d8844bbd..02f7fc11 100644
--- a/libtiff/tif_dirwrite.c
+++ b/libtiff/tif_dirwrite.c
@@ -303,12 +303,14 @@ int TIFFWriteCustomDirectory(TIFF *tif, uint64_t *pdiroff)
 }
 
 /*
- * Similar to TIFFWriteDirectory(

(Patch may be truncated, please check the link at the top of this post.)