Merge pull request #3023 from kkopachev/issue-3022

Certain corrupted jpegs can result in no data read
This commit is contained in:
wiredfool 2018-03-21 07:55:17 +00:00 committed by GitHub
commit d173e81798
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 63 additions and 37 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -348,6 +348,22 @@ class TestFileJpeg(PillowTestCase):
filename = "Tests/images/jpeg_ff00_header.jpg"
Image.open(filename)
def test_truncated_jpeg_should_read_all_the_data(self):
filename = "Tests/images/truncated_jpeg.jpg"
ImageFile.LOAD_TRUNCATED_IMAGES = True
im = Image.open(filename)
im.load()
ImageFile.LOAD_TRUNCATED_IMAGES = False
self.assertIsNotNone(im.getbbox())
def test_truncated_jpeg_throws_IOError(self):
filename = "Tests/images/truncated_jpeg.jpg"
im = Image.open(filename)
with self.assertRaises(IOError):
im.load()
def _n_qtables_helper(self, n, test_file):
im = Image.open(test_file)
f = self.tempfile('temp.jpg')

View File

@ -202,44 +202,39 @@ class ImageFile(Image.Image):
for decoder_name, extents, offset, args in self.tile:
decoder = Image._getdecoder(self.mode, decoder_name,
args, self.decoderconfig)
seek(offset)
decoder.setimage(self.im, extents)
if decoder.pulls_fd:
decoder.setfd(self.fp)
status, err_code = decoder.decode(b"")
else:
b = prefix
while True:
try:
s = read(self.decodermaxblock)
except (IndexError, struct.error): # truncated png/gif
if LOAD_TRUNCATED_IMAGES:
try:
seek(offset)
decoder.setimage(self.im, extents)
if decoder.pulls_fd:
decoder.setfd(self.fp)
status, err_code = decoder.decode(b"")
else:
b = prefix
while True:
try:
s = read(self.decodermaxblock)
except (IndexError, struct.error): # truncated png/gif
if LOAD_TRUNCATED_IMAGES:
break
else:
raise IOError("image file is truncated")
if not s: # truncated jpeg
if LOAD_TRUNCATED_IMAGES:
break
else:
self.tile = []
raise IOError("image file is truncated "
"(%d bytes not processed)" % len(b))
b = b + s
n, err_code = decoder.decode(b)
if n < 0:
break
else:
raise IOError("image file is truncated")
if not s: # truncated jpeg
self.tile = []
# JpegDecode needs to clean things up here either way
# If we don't destroy the decompressor,
# we have a memory leak.
decoder.cleanup()
if LOAD_TRUNCATED_IMAGES:
break
else:
raise IOError("image file is truncated "
"(%d bytes not processed)" % len(b))
b = b + s
n, err_code = decoder.decode(b)
if n < 0:
break
b = b[n:]
# Need to cleanup here to prevent leaks in PyPy
decoder.cleanup()
b = b[n:]
finally:
# Need to cleanup here to prevent leaks
decoder.cleanup()
self.tile = []
self.readonly = readonly

View File

@ -353,6 +353,21 @@ class JpegImageFile(ImageFile.ImageFile):
else:
raise SyntaxError("no marker found")
def load_read(self, read_bytes):
"""
internal: read more image data
For premature EOF and LOAD_TRUNCATED_IMAGES adds EOI marker
so libjpeg can finish decoding
"""
s = self.fp.read(read_bytes)
if not s and ImageFile.LOAD_TRUNCATED_IMAGES:
# Premature EOF.
# Pretend file is finished adding EOI marker
return b"\xFF\xD9"
return s
def draft(self, mode, size):
if len(self.tile) != 1: