diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index 2110aa637..8bfdc2ac7 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -72,6 +72,13 @@ _MODES = { _simple_palette = re.compile(b'^\xff+\x00\xff*$') +def _safe_zlib_decompress(s): + dobj = zlib.decompressobj() + plaintext = dobj.decompress(s, ImageFile.SAFEBLOCK) + if dobj.unconsumed_tail: + raise ValueError("Decompressed Data Too Large") + return plaintext + # -------------------------------------------------------------------- # Support classes. Suitable for PNG and related formats like MNG etc. @@ -184,7 +191,6 @@ class PngInfo: tkey = tkey.encode("utf-8", "strict") if zip: - import zlib self.add(b"iTXt", key + b"\0\x01\0" + lang + b"\0" + tkey + b"\0" + zlib.compress(value)) else: @@ -206,7 +212,6 @@ class PngInfo: key = key.encode('latin-1', 'strict') if zip: - import zlib self.add(b"zTXt", key + b"\0\0" + zlib.compress(value)) else: self.add(b"tEXt", key + b"\0" + value) @@ -247,7 +252,7 @@ class PngStream(ChunkStream): raise SyntaxError("Unknown compression method %s in iCCP chunk" % comp_method) try: - icc_profile = zlib.decompress(s[i+2:]) + icc_profile = _safe_zlib_decompress(s[i+2:]) except zlib.error: icc_profile = None # FIXME self.im_info["icc_profile"] = icc_profile @@ -359,9 +364,8 @@ class PngStream(ChunkStream): if comp_method != 0: raise SyntaxError("Unknown compression method %s in zTXt chunk" % comp_method) - import zlib try: - v = zlib.decompress(v[1:]) + v = _safe_zlib_decompress(v[1:]) except zlib.error: v = b"" @@ -390,9 +394,8 @@ class PngStream(ChunkStream): return s if cf != 0: if cm == 0: - import zlib try: - v = zlib.decompress(v) + v = _safe_zlib_decompress(v) except zlib.error: return s else: diff --git a/Tests/check_png_dos.py b/Tests/check_png_dos.py new file mode 100644 index 000000000..4e9b76537 --- /dev/null +++ b/Tests/check_png_dos.py @@ -0,0 +1,24 @@ +from helper import unittest, PillowTestCase +import sys +from PIL import Image +from io import BytesIO + +test_file = "Tests/images/png_decompression_dos.png" + +@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS") +class TestPngDos(PillowTestCase): + + def test_dos_text(self): + + try: + im = Image.open(test_file) + im.load() + except ValueError as msg: + self.assert_(msg, "Decompressed Data Too Large") + return + + for s in im.text.values(): + self.assert_(len(s) < 1024*1024, "Text chunk larger than 1M") + +if __name__ == '__main__': + unittest.main() diff --git a/Tests/images/png_decompression_dos.png b/Tests/images/png_decompression_dos.png new file mode 100644 index 000000000..986561b2e Binary files /dev/null and b/Tests/images/png_decompression_dos.png differ