Skip to content

Commit 46f4a34

Browse files
authored
Merge pull request #4507 from hugovk/fix_tiff
Fix 2 buffer overflows in TIFF decoding
2 parents ff60894 + 2092801 commit 46f4a34

File tree

4 files changed

+49
-3
lines changed

4 files changed

+49
-3
lines changed

Diff for: Tests/check_tiff_crashes.py

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/usr/bin/env python
2+
3+
# Reproductions/tests for crashes/read errors in TiffDecode.c
4+
5+
# When run in python, all of these images should fail for
6+
# one reason or another, either as a buffer overrun,
7+
# unrecognized datastream, or truncated image file.
8+
# There shouldn't be any segfaults.
9+
#
10+
# if run like
11+
# `valgrind --tool=memcheck python check_tiff_crashes.py 2>&1 | grep TiffDecode.c`
12+
# the output should be empty. There may be python issues
13+
# in the valgrind especially if run in a debug python
14+
# version.
15+
16+
17+
from PIL import Image
18+
19+
repro_read_strip = (
20+
"images/crash_1.tif",
21+
"images/crash_2.tif",
22+
)
23+
24+
for path in repro_read_strip:
25+
with Image.open(path) as im:
26+
try:
27+
im.load()
28+
except Exception as msg:
29+
print(msg)

Diff for: Tests/images/crash_1.tif

6.36 KB
Binary file not shown.

Diff for: Tests/images/crash_2.tif

6.08 KB
Binary file not shown.

Diff for: src/libImaging/TiffDecode.c

+20-3
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ int ImagingLibTiffInit(ImagingCodecState state, int fp, uint32 offset) {
171171

172172

173173
int ReadTile(TIFF* tiff, UINT32 col, UINT32 row, UINT32* buffer) {
174-
uint16 photometric;
174+
uint16 photometric = 0;
175175

176176
TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric);
177177

@@ -228,7 +228,7 @@ int ReadTile(TIFF* tiff, UINT32 col, UINT32 row, UINT32* buffer) {
228228
}
229229

230230
int ReadStrip(TIFF* tiff, UINT32 row, UINT32* buffer) {
231-
uint16 photometric;
231+
uint16 photometric = 0; // init to not PHOTOMETRIC_YCBCR
232232
TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric);
233233

234234
// To avoid dealing with YCbCr subsampling, let libtiff handle it
@@ -363,6 +363,13 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, Py_
363363

364364
state->bytes = row_byte_size * tile_length;
365365

366+
if (TIFFTileSize(tiff) > state->bytes) {
367+
// If the strip size as expected by LibTiff isn't what we're expecting, abort.
368+
state->errcode = IMAGING_CODEC_MEMORY;
369+
TIFFClose(tiff);
370+
return -1;
371+
}
372+
366373
/* realloc to fit whole tile */
367374
/* malloc check above */
368375
new_data = realloc (state->buffer, state->bytes);
@@ -424,11 +431,21 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, Py_
424431
TIFFClose(tiff);
425432
return -1;
426433
}
427-
434+
428435
state->bytes = rows_per_strip * row_byte_size;
429436

430437
TRACE(("StripSize: %d \n", state->bytes));
431438

439+
if (TIFFStripSize(tiff) > state->bytes) {
440+
// If the strip size as expected by LibTiff isn't what we're expecting, abort.
441+
// man: TIFFStripSize returns the equivalent size for a strip of data as it would be returned in a
442+
// call to TIFFReadEncodedStrip ...
443+
444+
state->errcode = IMAGING_CODEC_MEMORY;
445+
TIFFClose(tiff);
446+
return -1;
447+
}
448+
432449
/* realloc to fit whole strip */
433450
/* malloc check above */
434451
new_data = realloc (state->buffer, state->bytes);

0 commit comments

Comments
 (0)