libtiff: tif_fax3.c: error out after a number of times end-of-file has been reached (fixes #583)

From 8d0bf12233e1d0c5b7564d0938c4adbb3b93ac23 Mon Sep 17 00:00:00 2001
From: Even Rouault <[EMAIL REDACTED]>
Date: Tue, 31 Oct 2023 16:39:43 +0100
Subject: [PATCH] tif_fax3.c: error out after a number of times end-of-file has
 been reached (fixes #583)

---
 libtiff/tif_fax3.c | 43 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 42 insertions(+), 1 deletion(-)

diff --git a/libtiff/tif_fax3.c b/libtiff/tif_fax3.c
index a3c645cb..668e96a3 100644
--- a/libtiff/tif_fax3.c
+++ b/libtiff/tif_fax3.c
@@ -41,6 +41,14 @@
 #include "t4.h"
 #include <stdio.h>
 
+#ifndef EOF_REACHED_COUNT_THRESHOLD
+/* Arbitrary threshold to avoid corrupted single-strip files with extremely
+ * large imageheight to cause apparently endless looping, such as in
+ * https://gitlab.com/libtiff/libtiff/-/issues/583
+ */
+#define EOF_REACHED_COUNT_THRESHOLD 8192
+#endif
+
 /*
  * Compression+decompression state blocks are
  * derived from this ``base state'' block.
@@ -77,6 +85,8 @@ typedef struct
     uint32_t data;               /* current i/o byte/word */
     int bit;                     /* current i/o bit in byte */
     int EOLcnt;                  /* count of EOL codes recognized */
+    int eofReachedCount;         /* number of times decode has been called with
+                                    EOF already reached */
     TIFFFaxFillFunc fill;        /* fill routine */
     uint32_t *runs;              /* b&w runs for current/previous row */
     uint32_t nruns;              /* size of the refruns / curruns arrays */
@@ -120,6 +130,7 @@ typedef struct
     int EOLcnt;                               /* # EOL codes recognized */     \
     const unsigned char *bitmap = sp->bitmap; /* input data bit reverser */    \
     const TIFFFaxTabEnt *TabEnt
+
 #define DECLARE_STATE_2D(tif, sp, mod)                                         \
     DECLARE_STATE(tif, sp, mod);                                               \
     int b1; /* next change on prev line */                                     \
@@ -162,6 +173,7 @@ static int Fax3PreDecode(TIFF *tif, uint16_t s)
     sp->bit = 0; /* force initial read */
     sp->data = 0;
     sp->EOLcnt = 0; /* force initial scan for EOL */
+    sp->eofReachedCount = 0;
     /*
      * Decoder assumes lsb-to-msb bit order.  Note that we select
      * this here rather than in Fax3SetupState so that viewers can
@@ -232,7 +244,12 @@ static void Fax3PrematureEOF(const char *module, TIFF *tif, uint32_t line,
                     line, isTiled(tif) ? "tile" : "strip",
                     (isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip), a0);
 }
-#define prematureEOF(a0) Fax3PrematureEOF(module, tif, sp->line, a0)
+#define prematureEOF(a0)                                                       \
+    do                                                                         \
+    {                                                                          \
+        Fax3PrematureEOF(module, tif, sp->line, a0);                           \
+        ++sp->eofReachedCount;                                                 \
+    } while (0)
 
 #define Nop
 
@@ -252,6 +269,14 @@ static int Fax3Decode1D(TIFF *tif, uint8_t *buf, tmsize_t occ, uint16_t s)
         TIFFErrorExtR(tif, module, "Fractional scanlines cannot be read");
         return (-1);
     }
+    if (sp->eofReachedCount >= EOF_REACHED_COUNT_THRESHOLD)
+    {
+        TIFFErrorExtR(
+            tif, module,
+            "End of file has already been reached %d times within that strip",
+            sp->eofReachedCount);
+        return (-1);
+    }
     CACHE_STATE(tif, sp);
     thisrun = sp->curruns;
     while (occ > 0)
@@ -302,6 +327,14 @@ static int Fax3Decode2D(TIFF *tif, uint8_t *buf, tmsize_t occ, uint16_t s)
         TIFFErrorExtR(tif, module, "Fractional scanlines cannot be read");
         return (-1);
     }
+    if (sp->eofReachedCount >= EOF_REACHED_COUNT_THRESHOLD)
+    {
+        TIFFErrorExtR(
+            tif, module,
+            "End of file has already been reached %d times within that strip",
+            sp->eofReachedCount);
+        return (-1);
+    }
     CACHE_STATE(tif, sp);
     while (occ > 0)
     {
@@ -1514,6 +1547,14 @@ static int Fax4Decode(TIFF *tif, uint8_t *buf, tmsize_t occ, uint16_t s)
         TIFFErrorExtR(tif, module, "Fractional scanlines cannot be read");
         return (-1);
     }
+    if (sp->eofReachedCount >= EOF_REACHED_COUNT_THRESHOLD)
+    {
+        TIFFErrorExtR(
+            tif, module,
+            "End of file has already been reached %d times within that strip",
+            sp->eofReachedCount);
+        return (-1);
+    }
     CACHE_STATE(tif, sp);
     while (occ > 0)
     {