3528ec
From 52dcdc1d89925fe7e362b2cc9cae699773f9618e Mon Sep 17 00:00:00 2001
3528ec
From: Hugo Lefeuvre <hle@debian.org>
3528ec
Date: Sun, 8 Apr 2018 14:07:08 -0400
3528ec
Subject: [PATCH] Fix NULL pointer dereference in TIFFPrintDirectory
3528ec
3528ec
The TIFFPrintDirectory function relies on the following assumptions,
3528ec
supposed to be guaranteed by the specification:
3528ec
3528ec
(a) A Transfer Function field is only present if the TIFF file has
3528ec
    photometric type < 3.
3528ec
3528ec
(b) If SamplesPerPixel > Color Channels, then the ExtraSamples field
3528ec
    has count SamplesPerPixel - (Color Channels) and contains
3528ec
    information about supplementary channels.
3528ec
3528ec
While respect of (a) and (b) are essential for the well functioning of
3528ec
TIFFPrintDirectory, no checks are realized neither by the callee nor
3528ec
by TIFFPrintDirectory itself. Hence, following scenarios might happen
3528ec
and trigger the NULL pointer dereference:
3528ec
3528ec
(1) TIFF File of photometric type 4 or more has illegal Transfer
3528ec
    Function field.
3528ec
3528ec
(2) TIFF File has photometric type 3 or less and defines a
3528ec
    SamplesPerPixel field such that SamplesPerPixel > Color Channels
3528ec
    without defining all extra samples in the ExtraSamples fields.
3528ec
3528ec
In this patch, we address both issues with respect of the following
3528ec
principles:
3528ec
3528ec
(A) In the case of (1), the defined transfer table should be printed
3528ec
    safely even if it isn't 'legal'. This allows us to avoid expensive
3528ec
    checks in TIFFPrintDirectory. Also, it is quite possible that
3528ec
    an alternative photometric type would be developed (not part of the
3528ec
    standard) and would allow definition of Transfer Table. We want
3528ec
    libtiff to be able to handle this scenario out of the box.
3528ec
3528ec
(B) In the case of (2), the transfer table should be printed at its
3528ec
    right size, that is if TIFF file has photometric type Palette
3528ec
    then the transfer table should have one row and not three, even
3528ec
    if two extra samples are declared.
3528ec
3528ec
In order to fulfill (A) we simply add a new 'i < 3' end condition to
3528ec
the broken TIFFPrintDirectory loop. This makes sure that in any case
3528ec
where (b) would be respected but not (a), everything stays fine.
3528ec
3528ec
(B) is fulfilled by the loop condition
3528ec
'i < td->td_samplesperpixel - td->td_extrasamples'. This is enough as
3528ec
long as (b) is respected.
3528ec
3528ec
Naturally, we also make sure (b) is respected. This is done in the
3528ec
TIFFReadDirectory function by making sure any non-color channel is
3528ec
counted in ExtraSamples.
3528ec
3528ec
This commit addresses CVE-2018-7456.
3528ec
---
3528ec
 libtiff/tif_dirread.c | 61 +++++++++++++++++++++++++++++++++++++++++++
3528ec
 libtiff/tif_print.c   |  2 +-
3528ec
 libtiff/tif_unix.c    |  8 ++++++
3528ec
 libtiff/tif_win32.c   |  8 ++++++
3528ec
 libtiff/tiffio.h      |  1 +
3528ec
 5 files changed, 79 insertions(+), 1 deletion(-)
3528ec
3528ec
diff --git a/libtiff/tif_dirread.c b/libtiff/tif_dirread.c
3528ec
index 6667228..4f07bc8 100644
3528ec
--- a/libtiff/tif_dirread.c
3528ec
+++ b/libtiff/tif_dirread.c
3528ec
@@ -165,6 +165,7 @@ static int TIFFFetchStripThing(TIFF* tif, TIFFDirEntry* dir, uint32 nstrips, uin
3528ec
 static int TIFFFetchSubjectDistance(TIFF*, TIFFDirEntry*);
3528ec
 static void ChopUpSingleUncompressedStrip(TIFF*);
3528ec
 static uint64 TIFFReadUInt64(const uint8 *value);
3528ec
+static int _TIFFGetMaxColorChannels(uint16 photometric);
3528ec
 
3528ec
 typedef union _UInt64Aligned_t
3528ec
 {
3528ec
@@ -3415,6 +3416,34 @@ static void TIFFReadDirEntryOutputErr(TIFF* tif, enum TIFFReadDirEntryErr err, c
3528ec
 	}
3528ec
 }
3528ec
 
3528ec
+/*
3528ec
+ * Return the maximum number of color channels specified for a given photometric
3528ec
+ * type. 0 is returned if photometric type isn't supported or no default value
3528ec
+ * is defined by the specification.
3528ec
+ */
3528ec
+static int _TIFFGetMaxColorChannels( uint16 photometric )
3528ec
+{
3528ec
+    switch (photometric) {
3528ec
+	case PHOTOMETRIC_PALETTE:
3528ec
+	case PHOTOMETRIC_MINISWHITE:
3528ec
+	case PHOTOMETRIC_MINISBLACK:
3528ec
+            return 1;
3528ec
+	case PHOTOMETRIC_YCBCR:
3528ec
+	case PHOTOMETRIC_RGB:
3528ec
+	case PHOTOMETRIC_CIELAB:
3528ec
+            return 3;
3528ec
+	case PHOTOMETRIC_SEPARATED:
3528ec
+	case PHOTOMETRIC_MASK:
3528ec
+            return 4;
3528ec
+	case PHOTOMETRIC_LOGL:
3528ec
+	case PHOTOMETRIC_LOGLUV:
3528ec
+	case PHOTOMETRIC_ITULAB:
3528ec
+	case PHOTOMETRIC_ICCLAB:
3528ec
+	default:
3528ec
+            return 0;
3528ec
+    }
3528ec
+}
3528ec
+
3528ec
 /*
3528ec
  * Read the next TIFF directory from a file and convert it to the internal
3528ec
  * format. We read directories sequentially.
3528ec
@@ -3430,6 +3459,7 @@ TIFFReadDirectory(TIFF* tif)
3528ec
 	const TIFFField* fip;
3528ec
 	uint32 fii=FAILED_FII;
3528ec
         toff_t nextdiroff;
3528ec
+        int color_channels;
3528ec
 	tif->tif_diroff=tif->tif_nextdiroff;
3528ec
 	if (!TIFFCheckDirOffset(tif,tif->tif_nextdiroff))
3528ec
 		return 0;           /* last offset or bad offset (IFD looping) */
3528ec
@@ -3909,6 +3939,37 @@ TIFFReadDirectory(TIFF* tif)
3528ec
 			}
3528ec
 		}
3528ec
 	}
3528ec
+
3528ec
+	/*
3528ec
+	 * Make sure all non-color channels are extrasamples.
3528ec
+	 * If it's not the case, define them as such.
3528ec
+	 */
3528ec
+        color_channels = _TIFFGetMaxColorChannels(tif->tif_dir.td_photometric);
3528ec
+        if (color_channels && tif->tif_dir.td_samplesperpixel - tif->tif_dir.td_extrasamples > color_channels) {
3528ec
+                uint16 old_extrasamples;
3528ec
+                uint16 *new_sampleinfo;
3528ec
+
3528ec
+                TIFFWarningExt(tif->tif_clientdata,module, "Sum of Photometric type-related "
3528ec
+                    "color channels and ExtraSamples doesn't match SamplesPerPixel. "
3528ec
+                    "Defining non-color channels as ExtraSamples.");
3528ec
+
3528ec
+                old_extrasamples = tif->tif_dir.td_extrasamples;
3528ec
+                tif->tif_dir.td_extrasamples = (tif->tif_dir.td_samplesperpixel - color_channels);
3528ec
+
3528ec
+                // sampleinfo should contain information relative to these new extra samples
3528ec
+                new_sampleinfo = (uint16*) _TIFFcalloc(tif->tif_dir.td_extrasamples, sizeof(uint16));
3528ec
+                if (!new_sampleinfo) {
3528ec
+                    TIFFErrorExt(tif->tif_clientdata, module, "Failed to allocate memory for "
3528ec
+                                "temporary new sampleinfo array (%d 16 bit elements)",
3528ec
+                                tif->tif_dir.td_extrasamples);
3528ec
+                    goto bad;
3528ec
+                }
3528ec
+
3528ec
+                memcpy(new_sampleinfo, tif->tif_dir.td_sampleinfo, old_extrasamples * sizeof(uint16));
3528ec
+                _TIFFsetShortArray(&tif->tif_dir.td_sampleinfo, new_sampleinfo, tif->tif_dir.td_extrasamples);
3528ec
+                _TIFFfree(new_sampleinfo);
3528ec
+        }
3528ec
+
3528ec
 	/*
3528ec
 	 * Verify Palette image has a Colormap.
3528ec
 	 */
3528ec
diff --git a/libtiff/tif_print.c b/libtiff/tif_print.c
3528ec
index b323ef1..8c075e5 100644
3528ec
--- a/libtiff/tif_print.c
3528ec
+++ b/libtiff/tif_print.c
3528ec
@@ -541,7 +541,7 @@ TIFFPrintDirectory(TIFF* tif, FILE* fd, long flags)
3528ec
 			for (l = 0; l < n; l++) {
3528ec
 				fprintf(fd, "    %2lu: %5u",
3528ec
 				    l, td->td_transferfunction[0][l]);
3528ec
-				for (i = 1; i < td->td_samplesperpixel; i++)
3528ec
+				for (i = 1; i < td->td_samplesperpixel - td->td_extrasamples && i < 3; i++)
3528ec
 					fprintf(fd, " %5u",
3528ec
 					    td->td_transferfunction[i][l]);
3528ec
 				fputc('\n', fd);
3528ec
diff --git a/libtiff/tif_unix.c b/libtiff/tif_unix.c
3528ec
index 0a3a4a8..5c00917 100644
3528ec
--- a/libtiff/tif_unix.c
3528ec
+++ b/libtiff/tif_unix.c
3528ec
@@ -262,6 +262,14 @@ _TIFFmalloc(tmsize_t s)
3528ec
 	return (malloc((size_t) s));
3528ec
 }
3528ec
 
3528ec
+void* _TIFFcalloc(tmsize_t nmemb, tmsize_t siz)
3528ec
+{
3528ec
+    if( nmemb == 0 || siz == 0 )
3528ec
+        return ((void *) NULL);
3528ec
+
3528ec
+    return calloc((size_t) nmemb, (size_t)siz);
3528ec
+}
3528ec
+
3528ec
 void
3528ec
 _TIFFfree(void* p)
3528ec
 {
3528ec
diff --git a/libtiff/tif_win32.c b/libtiff/tif_win32.c
3528ec
index 4a2466f..ef6be08 100644
3528ec
--- a/libtiff/tif_win32.c
3528ec
+++ b/libtiff/tif_win32.c
3528ec
@@ -334,6 +334,14 @@ _TIFFmalloc(tmsize_t s)
3528ec
 	return (malloc((size_t) s));
3528ec
 }
3528ec
 
3528ec
+void* _TIFFcalloc(tmsize_t nmemb, tmsize_t siz)
3528ec
+{
3528ec
+    if( nmemb == 0 || siz == 0 )
3528ec
+        return ((void *) NULL);
3528ec
+
3528ec
+    return calloc((size_t) nmemb, (size_t)siz);
3528ec
+}
3528ec
+
3528ec
 void
3528ec
 _TIFFfree(void* p)
3528ec
 {
3528ec
diff --git a/libtiff/tiffio.h b/libtiff/tiffio.h
3528ec
index 038b670..9b39392 100644
3528ec
--- a/libtiff/tiffio.h
3528ec
+++ b/libtiff/tiffio.h
3528ec
@@ -293,6 +293,7 @@ extern TIFFCodec* TIFFGetConfiguredCODECs(void);
3528ec
  */
3528ec
 
3528ec
 extern void* _TIFFmalloc(tmsize_t s);
3528ec
+extern void* _TIFFcalloc(tmsize_t nmemb, tmsize_t siz);
3528ec
 extern void* _TIFFrealloc(void* p, tmsize_t s);
3528ec
 extern void _TIFFmemset(void* p, int v, tmsize_t c);
3528ec
 extern void _TIFFmemcpy(void* d, const void* s, tmsize_t c);
3528ec
-- 
3528ec
2.17.2
3528ec