libtiff: TIFFCurrentDirectory(), TIFFNumberOfDirectories(), TIFFSetDirectory(), TIFFUnlinkDirectory(): use tdir_t that is now a...

From 8f38557ef614f9af8b95ed164412e6157d1a5df8 Mon Sep 17 00:00:00 2001
From: Even Rouault <[EMAIL REDACTED]>
Date: Mon, 12 Dec 2022 19:02:54 +0100
Subject: [PATCH] TIFFCurrentDirectory(), TIFFNumberOfDirectories(),
 TIFFSetDirectory(), TIFFUnlinkDirectory(): use tdir_t that is now a uint32_t,
 and raise limit of IFDs to 1048576

---
 doc/functions/TIFFCreateDirectory.rst |  2 +-
 doc/functions/TIFFquery.rst           |  4 +--
 doc/functions/libtiff.rst             |  6 +++-
 libtiff/tif_dir.c                     | 50 ++++++++++++---------------
 libtiff/tif_dir.h                     |  4 +--
 libtiff/tif_dirread.c                 | 28 +++++++++------
 libtiff/tif_open.c                    |  2 +-
 libtiff/tiffio.h                      | 10 +++---
 libtiff/tiffiop.h                     |  8 ++---
 9 files changed, 59 insertions(+), 55 deletions(-)

diff --git a/doc/functions/TIFFCreateDirectory.rst b/doc/functions/TIFFCreateDirectory.rst
index 5e7377e5..7c285c05 100644
--- a/doc/functions/TIFFCreateDirectory.rst
+++ b/doc/functions/TIFFCreateDirectory.rst
@@ -14,7 +14,7 @@ Synopsis
 
 .. c:function:: int TIFFFreeDirectory(TIFF* tif)
 
-.. c:function:: int TIFFUnlinkDirectory(TIFF* tif, uint16_t dirn)
+.. c:function:: int TIFFUnlinkDirectory(TIFF* tif, tdir_t dirn)
 
 Description
 -----------
diff --git a/doc/functions/TIFFquery.rst b/doc/functions/TIFFquery.rst
index 6a03c626..75ccb4e1 100644
--- a/doc/functions/TIFFquery.rst
+++ b/doc/functions/TIFFquery.rst
@@ -12,11 +12,11 @@ Synopsis
 
 .. c:function:: tdir_t TIFFCurrentDirectory(TIFF* tif)
 
-.. c:function:: int TIFFCurrentDirOffset(TIFF* tif)
+.. c:function:: uint64_t TIFFCurrentDirOffset(TIFF* tif)
 
 .. c:function:: int TIFFLastDirectory(TIFF* tif)
 
-.. c:function:: int TIFFNumberOfDirectories(TIFF* tif)
+.. c:function:: tdir_t TIFFNumberOfDirectories(TIFF* tif)
 
 .. c:function:: uint32_t TIFFCurrentRow(TIFF* tif)
 
diff --git a/doc/functions/libtiff.rst b/doc/functions/libtiff.rst
index 54a08614..b4ad327f 100644
--- a/doc/functions/libtiff.rst
+++ b/doc/functions/libtiff.rst
@@ -70,7 +70,7 @@ definitions or through parameters passed through the varargs interfaces.
 ::
 
     typedef uint32_t ttag_t;    // directory tag
-    typedef uint16_t tdir_t;    // directory index
+    typedef uint32_t tdir_t;    // directory index
     typedef uint16_t tsample_t; // sample number
     typedef uint32_t tstrip_t;  // strip number
     typedef uint32_t ttile_t;   // tile number
@@ -94,10 +94,14 @@ Likewise
 is limited by the 16-bit field used to store the
 ``SamplesPerPixel``
 tag.
+
 :c:type:`tdir_t`
 constrains the maximum number of
 IFDs
 that may appear in an image and may be an arbitrary size (w/o penalty).
+Starting with libtiff 4.5.0, tdir_t is a 32-bit unsigned integer. Previously,
+it was a 16-bit unsigned integer.
+
 :c:type:`ttag_t`
 must be either int, unsigned int, pointer, or double because the library uses
 a varargs interface and
diff --git a/libtiff/tif_dir.c b/libtiff/tif_dir.c
index 6b58a2a3..3c3cb340 100644
--- a/libtiff/tif_dir.c
+++ b/libtiff/tif_dir.c
@@ -1812,7 +1812,7 @@ int TIFFDefaultDirectory(TIFF *tif)
 }
 
 static int TIFFAdvanceDirectory(TIFF *tif, uint64_t *nextdiroff, uint64_t *off,
-                                uint16_t *nextdirnum)
+                                tdir_t *nextdirnum)
 {
     static const char module[] = "TIFFAdvanceDirectory";
 
@@ -1820,8 +1820,8 @@ static int TIFFAdvanceDirectory(TIFF *tif, uint64_t *nextdiroff, uint64_t *off,
     if (!_TIFFCheckDirNumberAndOffset(tif, *nextdirnum, *nextdiroff))
     {
         TIFFErrorExtR(tif, module,
-                      "Starting directory %" PRIu16 " at offset 0x%" PRIx64
-                      " (%" PRIu64 ") might cause an IFD loop",
+                      "Starting directory %u at offset 0x%" PRIx64 " (%" PRIu64
+                      ") might cause an IFD loop",
                       *nextdirnum, *nextdiroff, *nextdiroff);
         *nextdiroff = 0;
         *nextdirnum = 0;
@@ -1995,12 +1995,11 @@ static int TIFFAdvanceDirectory(TIFF *tif, uint64_t *nextdiroff, uint64_t *off,
 /*
  * Count the number of directories in a file.
  */
-uint16_t TIFFNumberOfDirectories(TIFF *tif)
+tdir_t TIFFNumberOfDirectories(TIFF *tif)
 {
-    static const char module[] = "TIFFNumberOfDirectories";
     uint64_t nextdiroff;
-    uint16_t nextdirnum;
-    uint16_t n;
+    tdir_t nextdirnum;
+    tdir_t n;
     if (!(tif->tif_flags & TIFF_BIGTIFF))
         nextdiroff = tif->tif_header.classic.tiff_diroff;
     else
@@ -2010,17 +2009,7 @@ uint16_t TIFFNumberOfDirectories(TIFF *tif)
     while (nextdiroff != 0 &&
            TIFFAdvanceDirectory(tif, &nextdiroff, NULL, &nextdirnum))
     {
-        if (n != 65535)
-        {
-            ++n;
-        }
-        else
-        {
-            TIFFErrorExtR(tif, module,
-                          "Directory count exceeded 65535 limit,"
-                          " giving up on counting.");
-            return (65535);
-        }
+        ++n;
     }
     return (n);
 }
@@ -2029,11 +2018,11 @@ uint16_t TIFFNumberOfDirectories(TIFF *tif)
  * Set the n-th directory as the current directory.
  * NB: Directories are numbered starting at 0.
  */
-int TIFFSetDirectory(TIFF *tif, uint16_t dirn)
+int TIFFSetDirectory(TIFF *tif, tdir_t dirn)
 {
     uint64_t nextdiroff;
-    uint16_t nextdirnum;
-    uint16_t n;
+    tdir_t nextdirnum;
+    tdir_t n;
 
     if (!(tif->tif_flags & TIFF_BIGTIFF))
         nextdiroff = tif->tif_header.classic.tiff_diroff;
@@ -2076,12 +2065,12 @@ int TIFFSetSubDirectory(TIFF *tif, uint64_t diroff)
      * https://www.awaresystems.be/imaging/tiff/specification/TIFFPM6.pdf.)
      */
     int retval;
-    uint16_t curdir = 0;
+    uint32_t curdir = 0;
     int8_t probablySubIFD = 0;
     if (diroff == 0)
     {
         /* Special case to invalidate the tif_lastdiroff member. */
-        tif->tif_curdir = 65535;
+        tif->tif_curdir = 0xffffffffu;
     }
     else
     {
@@ -2091,7 +2080,7 @@ int TIFFSetSubDirectory(TIFF *tif, uint64_t diroff)
             probablySubIFD = 1;
         }
         /* -1 because TIFFReadDirectory() will increment tif_curdir. */
-        tif->tif_curdir = curdir - 1;
+        tif->tif_curdir = curdir == 0 ? 0xffffffffu : curdir - 1;
     }
 
     tif->tif_nextdiroff = diroff;
@@ -2099,7 +2088,12 @@ int TIFFSetSubDirectory(TIFF *tif, uint64_t diroff)
     /* If failed, curdir was not incremented in TIFFReadDirectory(), so set it
      * back. */
     if (!retval)
-        tif->tif_curdir++;
+    {
+        if (tif->tif_curdir == 0xffffffffu)
+            tif->tif_curdir = 0;
+        else
+            tif->tif_curdir++;
+    }
     if (retval && probablySubIFD)
     {
         /* Reset IFD list to start new one for SubIFD chain and also start
@@ -2129,13 +2123,13 @@ int TIFFLastDirectory(TIFF *tif) { return (tif->tif_nextdiroff == 0); }
  * This is different to TIFFSetDirectory() where the first directory starts with
  * zero.
  */
-int TIFFUnlinkDirectory(TIFF *tif, uint16_t dirn)
+int TIFFUnlinkDirectory(TIFF *tif, tdir_t dirn)
 {
     static const char module[] = "TIFFUnlinkDirectory";
     uint64_t nextdir;
-    uint16_t nextdirnum;
+    tdir_t nextdirnum;
     uint64_t off;
-    uint16_t n;
+    tdir_t n;
 
     if (tif->tif_mode == O_RDONLY)
     {
diff --git a/libtiff/tif_dir.h b/libtiff/tif_dir.h
index 3c5fd282..fad1eb02 100644
--- a/libtiff/tif_dir.h
+++ b/libtiff/tif_dir.h
@@ -325,10 +325,10 @@ extern "C"
                                                      TIFFDataType);
     extern TIFFField *_TIFFCreateAnonField(TIFF *, uint32_t, TIFFDataType);
     extern int _TIFFCheckFieldIsValidForCodec(TIFF *tif, ttag_t tag);
-    extern int _TIFFCheckDirNumberAndOffset(TIFF *tif, uint16_t dirn,
+    extern int _TIFFCheckDirNumberAndOffset(TIFF *tif, tdir_t dirn,
                                             uint64_t diroff);
     extern int _TIFFGetDirNumberFromOffset(TIFF *tif, uint64_t diroff,
-                                           uint16_t *dirn);
+                                           tdir_t *dirn);
 
 #if defined(__cplusplus)
 }
diff --git a/libtiff/tif_dirread.c b/libtiff/tif_dirread.c
index 0dbe1f3e..e2f563eb 100644
--- a/libtiff/tif_dirread.c
+++ b/libtiff/tif_dirread.c
@@ -4075,9 +4075,11 @@ int TIFFReadDirectory(TIFF *tif)
     /* tif_curdir++ and tif_nextdiroff should only be updated after SUCCESSFUL
      * reading of the directory. Otherwise, invalid IFD offsets could corrupt
      * the IFD list. */
-    if (!_TIFFCheckDirNumberAndOffset(tif, tif->tif_curdir + 1, nextdiroff))
+    if (!_TIFFCheckDirNumberAndOffset(
+            tif, tif->tif_curdir == 0xFFFFFFFFU ? 0 : tif->tif_curdir + 1,
+            nextdiroff))
     {
-        return 0; /* bad offset (IFD looping or more than 65535 IFDs) */
+        return 0; /* bad offset (IFD looping or more than 1048576 IFDs) */
     }
     dircount = TIFFFetchDirectory(tif, nextdiroff, &dir, &tif->tif_nextdiroff);
     if (!dircount)
@@ -4090,7 +4092,10 @@ int TIFFReadDirectory(TIFF *tif)
     /* Set global values after a valid directory has been fetched.
      * tif_diroff is already set to nextdiroff in TIFFFetchDirectory() in the
      * beginning. */
-    tif->tif_curdir++;
+    if (tif->tif_curdir == 0xffffffffu)
+        tif->tif_curdir = 0;
+    else
+        tif->tif_curdir++;
     (*tif->tif_cleanup)(tif); /* cleanup any previous compression state */
 
     TIFFReadDirectoryCheckOrder(tif, dir, dircount);
@@ -5319,7 +5324,7 @@ static bool equalFuncNumberToOffset(const void *elt1, const void *elt2)
  * Returns 1 if all is ok; 0 if last directory or IFD loop is encountered,
  * or an error has occurred.
  */
-int _TIFFCheckDirNumberAndOffset(TIFF *tif, uint16_t dirn, uint64_t diroff)
+int _TIFFCheckDirNumberAndOffset(TIFF *tif, tdir_t dirn, uint64_t diroff)
 {
     if (diroff == 0) /* no more directories */
         return 0;
@@ -5391,10 +5396,11 @@ int _TIFFCheckDirNumberAndOffset(TIFF *tif, uint16_t dirn, uint64_t diroff)
         return 1;
     }
 
-    if (tif->tif_dirnumber == 65535)
+    /* Arbitrary (hopefully big enough) limit */
+    if (tif->tif_dirnumber >= 1048576)
     {
         TIFFErrorExtR(tif, "_TIFFCheckDirNumberAndOffset",
-                      "Cannot handle more than 65535 TIFF directories");
+                      "Cannot handle more than 1048576 TIFF directories");
         return 0;
     }
 
@@ -5437,14 +5443,14 @@ int _TIFFCheckDirNumberAndOffset(TIFF *tif, uint16_t dirn, uint64_t diroff)
  * can be returned.
  * Otherwise returns 0 or if an error occurred.
  */
-int _TIFFGetDirNumberFromOffset(TIFF *tif, uint64_t diroff, uint16_t *dirn)
+int _TIFFGetDirNumberFromOffset(TIFF *tif, uint64_t diroff, tdir_t *dirn)
 {
     if (diroff == 0) /* no more directories */
         return 0;
-    if (tif->tif_dirnumber == 65535)
+    if (tif->tif_dirnumber >= 1048576)
     {
         TIFFErrorExtR(tif, "_TIFFGetDirNumberFromOffset",
-                      "Cannot handle more than 65535 TIFF directories");
+                      "Cannot handle more than 1048576 TIFF directories");
         return 0;
     }
 
@@ -5463,7 +5469,7 @@ int _TIFFGetDirNumberFromOffset(TIFF *tif, uint64_t diroff, uint16_t *dirn)
             tif->tif_map_dir_offset_to_number, &entry);
     if (foundEntry)
     {
-        *dirn = (uint16_t)foundEntry->dirNumber;
+        *dirn = foundEntry->dirNumber;
         return 1;
     }
 
@@ -5473,7 +5479,7 @@ int _TIFFGetDirNumberFromOffset(TIFF *tif, uint64_t diroff, uint16_t *dirn)
         tif->tif_map_dir_offset_to_number, &entry);
     if (foundEntry)
     {
-        *dirn = (uint16_t)foundEntry->dirNumber;
+        *dirn = foundEntry->dirNumber;
         return 1;
     }
 
diff --git a/libtiff/tif_open.c b/libtiff/tif_open.c
index 77372533..953d8258 100644
--- a/libtiff/tif_open.c
+++ b/libtiff/tif_open.c
@@ -721,7 +721,7 @@ uint32_t TIFFCurrentRow(TIFF *tif) { return (tif->tif_row); }
 /*
  * Return index of the current directory.
  */
-uint16_t TIFFCurrentDirectory(TIFF *tif) { return (tif->tif_curdir); }
+tdir_t TIFFCurrentDirectory(TIFF *tif) { return (tif->tif_curdir); }
 
 /*
  * Return current strip.
diff --git a/libtiff/tiffio.h b/libtiff/tiffio.h
index df20f8bb..4df57665 100644
--- a/libtiff/tiffio.h
+++ b/libtiff/tiffio.h
@@ -69,7 +69,7 @@ typedef uint64_t toff_t; /* file offset */
 /* the following are deprecated and should be replaced by their defining
    counterparts */
 typedef uint32_t ttag_t;    /* directory tag */
-typedef uint16_t tdir_t;    /* directory index */
+typedef uint32_t tdir_t;    /* directory index */
 typedef uint16_t tsample_t; /* sample number */
 typedef uint32_t tstrile_t; /* strip or tile number */
 typedef tstrile_t tstrip_t; /* strip number */
@@ -414,8 +414,8 @@ extern "C"
     extern TIFFMapFileProc TIFFGetMapFileProc(TIFF *);
     extern TIFFUnmapFileProc TIFFGetUnmapFileProc(TIFF *);
     extern uint32_t TIFFCurrentRow(TIFF *);
-    extern uint16_t TIFFCurrentDirectory(TIFF *);
-    extern uint16_t TIFFNumberOfDirectories(TIFF *);
+    extern tdir_t TIFFCurrentDirectory(TIFF *);
+    extern tdir_t TIFFNumberOfDirectories(TIFF *);
     extern uint64_t TIFFCurrentDirOffset(TIFF *);
     extern uint32_t TIFFCurrentStrip(TIFF *);
     extern uint32_t TIFFCurrentTile(TIFF *tif);
@@ -429,9 +429,9 @@ extern "C"
     extern int TIFFCreateEXIFDirectory(TIFF *);
     extern int TIFFCreateGPSDirectory(TIFF *);
     extern int TIFFLastDirectory(TIFF *);
-    extern int TIFFSetDirectory(TIFF *, uint16_t);
+    extern int TIFFSetDirectory(TIFF *, tdir_t);
     extern int TIFFSetSubDirectory(TIFF *, uint64_t);
-    extern int TIFFUnlinkDirectory(TIFF *, uint16_t);
+    extern int TIFFUnlinkDirectory(TIFF *, tdir_t);
     extern int TIFFSetField(TIFF *, uint32_t, ...);
     extern int TIFFVSetField(TIFF *, uint32_t, va_list);
     extern int TIFFUnsetField(TIFF *, uint32_t);
diff --git a/libtiff/tiffiop.h b/libtiff/tiffiop.h
index fc41be5b..1d4add0a 100644
--- a/libtiff/tiffiop.h
+++ b/libtiff/tiffiop.h
@@ -90,7 +90,7 @@ typedef void (*TIFFTileMethod)(TIFF *, uint32_t *, uint32_t *);
 struct TIFFOffsetAndDirNumber
 {
     uint64_t offset;
-    uint32_t dirNumber;
+    tdir_t dirNumber;
 };
 typedef struct TIFFOffsetAndDirNumber TIFFOffsetAndDirNumber;
 
@@ -142,8 +142,8 @@ struct tiff
                                  prevent IFD looping */
     TIFFHashSet *tif_map_dir_offset_to_number;
     TIFFHashSet *tif_map_dir_number_to_offset;
-    uint16_t tif_dirnumber; /* number of already seen directories */
-    TIFFDirectory tif_dir;  /* internal rep of current directory */
+    tdir_t tif_dirnumber;  /* number of already seen directories */
+    TIFFDirectory tif_dir; /* internal rep of current directory */
     TIFFDirectory
         tif_customdir; /* custom IFDs are separated from the main ones */
     union
@@ -154,7 +154,7 @@ struct tiff
     } tif_header;
     uint16_t tif_header_size;  /* file's header block and its length */
     uint32_t tif_row;          /* current scanline */
-    uint16_t tif_curdir;       /* current directory (index) */
+    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