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" filename = "Tests/images/jpeg_ff00_header.jpg"
Image.open(filename) 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): def _n_qtables_helper(self, n, test_file):
im = Image.open(test_file) im = Image.open(test_file)
f = self.tempfile('temp.jpg') f = self.tempfile('temp.jpg')

View File

@ -202,44 +202,39 @@ class ImageFile(Image.Image):
for decoder_name, extents, offset, args in self.tile: for decoder_name, extents, offset, args in self.tile:
decoder = Image._getdecoder(self.mode, decoder_name, decoder = Image._getdecoder(self.mode, decoder_name,
args, self.decoderconfig) args, self.decoderconfig)
seek(offset) try:
decoder.setimage(self.im, extents) seek(offset)
if decoder.pulls_fd: decoder.setimage(self.im, extents)
decoder.setfd(self.fp) if decoder.pulls_fd:
status, err_code = decoder.decode(b"") decoder.setfd(self.fp)
else: status, err_code = decoder.decode(b"")
b = prefix else:
while True: b = prefix
try: while True:
s = read(self.decodermaxblock) try:
except (IndexError, struct.error): # truncated png/gif s = read(self.decodermaxblock)
if LOAD_TRUNCATED_IMAGES: 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 break
else: b = b[n:]
raise IOError("image file is truncated") finally:
# Need to cleanup here to prevent leaks
if not s: # truncated jpeg decoder.cleanup()
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()
self.tile = [] self.tile = []
self.readonly = readonly self.readonly = readonly

View File

@ -353,6 +353,21 @@ class JpegImageFile(ImageFile.ImageFile):
else: else:
raise SyntaxError("no marker found") 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): def draft(self, mode, size):
if len(self.tile) != 1: if len(self.tile) != 1: