Certain corrupted jpegs can result in no data read

On truncated jpeg, decoder can suspend waiting for additional bytes in
buffer. For some input files, decoder suspends on jpeg_start_decompress
stage. If at this point file reader reaches EOF, py code never gets back
to jpeg decoder and we end up with no bytes to result image. This leaves
us with some amount of potentially useful bytes undecoded and thrown
away.
Libjpeg docs suggest that in such situation, more appropriate would be
to add EOI marker to the end of buffer, which will allows decoder
to finish. https://github.com/libjpeg-turbo/libjpeg-turbo/blob/0dd9a2c1fd6c/libjpeg.txt#L1803-L1809
Docs also mention that adding EOI markers is what non-suspending code
does anyway.
This commit is contained in:
Konstantin Kopachev 2018-02-28 22:15:58 -08:00
parent 1591c530bd
commit add2746ac6
No known key found for this signature in database
GPG Key ID: CECF757E656F4F62

View File

@ -202,6 +202,7 @@ 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)
try:
seek(offset) seek(offset)
decoder.setimage(self.im, extents) decoder.setimage(self.im, extents)
if decoder.pulls_fd: if decoder.pulls_fd:
@ -219,16 +220,10 @@ class ImageFile(Image.Image):
raise IOError("image file is truncated") raise IOError("image file is truncated")
if not s: # truncated jpeg 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: if LOAD_TRUNCATED_IMAGES:
break s = b"\xFF\xD9" # Pretend file is finished adding EOI marker
else: else:
self.tile = []
raise IOError("image file is truncated " raise IOError("image file is truncated "
"(%d bytes not processed)" % len(b)) "(%d bytes not processed)" % len(b))
@ -237,8 +232,8 @@ class ImageFile(Image.Image):
if n < 0: if n < 0:
break break
b = b[n:] b = b[n:]
finally:
# Need to cleanup here to prevent leaks in PyPy # Need to cleanup here to prevent leaks
decoder.cleanup() decoder.cleanup()
self.tile = [] self.tile = []