diff --git a/Tests/check_tiff_crashes.py b/Tests/check_tiff_crashes.py new file mode 100644 index 000000000..f4eb04375 --- /dev/null +++ b/Tests/check_tiff_crashes.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +# Reproductions/tests for crashes/read errors in TiffDecode.c + +# When run in python, all of these images should fail for +# one reason or another, either as a buffer overrun, +# unrecognized datastream, or truncated image file. +# There shouldn't be any segfaults. +# +# if run like +# `valgrind --tool=memcheck python check_tiff_crashes.py 2>&1 | grep TiffDecode.c` +# the output should be empty. There may be python issues +# in the valgrind especially if run in a debug python +# version. + + +from PIL import Image + +repro_read_strip = ( + "images/crash_1.tif", + "images/crash_2.tif", +) + +for path in repro_read_strip: + with Image.open(path) as im: + try: + im.load() + except Exception as msg: + print(msg) diff --git a/Tests/images/crash_1.tif b/Tests/images/crash_1.tif new file mode 100644 index 000000000..230d4439a Binary files /dev/null and b/Tests/images/crash_1.tif differ diff --git a/Tests/images/crash_2.tif b/Tests/images/crash_2.tif new file mode 100644 index 000000000..26c00d0ff Binary files /dev/null and b/Tests/images/crash_2.tif differ diff --git a/src/libImaging/TiffDecode.c b/src/libImaging/TiffDecode.c index c3df1174e..532db1f68 100644 --- a/src/libImaging/TiffDecode.c +++ b/src/libImaging/TiffDecode.c @@ -171,7 +171,7 @@ int ImagingLibTiffInit(ImagingCodecState state, int fp, uint32 offset) { int ReadTile(TIFF* tiff, UINT32 col, UINT32 row, UINT32* buffer) { - uint16 photometric; + uint16 photometric = 0; TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric); @@ -228,7 +228,7 @@ int ReadTile(TIFF* tiff, UINT32 col, UINT32 row, UINT32* buffer) { } int ReadStrip(TIFF* tiff, UINT32 row, UINT32* buffer) { - uint16 photometric; + uint16 photometric = 0; // init to not PHOTOMETRIC_YCBCR TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric); // To avoid dealing with YCbCr subsampling, let libtiff handle it @@ -363,6 +363,13 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, Py_ state->bytes = row_byte_size * tile_length; + if (TIFFTileSize(tiff) > state->bytes) { + // If the strip size as expected by LibTiff isn't what we're expecting, abort. + state->errcode = IMAGING_CODEC_MEMORY; + TIFFClose(tiff); + return -1; + } + /* realloc to fit whole tile */ /* malloc check above */ new_data = realloc (state->buffer, state->bytes); @@ -424,11 +431,21 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, Py_ TIFFClose(tiff); return -1; } - + state->bytes = rows_per_strip * row_byte_size; TRACE(("StripSize: %d \n", state->bytes)); + if (TIFFStripSize(tiff) > state->bytes) { + // If the strip size as expected by LibTiff isn't what we're expecting, abort. + // man: TIFFStripSize returns the equivalent size for a strip of data as it would be returned in a + // call to TIFFReadEncodedStrip ... + + state->errcode = IMAGING_CODEC_MEMORY; + TIFFClose(tiff); + return -1; + } + /* realloc to fit whole strip */ /* malloc check above */ new_data = realloc (state->buffer, state->bytes);