Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix 2 buffer overflows in TIFF decoding #4507

Merged
merged 5 commits into from Apr 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 29 additions & 0 deletions 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)
Binary file added Tests/images/crash_1.tif
Binary file not shown.
Binary file added Tests/images/crash_2.tif
Binary file not shown.
23 changes: 20 additions & 3 deletions src/libImaging/TiffDecode.c
Expand Up @@ -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);

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down