CVE-2018-5784: Uncontrolled Resource Consumption (CWE-400)
value: In LibTIFF 4.0.9, there is an uncontrolled resource consumption in the TIFFSetDirectory function of tif_dir.c. Remote attackers could leverage this vulnerability to cause a denial of service via a crafted tif file. This occurs because the declared number of directory entries is not validated against the actual number of directory entries.
CWE description: The product does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed, eventually leading to the exhaustion of available resources.
CVSS v3 base score: 6.5
Published: 2018-01-19T08:29Z
Repository: https://gitlab.com/libtiff/libtiff
Language: C
Files changed: contrib/addtiffo/tif_overview.c, tools/tiff2pdf.c, tools/tiffcrop.c
Commit date: 2018-02-12 09:43:34 -0500
Commit message: Fix for bug 2772

It is possible to craft a TIFF document where the IFD list is circular,
leading to an infinite loop while traversing the chain. The libtiff
directory reader has a failsafe that will break out of this loop after
reading 65535 directory entries, but it will continue processing,
consuming time and resources to process what is essentially a bogus TIFF
document.

This change fixes the above behavior by breaking out of processing when
a TIFF document has >= 65535 directories and terminating with an error.

Diff: diff --git a/contrib/addtiffo/tif_overview.c b/contrib/addtiffo/tif_overview.c
index c61ffbb8..03b35733 100644
--- a/contrib/addtiffo/tif_overview.c
+++ b/contrib/addtiffo/tif_overview.c
@@ -63,10 +63,12 @@
 #ifndef MAX
 #  define MIN(a,b)      ((a<b) ? a : b)
 #  define MAX(a,b)      ((a>b) ? a : b)
 #endif
 
+#define TIFF_DIR_MAX  65534
+
 void TIFFBuildOverviews( TIFF *, int, int *, int, const char *,
                          int (*)(double,void*), void * );
 
 /************************************************************************/
 /*                         TIFF_WriteOverview()                         */
@@ -89,10 +91,11 @@ uint32 TIFF_WriteOverview( TIFF *hTIFF, uint32 nXSize, uint32 nYSize,
                            int nHorSubsampling, int nVerSubsampling )
 
 {
     toff_t	nBaseDirOffset;
     toff_t	nOffset;
+    tdir_t	iNumDir;
 
     (void) bUseSubIFDs;
 
     nBaseDirOffset = TIFFCurrentDirOffset( hTIFF );
 
@@ -145,11 +148,20 @@ uint32 TIFF_WriteOverview( TIFF *hTIFF, uint32 nXSize, uint32 nYSize,
 /* -------------------------------------------------------------------- */
     if( TIFFWriteCheck( hTIFF, bTiled, "TIFFBuildOverviews" ) == 0 )
         return 0;
 
     TIFFWriteDirectory( hTIFF );
-    TIFFSetDirectory( hTIFF, (tdir_t) (TIFFNumberOfDirectories(hTIFF)-1) );
+    iNumDir = TIFFNumberOfDirectories(hTIFF);
+    if( iNumDir > TIFF_DIR_MAX )
+    {
+        TIFFErrorExt( TIFFClientdata(hTIFF),
+                      "TIFF_WriteOverview",
+                      "File `%s' has too many directories.\n",
+                      TIFFFileName(hTIFF) );
+        exit(-1);
+    }
+    TIFFSetDirectory( hTIFF, (tdir_t) (iNumDir - 1) );
 
     nOffset = TIFFCurrentDirOffset( hTIFF );
 
     TIFFSetSubDirectory( hTIFF, nBaseDirOffset );
 
diff --git a/tools/tiff2pdf.c b/tools/tiff2pdf.c
index 984ef654..832a2475 100644
--- a/tools/tiff2pdf.c
+++ b/tools/tiff2pdf.c
@@ -66,10 +66,12 @@ extern int getopt(int, char**, char*);
 
 #define TIFF2PDF_MODULE "tiff2pdf"
 
 #define PS_UNIT_SIZE	72.0F
 
+#define TIFF_DIR_MAX    65534
+
 /* This type is of PDF color spaces. */
 typedef enum {
 	T2P_CS_BILEVEL = 0x01,	/* Bilevel, black and white */
 	T2P_CS_GRAY = 0x02,	/* Single channel */
 	T2P_CS_RGB = 0x04,	/* Three channel tristimulus RGB */
@@ -1049,10 +1051,18 @@ void t2p_read_tiff_init(T2P* t2p, TIFF* input){
 	uint16 xuint16=0;
 	uint16 tiff_transferfunctioncount=0;
 	uint16* tiff_transferfunction[3];
 
 	directorycount=TIFFNumberOfDirectories(input);
+	if(directorycount > TIFF_DIR_MAX) {
+		TIFFError(
+			TIFF2PDF_MODULE,
+			"TIFF contains too many directories, %s",
+			TIFFFileName(input));
+		t2p->t2p_error = T2P_ERR_ERROR;
+		return;
+	}
 	t2p->tiff_pages = (T2P_PAGE*) _TIFFmalloc(TIFFSafeMultiply(tmsize_t,directorycount,sizeof(T2P_PAGE)));
 	if(t2p->tiff_pages==NULL){
 		TIFFError(
 			TIFF2PDF_MODULE, 
 			"Can't allocate " TIFF_SIZE_FORMAT " bytes of memory for tiff_pages array, %s", 
diff --git a/tools/tiffcrop.c b/tools/tiffcrop.c
index 91a38f67..e466dae6 100644
--- a/tools/tiffcrop.c
+++ b/tools/tiffcrop.c
@@ -213,10 +213,12 @@ extern int getopt(int argc, char * const argv[], const char *optstring);
 
 #define DUMP_NONE   0
 #define DUMP_TEXT   1
 #define DUMP_RAW    2
 
+#define TIFF_DIR_MAX  65534
+
 /* Offsets into buffer for margins and fixed width and length segments */
 struct offset {
   uint32  tmargin;
   uint32  lmargin;
   uint32  bmargin;
@@ -2230,19 +2232,26 @@ main(int argc, char* argv[])
 
   if ((argc - optind) == 2)
     pageNum = -1;
   else
     total_images = 0;
-  /* read multiple input files and write to output file(s) */
+  /* Read multiple input files and write to output file(s) */
   while (optind < argc - 1)
     {
     in = TIFFOpen (argv[optind], "r");
     if (in == NULL)
       return (-3);
 
     /* If only one input file is specified, we can use directory count */
-    total_images = TIFFNumberOfDirectories(in); 
+    total_images = TIFFNumberOfDirectories(in);
+    if (total_images > TIFF_DIR_MAX)
+      {
+      TIFFError (TIFFFileName(in), "File contains too many directories");
+      if (out != NULL)
+        (void) TIFFClose(out);
+      return (1);
+      }
     if (image_count == 0)
       {
       dirnum = 0;
       total_pages = total_images; /* Only valid with single input file */
       }

Vulnerable code:     TIFFSetDirectory( hTIFF, (tdir_t) (TIFFNumberOfDirectories(hTIFF)-1) );
  /* read multiple input files and write to output file(s) */
    total_images = TIFFNumberOfDirectories(in); 
Fixed code: #define TIFF_DIR_MAX  65534

    tdir_t	iNumDir;
    iNumDir = TIFFNumberOfDirectories(hTIFF);
    if( iNumDir > TIFF_DIR_MAX )
    {
        TIFFErrorExt( TIFFClientdata(hTIFF),
                      "TIFF_WriteOverview",
                      "File `%s' has too many directories.\n",
                      TIFFFileName(hTIFF) );
        exit(-1);
    }
    TIFFSetDirectory( hTIFF, (tdir_t) (iNumDir - 1) );
#define TIFF_DIR_MAX    65534

	if(directorycount > TIFF_DIR_MAX) {
		TIFFError(
			TIFF2PDF_MODULE,
			"TIFF contains too many directories, %s",
			TIFFFileName(input));
		t2p->t2p_error = T2P_ERR_ERROR;
		return;
	}
#define TIFF_DIR_MAX  65534

  /* Read multiple input files and write to output file(s) */
    total_images = TIFFNumberOfDirectories(in);
    if (total_images > TIFF_DIR_MAX)
      {
      TIFFError (TIFFFileName(in), "File contains too many directories");
      if (out != NULL)
        (void) TIFFClose(out);
      return (1);
      }