8ca2e9
From f1c2759436a0ee5181cae7176114f57e45f0eb16 Mon Sep 17 00:00:00 2001
8ca2e9
From: Hugo Lefeuvre <hle@debian.org>
8ca2e9
Date: Sun, 8 Apr 2018 14:07:08 -0400
8ca2e9
Subject: [PATCH] Fix NULL pointer dereference in TIFFPrintDirectory
8ca2e9
8ca2e9
The TIFFPrintDirectory function relies on the following assumptions,
8ca2e9
supposed to be guaranteed by the specification:
8ca2e9
8ca2e9
(a) A Transfer Function field is only present if the TIFF file has
8ca2e9
    photometric type < 3.
8ca2e9
8ca2e9
(b) If SamplesPerPixel > Color Channels, then the ExtraSamples field
8ca2e9
    has count SamplesPerPixel - (Color Channels) and contains
8ca2e9
    information about supplementary channels.
8ca2e9
8ca2e9
While respect of (a) and (b) are essential for the well functioning of
8ca2e9
TIFFPrintDirectory, no checks are realized neither by the callee nor
8ca2e9
by TIFFPrintDirectory itself. Hence, following scenarios might happen
8ca2e9
and trigger the NULL pointer dereference:
8ca2e9
8ca2e9
(1) TIFF File of photometric type 4 or more has illegal Transfer
8ca2e9
    Function field.
8ca2e9
8ca2e9
(2) TIFF File has photometric type 3 or less and defines a
8ca2e9
    SamplesPerPixel field such that SamplesPerPixel > Color Channels
8ca2e9
    without defining all extra samples in the ExtraSamples fields.
8ca2e9
8ca2e9
In this patch, we address both issues with respect of the following
8ca2e9
principles:
8ca2e9
8ca2e9
(A) In the case of (1), the defined transfer table should be printed
8ca2e9
    safely even if it isn't 'legal'. This allows us to avoid expensive
8ca2e9
    checks in TIFFPrintDirectory. Also, it is quite possible that
8ca2e9
    an alternative photometric type would be developed (not part of the
8ca2e9
    standard) and would allow definition of Transfer Table. We want
8ca2e9
    libtiff to be able to handle this scenario out of the box.
8ca2e9
8ca2e9
(B) In the case of (2), the transfer table should be printed at its
8ca2e9
    right size, that is if TIFF file has photometric type Palette
8ca2e9
    then the transfer table should have one row and not three, even
8ca2e9
    if two extra samples are declared.
8ca2e9
8ca2e9
In order to fulfill (A) we simply add a new 'i < 3' end condition to
8ca2e9
the broken TIFFPrintDirectory loop. This makes sure that in any case
8ca2e9
where (b) would be respected but not (a), everything stays fine.
8ca2e9
8ca2e9
(B) is fulfilled by the loop condition
8ca2e9
'i < td->td_samplesperpixel - td->td_extrasamples'. This is enough as
8ca2e9
long as (b) is respected.
8ca2e9
8ca2e9
Naturally, we also make sure (b) is respected. This is done in the
8ca2e9
TIFFReadDirectory function by making sure any non-color channel is
8ca2e9
counted in ExtraSamples.
8ca2e9
8ca2e9
This commit addresses CVE-2018-7456.
8ca2e9
---
8ca2e9
 libtiff/tif_dirread.c | 61 +++++++++++++++++++++++++++++++++++++++++++
8ca2e9
 libtiff/tif_print.c   |  2 +-
8ca2e9
 libtiff/tif_unix.c    |  9 +++++++
8ca2e9
 libtiff/tiffio.h      |  1 +
8ca2e9
 4 files changed, 72 insertions(+), 1 deletion(-)
8ca2e9
8ca2e9
diff --git a/libtiff/tif_dirread.c b/libtiff/tif_dirread.c
8ca2e9
index 0a9484d..8dc40b5 100644
8ca2e9
--- a/libtiff/tif_dirread.c
8ca2e9
+++ b/libtiff/tif_dirread.c
8ca2e9
@@ -65,6 +65,35 @@ static	int TIFFFetchDoubleArray(TIFF*, TIFFDirEntry*, double*);
8ca2e9
 static	int TIFFFetchAnyArray(TIFF*, TIFFDirEntry*, double*);
8ca2e9
 static	int TIFFFetchShortPair(TIFF*, TIFFDirEntry*);
8ca2e9
 static	void ChopUpSingleUncompressedStrip(TIFF*);
8ca2e9
+static	int _TIFFGetMaxColorChannels(uint16 photometric);
8ca2e9
+
8ca2e9
+/*
8ca2e9
+ * Return the maximum number of color channels specified for a given photometric
8ca2e9
+ * type. 0 is returned if photometric type isn't supported or no default value
8ca2e9
+ * is defined by the specification.
8ca2e9
+ */
8ca2e9
+static int _TIFFGetMaxColorChannels( uint16 photometric )
8ca2e9
+{
8ca2e9
+    switch (photometric) {
8ca2e9
+	case PHOTOMETRIC_PALETTE:
8ca2e9
+	case PHOTOMETRIC_MINISWHITE:
8ca2e9
+	case PHOTOMETRIC_MINISBLACK:
8ca2e9
+            return 1;
8ca2e9
+	case PHOTOMETRIC_YCBCR:
8ca2e9
+	case PHOTOMETRIC_RGB:
8ca2e9
+	case PHOTOMETRIC_CIELAB:
8ca2e9
+            return 3;
8ca2e9
+	case PHOTOMETRIC_SEPARATED:
8ca2e9
+	case PHOTOMETRIC_MASK:
8ca2e9
+            return 4;
8ca2e9
+	case PHOTOMETRIC_LOGL:
8ca2e9
+	case PHOTOMETRIC_LOGLUV:
8ca2e9
+	case PHOTOMETRIC_ITULAB:
8ca2e9
+	case PHOTOMETRIC_ICCLAB:
8ca2e9
+	default:
8ca2e9
+            return 0;
8ca2e9
+    }
8ca2e9
+}
8ca2e9
 
8ca2e9
 /*
8ca2e9
  * Read the next TIFF directory from a file and convert it to the internal
8ca2e9
@@ -86,6 +115,7 @@ TIFFReadDirectory(TIFF* tif)
8ca2e9
 	uint16 previous_tag = 0;
8ca2e9
 	int diroutoforderwarning = 0, compressionknown = 0;
8ca2e9
 	int haveunknowntags = 0;
8ca2e9
+	int color_channels;
8ca2e9
 
8ca2e9
 	tif->tif_diroff = tif->tif_nextdiroff;
8ca2e9
 	/*
8ca2e9
@@ -617,6 +647,37 @@ TIFFReadDirectory(TIFF* tif)
8ca2e9
 			}
8ca2e9
 		}
8ca2e9
 	}
8ca2e9
+
8ca2e9
+	/*
8ca2e9
+	 * Make sure all non-color channels are extrasamples.
8ca2e9
+	 * If it's not the case, define them as such.
8ca2e9
+	 */
8ca2e9
+        color_channels = _TIFFGetMaxColorChannels(tif->tif_dir.td_photometric);
8ca2e9
+        if (color_channels && tif->tif_dir.td_samplesperpixel - tif->tif_dir.td_extrasamples > color_channels) {
8ca2e9
+                uint16 old_extrasamples;
8ca2e9
+                uint16 *new_sampleinfo;
8ca2e9
+
8ca2e9
+                TIFFWarningExt(tif->tif_clientdata,module, "Sum of Photometric type-related "
8ca2e9
+                    "color channels and ExtraSamples doesn't match SamplesPerPixel. "
8ca2e9
+                    "Defining non-color channels as ExtraSamples.");
8ca2e9
+
8ca2e9
+                old_extrasamples = tif->tif_dir.td_extrasamples;
8ca2e9
+                tif->tif_dir.td_extrasamples = (tif->tif_dir.td_samplesperpixel - color_channels);
8ca2e9
+
8ca2e9
+                // sampleinfo should contain information relative to these new extra samples
8ca2e9
+                new_sampleinfo = (uint16*) _TIFFcalloc(tif->tif_dir.td_extrasamples, sizeof(uint16));
8ca2e9
+                if (!new_sampleinfo) {
8ca2e9
+                    TIFFErrorExt(tif->tif_clientdata, module, "Failed to allocate memory for "
8ca2e9
+                                "temporary new sampleinfo array (%d 16 bit elements)",
8ca2e9
+                                tif->tif_dir.td_extrasamples);
8ca2e9
+                    goto bad;
8ca2e9
+                }
8ca2e9
+
8ca2e9
+                memcpy(new_sampleinfo, tif->tif_dir.td_sampleinfo, old_extrasamples * sizeof(uint16));
8ca2e9
+                _TIFFsetShortArray(&tif->tif_dir.td_sampleinfo, new_sampleinfo, tif->tif_dir.td_extrasamples);
8ca2e9
+                _TIFFfree(new_sampleinfo);
8ca2e9
+        }
8ca2e9
+
8ca2e9
 	/*
8ca2e9
 	 * Verify Palette image has a Colormap.
8ca2e9
 	 */
8ca2e9
diff --git a/libtiff/tif_print.c b/libtiff/tif_print.c
8ca2e9
index 0f6ea01..1ec4f26 100644
8ca2e9
--- a/libtiff/tif_print.c
8ca2e9
+++ b/libtiff/tif_print.c
8ca2e9
@@ -503,7 +503,7 @@ TIFFPrintDirectory(TIFF* tif, FILE* fd, long flags)
8ca2e9
 			for (l = 0; l < n; l++) {
8ca2e9
 				fprintf(fd, "    %2lu: %5u",
8ca2e9
 				    l, td->td_transferfunction[0][l]);
8ca2e9
-				for (i = 1; i < td->td_samplesperpixel; i++)
8ca2e9
+				for (i = 1; i < td->td_samplesperpixel - td->td_extrasamples && i < 3; i++)
8ca2e9
 					fprintf(fd, " %5u",
8ca2e9
 					    td->td_transferfunction[i][l]);
8ca2e9
 				fputc('\n', fd);
8ca2e9
diff --git a/libtiff/tif_unix.c b/libtiff/tif_unix.c
8ca2e9
index b73e80d..5d29040 100644
8ca2e9
--- a/libtiff/tif_unix.c
8ca2e9
+++ b/libtiff/tif_unix.c
8ca2e9
@@ -241,6 +241,15 @@ _TIFFmalloc(tsize_t s)
8ca2e9
 	return (malloc((size_t) s));
8ca2e9
 }
8ca2e9
 
8ca2e9
+void*
8ca2e9
+_TIFFcalloc(tsize_t nmemb, tsize_t siz)
8ca2e9
+{
8ca2e9
+    if( nmemb == 0 || siz == 0 )
8ca2e9
+        return ((void *) NULL);
8ca2e9
+
8ca2e9
+    return calloc((size_t) nmemb, (size_t)siz);
8ca2e9
+}
8ca2e9
+
8ca2e9
 void
8ca2e9
 _TIFFfree(tdata_t p)
8ca2e9
 {
8ca2e9
diff --git a/libtiff/tiffio.h b/libtiff/tiffio.h
8ca2e9
index 06ec25c..3cf8e75 100644
8ca2e9
--- a/libtiff/tiffio.h
8ca2e9
+++ b/libtiff/tiffio.h
8ca2e9
@@ -281,6 +281,7 @@ extern	TIFFCodec* TIFFGetConfiguredCODECs(void);
8ca2e9
  */
8ca2e9
 
8ca2e9
 extern	tdata_t _TIFFmalloc(tsize_t);
8ca2e9
+extern	tdata_t _TIFFcalloc(tsize_t, tsize_t);
8ca2e9
 extern	tdata_t _TIFFrealloc(tdata_t, tsize_t);
8ca2e9
 extern	void _TIFFmemset(tdata_t, int, tsize_t);
8ca2e9
 extern	void _TIFFmemcpy(tdata_t, const tdata_t, tsize_t);
8ca2e9
-- 
8ca2e9
2.17.0
8ca2e9