libtiff: Merge branch 'fix_CurDir_wrong_incrementing' into 'master'

From 5eb068a4cd11a5f8d05fb3fbe2399aac55e4e837 Mon Sep 17 00:00:00 2001
From: Su Laus <[EMAIL REDACTED]>
Date: Sun, 11 Aug 2024 15:59:39 +0000
Subject: [PATCH] Fix cases where tif_curdir is set incorrectly

Fix cases where the current directory number (tif_curdir) is set inconsistently or incorrectly, depending on the previous history.

See additional checks and tests in test/test_directory.c of this MR for intended setting of tif_curdir (i.e. TIFFCurrentDirectory(tif)).
---
 libtiff/tif_dir.c      |  44 ++-
 libtiff/tif_dir.h      |   3 +
 libtiff/tif_dirread.c  |   8 +-
 libtiff/tif_dirwrite.c | 135 +++++--
 libtiff/tif_open.c     |   4 +
 libtiff/tiffiop.h      |  32 +-
 test/test_directory.c  | 877 ++++++++++++++++++++++++++++++++++++++++-
 7 files changed, 1048 insertions(+), 55 deletions(-)

diff --git a/libtiff/tif_dir.c b/libtiff/tif_dir.c
index a491097b..459f9696 100644
--- a/libtiff/tif_dir.c
+++ b/libtiff/tif_dir.c
@@ -1662,6 +1662,7 @@ void TIFFFreeDirectory(TIFF *tif)
         tif->tif_dir.td_dirdatasize_offsets = NULL;
         tif->tif_dir.td_dirdatasize_Noffsets = 0;
     }
+    tif->tif_dir.td_iswrittentofile = FALSE;
 }
 #undef CleanupField
 
@@ -1694,6 +1695,7 @@ int TIFFCreateDirectory(TIFF *tif)
     tif->tif_curoff = 0;
     tif->tif_row = (uint32_t)-1;
     tif->tif_curstrip = (uint32_t)-1;
+    tif->tif_dir.td_iswrittentofile = FALSE;
 
     return 0;
 }
@@ -2042,6 +2044,8 @@ tdir_t TIFFNumberOfDirectories(TIFF *tif)
     {
         ++n;
     }
+    /* Update number of main-IFDs in file. */
+    tif->tif_curdircount = n;
     return (n);
 }
 
@@ -2124,7 +2128,19 @@ int TIFFSetDirectory(TIFF *tif, tdir_t dirn)
         tif->tif_curdir = TIFF_NON_EXISTENT_DIR_NUMBER;
     else
         tif->tif_curdir--;
-    return (TIFFReadDirectory(tif));
+
+    tdir_t curdir = tif->tif_curdir;
+
+    int retval = TIFFReadDirectory(tif);
+
+    if (!retval && tif->tif_curdir == curdir)
+    {
+        /* If tif_curdir has not be incremented, TIFFFetchDirectory() in
+         * TIFFReadDirectory() has failed and tif_curdir shall be set
+         * specifically. */
+        tif->tif_curdir = TIFF_NON_EXISTENT_DIR_NUMBER;
+    }
+    return (retval);
 }
 
 /*
@@ -2149,8 +2165,11 @@ int TIFFSetSubDirectory(TIFF *tif, uint64_t diroff)
     int8_t probablySubIFD = 0;
     if (diroff == 0)
     {
-        /* Special case to invalidate the tif_lastdiroff member. */
+        /* Special case to set tif_diroff=0, which is done in
+         * TIFFReadDirectory() below to indicate that the currently read IFD is
+         * treated as a new, fresh IFD. */
         tif->tif_curdir = TIFF_NON_EXISTENT_DIR_NUMBER;
+        tif->tif_dir.td_iswrittentofile = FALSE;
     }
     else
     {
@@ -2165,24 +2184,25 @@ int TIFFSetSubDirectory(TIFF *tif, uint64_t diroff)
         else
             tif->tif_curdir = TIFF_NON_EXISTENT_DIR_NUMBER;
     }
+    curdir = tif->tif_curdir;
 
     tif->tif_nextdiroff = diroff;
     retval = TIFFReadDirectory(tif);
-    /* If failed, curdir was not incremented in TIFFReadDirectory(), so set it
-     * back, but leave it for diroff==0. */
-    if (!retval && diroff != 0)
+
+    /* tif_curdir is incremented in TIFFReadDirectory(), but if it has not been
+     * incremented, TIFFFetchDirectory() has failed there and tif_curdir shall
+     * be set specifically. */
+    if (!retval && diroff != 0 && tif->tif_curdir == curdir)
     {
-        if (tif->tif_curdir == TIFF_NON_EXISTENT_DIR_NUMBER)
-            tif->tif_curdir = 0;
-        else
-            tif->tif_curdir++;
+        tif->tif_curdir = TIFF_NON_EXISTENT_DIR_NUMBER;
     }
+
     if (probablySubIFD)
     {
         if (retval)
         {
             /* Reset IFD list to start new one for SubIFD chain and also start
-             * SubIFD chain with tif_curdir=0. */
+             * SubIFD chain with tif_curdir=0 for IFD loop checking. */
             /* invalidate IFD loop lists */
             _TIFFCleanupIFDOffsetAndNumberMaps(tif);
             tif->tif_curdir = 0; /* first directory of new chain */
@@ -2336,6 +2356,10 @@ int TIFFUnlinkDirectory(TIFF *tif, tdir_t dirn)
     tif->tif_row = (uint32_t)-1;
     tif->tif_curstrip = (uint32_t)-1;
     tif->tif_curdir = TIFF_NON_EXISTENT_DIR_NUMBER;
+    if (tif->tif_curdircount > 0)
+        tif->tif_curdircount--;
+    else
+        tif->tif_curdircount = TIFF_NON_EXISTENT_DIR_NUMBER;
     _TIFFCleanupIFDOffsetAndNumberMaps(tif); /* invalidate IFD loop lists */
     return (1);
 }
diff --git a/libtiff/tif_dir.h b/libtiff/tif_dir.h
index f9558b62..f4182a2c 100644
--- a/libtiff/tif_dir.h
+++ b/libtiff/tif_dir.h
@@ -145,6 +145,9 @@ typedef struct
     unsigned char
         td_deferstrilearraywriting; /* see TIFFDeferStrileArrayWriting() */
 
+    unsigned char
+        td_iswrittentofile; /* indicates if current IFD is present on file */
+
     /* LibTIFF writes all data that does not fit into the IFD entries directly
      * after the IFD tag entry part. When reading, only the IFD data directly
      * and continuously behind the IFD tags is taken into account for the IFD
diff --git a/libtiff/tif_dirread.c b/libtiff/tif_dirread.c
index 4305a512..b360deae 100644
--- a/libtiff/tif_dirread.c
+++ b/libtiff/tif_dirread.c
@@ -4281,6 +4281,7 @@ int TIFFReadDirectory(TIFF *tif)
         tif->tif_curdir = 0;
     else
         tif->tif_curdir++;
+
     (*tif->tif_cleanup)(tif); /* cleanup any previous compression state */
 
     TIFFReadDirectoryCheckOrder(tif, dir, dircount);
@@ -4314,6 +4315,11 @@ int TIFFReadDirectory(TIFF *tif)
     TIFFFreeDirectory(tif);
     TIFFDefaultDirectory(tif);
 
+    /* After setup a fresh directory indicate that now active IFD is also
+     * present on file, even if its entries could not be read successfully
+     * below.  */
+    tif->tif_dir.td_iswrittentofile = TRUE;
+
     /* 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 =
@@ -5191,7 +5197,7 @@ int TIFFReadDirectory(TIFF *tif)
     if (dir)
         _TIFFfreeExt(tif, dir);
     return (0);
-}
+} /*-- TIFFReadDirectory() --*/
 
 static void TIFFReadDirectoryCheckOrder(TIFF *tif, TIFFDirEntry *dir,
                                         uint16_t dircount)
diff --git a/libtiff/tif_dirwrite.c b/libtiff/tif_dirwrite.c
index 7ebcbb26..48407a3b 100644
--- a/libtiff/tif_dirwrite.c
+++ b/libtiff/tif_dirwrite.c
@@ -1185,17 +1185,12 @@ static int TIFFWriteDirectorySec(TIFF *tif, int isimage, int imagedone,
         }
         if (tif->tif_dataoff & 1)
             tif->tif_dataoff++;
-        if (isimage)
-        {
-            if (tif->tif_curdir == TIFF_NON_EXISTENT_DIR_NUMBER)
-                tif->tif_curdir = 0;
-            else
-                tif->tif_curdir++;
-        }
     } /* while() */
     if (isimage)
     {
-        /* For SubIFDs remember offset of SubIFD tag within main IFD. */
+        /* For SubIFDs remember offset of SubIFD tag within main IFD.
+         * However, might be already done in TIFFWriteDirectoryTagSubifd() if
+         * there are more than one SubIFD. */
         if (TIFFFieldSet(tif, FIELD_SUBIFD) && (tif->tif_subifdoff == 0))
         {
             uint32_t na;
@@ -1297,7 +1292,8 @@ static int TIFFWriteDirectorySec(TIFF *tif, int isimage, int imagedone,
     dir = NULL;
     if (!SeekOK(tif, tif->tif_diroff))
     {
-        TIFFErrorExtR(tif, module, "IO error writing directory");
+        TIFFErrorExtR(tif, module,
+                      "IO error writing directory at seek to offset");
         goto bad;
     }
     if (!WriteOK(tif, dirmem, (tmsize_t)dirsize))
@@ -1306,6 +1302,68 @@ static int TIFFWriteDirectorySec(TIFF *tif, int isimage, int imagedone,
         goto bad;
     }
     _TIFFfreeExt(tif, dirmem);
+
+    /* Increment tif_curdir if IFD wasn't already written to file and no error
+     * occurred during IFD writing above. */
+    if (isimage && !tif->tif_dir.td_iswrittentofile)
+    {
+        if (!((tif->tif_flags & TIFF_INSUBIFD) &&
+              !(TIFFFieldSet(tif, FIELD_SUBIFD))))
+        {
+            /*-- Normal main-IFD case --*/
+            if (tif->tif_curdircount != TIFF_NON_EXISTENT_DIR_NUMBER)
+            {
+                tif->tif_curdir = tif->tif_curdircount;
+            }
+            else
+            {
+                /*ToDo SU: NEW_IFD_CURDIR_INCREMENTING:  Delete this
+                 * unexpected case after some testing time. */
+                /* Attention: tif->tif_curdircount is already set within
+                 * TIFFNumberOfDirectories() */
+                tif->tif_curdircount = TIFFNumberOfDirectories(tif);
+                tif->tif_curdir = tif->tif_curdircount;
+                TIFFErrorExtR(
+                    tif, module,
+                    "tif_curdircount is TIFF_NON_EXISTENT_DIR_NUMBER, "
+                    "not expected !! Line %d",
+                    __LINE__);
+                goto bad;
+            }
+        }
+        else
+        {
+            /*-- SubIFD case -- */
+            /* tif_curdir is always set to 0 for all SubIFDs. */
+            tif->tif_curdir = 0;
+        }
+    }
+    /* Increment tif_curdircount only if main-IFD of an image was not already
+     * present on file. */
+    /* Check in combination with (... && !(TIFFFieldSet(tif, FIELD_SUBIFD)))
+     * is necessary here because TIFF_INSUBIFD was already set above for the
+     * next SubIFD when this main-IFD (with FIELD_SUBIFD) is currently being
+     * written. */
+    if (isimage && !tif->tif_dir.td_iswrittentofile &&
+        !((tif->tif_flags & TIFF_INSUBIFD) &&
+          !(TIFFFieldSet(tif, FIELD_SUBIFD))))
+        tif->tif_curdircount++;
+
+    tif->tif_dir.td_iswrittentofile = TRUE;
+
+    /* Reset SubIFD writing stage after last SubIFD has been written. */
+    if (imagedone && (tif->tif_flags & TIFF_INSUBIFD) && tif->tif_nsubifd == 0)
+        tif->tif_flags &= ~TIFF_INSUBIFD;
+
+    /* Add or update this directory to the IFD list. */
+    if (!_TIFFCheckDirNumberAndOffset(tif, tif->tif_curdir, tif->tif_diroff))
+    {
+        TIFFErrorExtR(tif, module,
+                      "Starting directory %u at offset 0x%" PRIx64 " (%" PRIu64
+                      ") might cause an IFD loop",
+                      tif->tif_curdir, tif->tif_diroff, tif->tif_diroff);
+    }
+
     if (imagedone)
     {
         TIFFFreeDirectory(tif);
@@ -1317,9 +1375,8 @@ static int TIFFWriteDirectorySec(TIFF *tif, int isimage, int imagedone,
     }
     else
     {
-        /* IFD is only checkpointed to file, thus set IFD data size written to
-         * file.
-         */
+        /* IFD is only checkpointed to file (or a custom IFD like EXIF is
+         * written), thus set IFD data size written to file. */
         tif->tif_dir.td_dirdatasize_read = tif->tif_dir.td_dirdatasize_write;
     }
     return (1);
@@ -3073,15 +3130,15 @@ static int TIFFLinkDirectory(TIFF *tif)
                               "Error writing SubIFD directory link");
                 return (0);
             }
+
             /*
              * Advance to the next SubIFD or, if this is
-             * the last one configured, revert back to the
-             * normal directory linkage.
+             * the last one configured, reverting back to the
+             * normal directory linkage is done in TIFFWriteDirectorySec()
+             * by tif->tif_flags &= ~TIFF_INSUBIFD;.
              */
             if (--tif->tif_nsubifd)
                 tif->tif_subifdoff += 4;
-            else
-                tif->tif_flags &= ~TIFF_INSUBIFD;
             return (1);
         }
         else
@@ -3097,19 +3154,23 @@ static int TIFFLinkDirectory(TIFF *tif)
                               "Error writing SubIFD directory link");
                 return (0);
             }
+
             /*
              * Advance to the next SubIFD or, if this is
-             * the last one configured, revert back to the
-             * normal directory linkage.
+             * the last one configured, reverting back to the
+             * normal directory linkage is done in TIFFWriteDirectorySec()
+             * by tif->tif_flags &= ~TIFF_INSUBIFD;.
              */
             if (--tif->tif_nsubifd)
                 tif->tif_subifdoff += 8;
-            else
-                tif->tif_flags &= ~TIFF_INSUBIFD;
             return (1);
         }
     }
 
+    /*
+     * Handle main-IFDs
+     */
+    tdir_t ndir = 1; /* count current number of main-IFDs */
     if (!(tif->tif_flags & TIFF_BIGTIFF))
     {
         uint32_t m;
@@ -3130,18 +3191,26 @@ static int TIFFLinkDirectory(TIFF *tif)
                 TIFFErrorExtR(tif, tif->tif_name, "Error writing TIFF header");
                 return (0);
             }
+            if (!tif->tif_dir.td_iswrittentofile)
+                tif->tif_curdircount = 0;
             return (1);
         }
         /*
          * Not the first directory, search to the last and append.
          */
-        if (tif->tif_lastdiroff != 0)
+        tdir_t dirn = -1;
+        if (tif->tif_lastdiroff != 0 &&
+            _TIFFGetDirNumberFromOffset(tif, tif->tif_lastdiroff, &dirn))
         {
+            /* Start searching from the lastely written IFD. Thus get its IFD
+             * number. */
             nextdir = (uint32_t)tif->tif_lastdiroff;
+            ndir = dirn + 1;
         }
         else
         {
             nextdir = tif->tif_header.classic.tiff_diroff;
+            ndir = 1; /* start searching from the first IFD */
         }
 
         while (1)
@@ -3176,10 +3245,12 @@ static int TIFFLinkDirectory(TIFF *tif)
                 break;
             }
             nextdir = nextnextdir;
+            ndir++;
         }
     }
     else
     {
+        /*- BigTIFF -*/
         uint64_t m;
         uint64_t nextdir;
         m = tif->tif_diroff;
@@ -3198,18 +3269,26 @@ static int TIFFLinkDirectory(TIFF *tif)
                 TIFFErrorExtR(tif, tif->tif_name, "Error writing TIFF header");
                 return (0);
             }
+            if (!tif->tif_dir.td_iswrittentofile)
+                tif->tif_curdircount = 0;
             return (1);
         }
         /*
          * Not the first directory, search to the last and append.
          */
-        if (tif->tif_lastdiroff != 0)
+        tdir_t dirn = -1;
+        if (tif->tif_lastdiroff != 0 &&
+            _TIFFGetDirNumberFromOffset(tif, tif->tif_lastdiroff, &dirn))
         {
+            /* Start searching from the lastely written IFD. Thus get its IFD
+             * number. */
             nextdir = tif->tif_lastdiroff;
+            ndir = dirn + 1;
         }
         else
         {
             nextdir = tif->tif_header.big.tiff_diroff;
+            ndir = 1; /* start searching from the first IFD */
         }
         while (1)
         {
@@ -3252,8 +3331,20 @@ static int TIFFLinkDirectory(TIFF *tif)
                 break;
             }
             nextdir = nextnextdir;
+            ndir++;
         }
     }
+    /* Offset of next IFD is written to file.
+     * Update number of main-IFDs in file.
+     * However, tif_curdircount shall count only newly written main-IFDs with
+     * entries and not only number of linked offsets! Thus, tif_curdircount is
+     * incremented at the end of TIFFWriteDirectorySec().
+     * TIFF_NON_EXISTENT_DIR_NUMBER means 'dont know number of IFDs'
+     * 0 means 'empty file opened for writing, but no IFD written yet' */
+    if (!tif->tif_dir.td_iswrittentofile && !(tif->tif_flags & TIFF_INSUBIFD))
+    {
+        tif->tif_curdircount = ndir;
+    }
     return (1);
 }
 
diff --git a/libtiff/tif_open.c b/libtiff/tif_open.c
index b18e8948..56330d77 100644
--- a/libtiff/tif_open.c
+++ b/libtiff/tif_open.c
@@ -366,6 +366,7 @@ TIFF *TIFFClientOpenExt(const char *name, const char *mode,
     strcpy(tif->tif_name, name);
     tif->tif_mode = m & ~(O_CREAT | O_TRUNC);
     tif->tif_curdir = TIFF_NON_EXISTENT_DIR_NUMBER; /* non-existent directory */
+    tif->tif_curdircount = TIFF_NON_EXISTENT_DIR_NUMBER;
     tif->tif_curoff = 0;
     tif->tif_curstrip = (uint32_t)-1; /* invalid strip */
     tif->tif_row = (uint32_t)-1;      /* read/write pre-increment */
@@ -607,6 +608,9 @@ TIFF *TIFFClientOpenExt(const char *name, const char *mode,
         tif->tif_diroff = 0;
         tif->tif_lastdiroff = 0;
         tif->tif_setdirectory_force_absolute = FALSE;
+        /* tif_curdircount = 0 means 'empty file opened for writing, but no IFD
+         * written yet' */
+        tif->tif_curdircount = 0;
         return (tif);
     }
 
diff --git a/libtiff/tiffiop.h b/libtiff/tiffiop.h
index 803ca98c..c4348206 100644
--- a/libtiff/tiffiop.h
+++ b/libtiff/tiffiop.h
@@ -163,12 +163,32 @@ struct tiff
     TIFFHeaderUnion tif_header; /* file's header block Classic/BigTIFF union */
     uint16_t tif_header_size;   /* file's header block and its length */
     uint32_t tif_row;           /* current scanline */
-    tdir_t tif_curdir;          /* current directory (index) */
-    uint32_t tif_curstrip;      /* current strip for read/write */
-    uint64_t tif_curoff;        /* current offset for read/write */
-    uint64_t tif_lastvalidoff;  /* last valid offset allowed for rewrite in
-                                   place. Used only by TIFFAppendToStrip() */
-    uint64_t tif_dataoff;       /* current offset for writing dir (IFD) */
+
+    /* There are IFDs in the file and an "active" IFD in memory,
+     * from which fields are "set" and "get".
+     * tif_curdir is set to:
+     *   a) TIFF_NON_EXISTENT_DIR_NUMBER if there is no IFD in the file
+     *      or the state is unknown,
+     *      or the last read (i.e. TIFFFetchDirectory()) failed,
+     *      or a custom directory was written.
+     *   b) IFD index of last IFD written in the file. In this case the
+     *      active IFD is a new (empty) one and tif_diroff is zero.
+     *      If writing fails, tif_curdir is not changed.
+     *   c) IFD index of IFD read from file into memory (=active IFD),
+     *      even if IFD is corrupt and TIFFReadDirectory() returns 0.
+     *      Then tif_diroff contains the offset of the IFD in the file.
+     *   d) IFD index 0, whenever a custom directory or an unchained SubIFD
+     *      was read. */
+    tdir_t tif_curdir; /* current directory (index) */
+    /* tif_curdircount: number of directories (main-IFDs) in file:
+     * - TIFF_NON_EXISTENT_DIR_NUMBER means 'dont know number of IFDs'.
+     * - 0 means 'empty file opened for writing, but no IFD written yet' */
+    tdir_t tif_curdircount;
+    uint32_t tif_curstrip;     /* current strip for read/write */
+    uint64_t tif_curoff;       /* current offset for read/write */
+    uint64_t tif_lastvalidoff; /* last valid offset allowed for rewrite in
+                                  place. Used only by TIFFAppendToStrip() */
+    uint64_t tif_dataoff;      /* current offset for writing dir (IFD) */
     /* SubIFD support */
     uint16_t tif_nsubifd;   /* remaining subifds to write */
     uint64_t tif_subifdoff; /* offset for patching SubIFD link */
diff --git a/test/test_directory.c b/test/test_directory.c
index b8ba8d2f..a50096a4 100644
--- a/test/test_directory.c
+++ b/test/test_directory.c
@@ -30,6 +30,10 @@
  * Furthermore, tests are performed with big-endian, little-endian and BigTIFF
  * images.
  *
+ * Furthermore the correctness of tif_curdir = TIFFCurrentDirectory() when
+ * moving though a multi-IFD file and creating new IFDs, overwriting or
+ * re-writing IFDs as well for reading and writing SubIFDs is tested.
+ *
  */
 
 #include "tif_config.h"
@@ -59,6 +63,51 @@ char *openModeStrings[] = {"wl", "wb", "w8l", "w8b"};
 char *openModeText[] = {"non-BigTIFF and LE", "non-BigTIFF and BE",
                         "BigTIFF and LE", "BigTIFF and BE"};
 
+/* Some functions and macros to get more readable test code. */
+int CheckCurDirNum(TIFF *tif, tdir_t expected_dirnum, int line)
+{
+
+    tdir_t curdir = TIFFCurrentDirectory(tif);
+    if (curdir != expected_dirnum)
+    {
+        fprintf(stderr,
+                "Error: curdir %d is different to expected one %d at line %d\n",
+                curdir, expected_dirnum, line);
+        return 1;
+    }
+    return 0;
+}
+
+#define TIFFWriteDirectory_M(tif, filename, line)                              \
+    if (!TIFFWriteDirectory(tif))                                              \
+    {                                                                          \
+        fprintf(stderr, "Can't write directory to %s at line %d\n", filename,  \
+                line);                                                         \
+        goto failure;                                                          \
+    }
+
+#define TIFFCheckpointDirectory_M(tif, dirnum, filename, line)                 \
+    if (!TIFFCheckpointDirectory(tif))                                         \
+    {                                                                          \
+        fprintf(stderr, "Can't checkpoint directory %d of %s at line %d\n",    \
+                dirnum, filename, line);                                       \
+        goto failure;                                                          \
+    }
+
+#define TIFFSetDirectory_M(tif, dirnum, filename, line)                        \
+    if (!TIFFSetDirectory(tif, dirnum))                                        \
+    {                                                                          \
+        fprintf(stderr, "Can't set directory %d of %s at line %d\n", dirnum,   \
+                filename, line);                                               \
+        goto failure;                                                          \
+    }
+
+#define CHECKCURDIRNUM_M(tif, x, line)                                         \
+    if (CheckCurDirNum(tif, x, line))                                          \
+    {                                                                          \
+        goto failure;                                                          \
+    }
+
 /* Writes basic tags to current directory (IFD) as well one pixel to the file.
  * For is_corrupted = TRUE a corrupted IFD (missing image width tag) is
  * generated. */
@@ -180,9 +229,10 @@ int count_directories(const char *filename, int *count)
 
     do
     {
+        CHECKCURDIRNUM_M(tif, (tdir_t)(*count), __LINE__);
         (*count)++;
     } while (TIFFReadDirectory(tif));
-
+failure:
     TIFFClose(tif);
     return 0;
 }
@@ -303,6 +353,7 @@ int test_arbitrary_directrory_loading(unsigned int openMode)
     TIFF *tif;
     uint64_t offsets_base[N_DIRECTORIES];
     int expected_original_dirnumber;
+    tdir_t expected_curdir = (tdir_t)(-1);
 
     if (openMode >= (sizeof(openModeStrings) / sizeof(openModeStrings[0])))
     {
@@ -324,6 +375,7 @@ int test_arbitrary_directrory_loading(unsigned int openMode)
         return 1;
     }
     TIFFSetDirectory(tif, 0);
+    CHECKCURDIRNUM_M(tif, expected_curdir, __LINE__);
     for (int i = 0; i < N_DIRECTORIES; i++)
     {
         if (write_data_to_current_directory(tif, i, false))
@@ -337,11 +389,15 @@ int test_arbitrary_directrory_loading(unsigned int openMode)
             fprintf(stderr, "Can't write directory to %s\n", filename);
             goto failure;
         }
+        expected_curdir++;
+        /* Fails in 4.6.0 */
+        CHECKCURDIRNUM_M(tif, expected_curdir, __LINE__);
         if (i >= 2 && i <= 4)
         {
             if (i == 3)
             {
-                /* Invalidate directory - TIFFSetSubDirectory() will fail */
+                /* Invalidate directory - TIFFSetSubDirectory() will fail.
+                 * The next TIFFSetDirectory(i) will revoke this action.*/
                 if (TIFFSetSubDirectory(tif, 0))
                 {
                     fprintf(stderr,
@@ -362,7 +418,10 @@ int test_arbitrary_directrory_loading(unsigned int openMode)
             }
             if (i == 4)
             {
-                /* Invalidate directory - TIFFSetSubDirectory() will fail */
+                /* Invalidate directory - TIFFSetSubDirectory() will fail.
+                 * This time, tif_curdir keeps set to -1 and counts now again
+                 * from 0, despite the directory number in the file is equal
+                 * "i". */
                 if (TIFFSetSubDirectory(tif, 0))
                 {
                     fprintf(stderr,
@@ -413,6 +472,7 @@ int test_arbitrary_directrory_loading(unsigned int openMode)
                 N_DIRECTORIES + 1, filename);
         goto failure;
     }
+    CHECKCURDIRNUM_M(tif, N_DIRECTORIES - 1, __LINE__);
 
     /* Test very fast  TIFFSetDirectory() using IFD loop directory list.
      * First populate IFD loop directory list and then go through directories in
@@ -425,8 +485,10 @@ int test_arbitrary_directrory_loading(unsigned int openMode)
             fprintf(stderr, "Can't set %d.th directory from %s\n", i, filename);
             goto failure;
         }
+        CHECKCURDIRNUM_M(tif, i, __LINE__);
     }
     TIFFReadDirectory(tif);
+    CHECKCURDIRNUM_M(tif, N_DIRECTORIES - 1, __LINE__);
     for (int i = N_DIRECTORIES - 1; i >= 0; i--)
     {
         if (!TIFFSetDirectory(tif, i))
@@ -438,6 +500,7 @@ int test_arbitrary_directrory_loading(unsigned int openMode)
         {
             goto failure;
         }
+        CHECKCURDIRNUM_M(tif, i, __LINE__);
     }
 
     /* Test not existing directory number */
@@ -449,6 +512,7 @@ int test_arbitrary_directrory_loading(unsigned int openMode)
                 N_DIRECTORIES, filename);
         goto failure;
     }
+    CHECKCURDIRNUM_M(tif, 0, __LINE__);
 
     /* Close and Reopen prepared testfile */
     TIFFClose(tif);
@@ -471,6 +535,7 @@ int test_arbitrary_directrory_loading(unsigned int openMode)
         {
             goto failure;
         }
+        CHECKCURDIRNUM_M(tif, i, __LINE__);
     }
 
     /* More specialized test cases for relative seeking within TIFFSetDirectory.
@@ -481,6 +546,7 @@ int test_arbitrary_directrory_loading(unsigned int openMode)
         fprintf(stderr, "Can't set directory %d within %s\n", 2, filename);
         goto failure;
     }
+    CHECKCURDIRNUM_M(tif, 2, __LINE__);
     uint64_t off2 = TIFFCurrentDirOffset(tif);
     /* Note that dirnum = 2 is deleted here since TIFFUnlinkDirectory()
      * starts with 1 instead of 0. */
@@ -500,6 +566,7 @@ int test_arbitrary_directrory_loading(unsigned int openMode)
         goto failure;
     }
     /*Check if correct directory is loaded */
+    CHECKCURDIRNUM_M(tif, 0, __LINE__);
     expected_original_dirnumber = 2;
     if (!is_requested_directory(tif, expected_original_dirnumber, filename))
     {
@@ -512,6 +579,7 @@ int test_arbitrary_directrory_loading(unsigned int openMode)
         fprintf(stderr, "Can't set new directory %d within %s\n", 3, filename);
         goto failure;
     }
+    CHECKCURDIRNUM_M(tif, 3, __LINE__);
     expected_original_dirnumber = 4;
     if (!is_requested_directory(tif, expected_original_dirnumber, filename))
     {
@@ -544,6 +612,7 @@ int test_arbitrary_directrory_loading(unsigned int openMode)
                 2, filename);
         goto failure;
     }
+    CHECKCURDIRNUM_M(tif, 2, __LINE__);
     /* which should now be the previous dir-3. */
     expected_original_dirnumber = 4;
     if (!is_requested_directory(tif, expected_original_dirnumber, filename))
@@ -551,8 +620,11 @@ int test_arbitrary_directrory_loading(unsigned int openMode)
         goto failure;
     }
 
-    /* Check, if third original directory could be loaded and the following,
-     * still chained one. This is like for a SubIFD. */
+    /* Check, if third original directory, which is unlinked, could be loaded
+     * and the following, still chained one. This is like for a SubIFD.
+     * tif_curdir will be set to 0 because deleted IFD is not in main sequence
+     * and then incremented to 1 when reading the still chained next IFD using
+     * TIFFReadDirectory(). */
     if (!TIFFSetSubDirectory(tif, offsets_base[2]))
     {
         fprintf(stderr,
@@ -561,6 +633,7 @@ int test_arbitrary_directrory_loading(unsigned int openMode)
                 offsets_base[2], offsets_base[2], filename);
         goto failure;
     }
+    CHECKCURDIRNUM_M(tif, 0, __LINE__);
     if (!TIFFReadDirectory(tif))
     {
         fprintf(stderr,
@@ -572,6 +645,7 @@ int test_arbitrary_directrory_loading(unsigned int openMode)
     /* Check if correct directory is loaded, which was unlinked the second
      * time.
      */
+    CHECKCURDIRNUM_M(tif, 1, __LINE__);
     expected_original_dirnumber = 3;
     if (!is_requested_directory(tif, expected_original_dirnumber, filename))
     {
@@ -589,6 +663,7 @@ int test_arbitrary_directrory_loading(unsigned int openMode)
                 offsets_base[2], offsets_base[2], filename);
         goto failure;
     }
+    CHECKCURDIRNUM_M(tif, 0, __LINE__);
     if (!TIFFSetDirectory(tif, 3))
     {
         fprintf(stderr, "Can't set new directory %d within %s\n", 3, filename);
@@ -600,6 +675,7 @@ int test_arbitrary_directrory_loading(unsigned int openMode)
                 3, filename);
         goto failure;
     }
+    CHECKCURDIRNUM_M(tif, 3, __LINE__);
     /*Check if correct directory is loaded. Because two original IFDs are
      * unlinked / missing, the original dirnumber is now 5. */
     expected_original_dirnumber = 5;
@@ -653,11 +729,13 @@ int test_arbitrary_directrory_loading(unsigned int openMode)
      * Furthermore, this test checks that TIFFUnlinkDirectory() can unlink
      * the first directory dirnum = 0 and a following TIFFSetDirectory(0)
      * does not load the unlinked directory. */
+    CHECKCURDIRNUM_M(tif, 3, __LINE__);
     if (!TIFFUnlinkDirectory(tif, 1))
     {
         fprintf(stderr, "Can't unlink directory %d within %s\n", 0, filename);
         goto failure;
     }
+    CHECKCURDIRNUM_M(tif, (tdir_t)(-1), __LINE__);
     /* Now three directories are missing (0,2,3) and thus directory 0 is
      * original directory 1 and directory 2 is original directory 5. */
     if (!TIFFSetDirectory(tif, 0))
@@ -668,6 +746,7 @@ int test_arbitrary_directrory_loading(unsigned int openMode)
                 0, filename);
         goto failure;
     }
+    CHECKCURDIRNUM_M(tif, 0, __LINE__);
     expected_original_dirnumber = 1;
     if (!is_requested_directory(tif, expected_original_dirnumber, filename))
     {
@@ -690,11 +769,15 @@ int test_arbitrary_directrory_loading(unsigned int openMode)
     /* TIFFUnlinkDirectory(0) is not allowed, because dirnum starts for
      * this function with 1 instead of 0.
      * An error return is expected here. */
+    CHECKCURDIRNUM_M(tif, 2, __LINE__);
+    fprintf(stderr, "----- Expect error messages about 'TIFFUnlinkDirectory() "
+                    "first directory starts with number 1 and not 0.' -----\n");
     if (TIFFUnlinkDirectory(tif, 0))
     {
         fprintf(stderr, "TIFFUnlinkDirectory(0) did not return an error.\n");
         goto failure;
     }
+    CHECKCURDIRNUM_M(tif, 2, __LINE__);
 
     TIFFClose(tif);
     unlink(filename);
@@ -717,7 +800,7 @@ int test_SubIFD_directrory_handling(unsigned int openMode)
     char filename[128] = {0};
 
 /* Define the number of sub-IFDs you are going to write */
-#define NUMBER_OF_SUBIFDs 3
+#define NUMBER_OF_SUBIFDs 4
     uint16_t number_of_sub_IFDs = NUMBER_OF_SUBIFDs;
     toff_t sub_IFDs_offsets[NUMBER_OF_SUBIFDs] = {
         0UL}; /* array for SubIFD tag */
@@ -763,6 +846,8 @@ int test_SubIFD_directrory_handling(unsigned int openMode)
                 goto failure;
         }
 
+        expected_origin

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