Hi,

The overflow happens during the following call to memcpy:

    // convert to strip
    if(x + tileWidth > width) {
            src_line = imageRowSize - rowSize;
    } else {
            src_line = tileRowSize;
    }
    BYTE *src_bits = tileBuffer;
    BYTE *dst_bits = bits + rowSize;
    for(int k = 0; k < nrows; k++) {
            memcpy(dst_bits, src_bits, src_line);
            src_bits += tileRowSize;
            dst_bits -= dst_pitch;
    }

This portion of code copies image data from a libTIFF-provided buffer to an
internal buffer. The overflow happens because src_line is larger than the
size of dst_bits.

This is the result of an inconsistency between libTIFF and freeimage:

In the libTIFF case, tile row size is
= samplesperpixel * bitspersample * tilewidth / 8
= bitsperpixel * tilewidth / 8
= 6 * 32 * 7 / 8 = 168

In the freeimage case, tile row size is
bitsperpixel * tilewidth / 8
= 32 * 7 / 8 = 28

As a result, the two buffers are differently sized.

freeimage has a bpp of 32 because CreateImageType calls
FreeImage_AllocateHeader with MIN(bpp, 32).

This 'MIN(bpp, 32)' looks like a terrible hack to me, but we can't change
it to 'bpp' because FIT_BITMAP images with bpp > 32 does not seem to be
supported by freeimage. Also, in this case, bpp > 32 doesn't even make
sense:

Looking closely at the reproducer, we can notice that it defines a bilevel
image with samplesperpixel and bitspersample parameters, both unexpected in
bilevel images.

Pixels in bilevel images can either be black or white. There is as such
only one sample per pixel, and a single bit per sample is sufficient.  The
spec defines bpp = 8. It is unclear whether the specification allows for
arbitrary values of bitspersample or samplesperpixel (extrasamples?) in
this case.

This file gets rejected by most libTIFF tools.

# patch

+ add check to CreateImageType() to reject FIT_BITMAP images with bpp > 32
  instead of passing MIN(bpp, 32).
+ change type of dst_pitch to unsigned
+ call memcpy with MIN(dst_pitch, src_line) instead of src_line. this will
  help overcome any further (future) discrepancy between libTIFF and
  freeimage.

# tests

I have tested for regressions with the following samples, using a modified
version of Examples/Linux/linux-gtk.c:

http://www.simplesystems.org/libtiff/images.html

During these tests, I found other issues with bilevel images, unrelated to
this patch. I will try to take a look at them in the future.

I can provide additional explanations if there is anything unclear.

I'd like to get this patch peer-reviewed/merged upstream before shipping
it in a Debian release.

regards,
Hugo

-- 
                Hugo Lefeuvre (hle)    |    www.owl.eu.com
RSA4096_ 360B 03B3 BF27 4F4D 7A3F D5E8 14AA 1EB8 A247 3DFD
ed25519_ 37B2 6D38 0B25 B8A2 6B9F 3A65 A36F 5357 5F2D DC4C
Description: fix heap buffer overflow when bpp > 32 and fit == FIT_BITMAP
 + add check to CreateImageType() to reject FIT_BITMAP images with bpp > 32
   instead of passing MIN(bpp, 32).
 + change type of dst_pitch to unsigned.
 + call memcpy with MIN(dst_pitch, src_line) instead of src_line. this will
   help overcome any further (future) discrepancy between libTIFF and
   freeimage.
Author: Hugo Lefeuvre <h...@debian.org>
Bug-Debian: https://bugs.debian.org/929597
--- a/Source/FreeImage/PluginTIFF.cpp	2019-10-26 14:21:39.329052757 +0200
+++ b/Source/FreeImage/PluginTIFF.cpp	2019-10-26 15:03:18.597957090 +0200
@@ -461,8 +461,12 @@
 			
 		}
 		else {
+			if(bpp > 32) {
+				// check for malicious images
+				return NULL;
+			}
 
-			dib = FreeImage_AllocateHeader(header_only, width, height, MIN(bpp, 32), FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+			dib = FreeImage_AllocateHeader(header_only, width, height, bpp, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
 		}
 
 
@@ -2041,7 +2045,7 @@
 				}
 
 				// calculate src line and dst pitch
-				int dst_pitch = FreeImage_GetPitch(dib);
+				unsigned int dst_pitch = FreeImage_GetPitch(dib);
 				uint32 tileRowSize = (uint32)TIFFTileRowSize(tif);
 				uint32 imageRowSize = (uint32)TIFFScanlineSize(tif);
 
@@ -2071,7 +2075,7 @@
 						BYTE *src_bits = tileBuffer;
 						BYTE *dst_bits = bits + rowSize;
 						for(int k = 0; k < nrows; k++) {
-							memcpy(dst_bits, src_bits, src_line);
+							memcpy(dst_bits, src_bits, MIN(dst_pitch, src_line));
 							src_bits += tileRowSize;
 							dst_bits -= dst_pitch;
 						}

Attachment: signature.asc
Description: PGP signature

Reply via email to