Limit total text chunk size to 64k

This commit is contained in:
wiredfool 2014-12-29 17:10:27 -08:00
parent b73c4b9e8b
commit 0b75526ffe
2 changed files with 48 additions and 5 deletions

View File

@ -72,9 +72,15 @@ _MODES = {
_simple_palette = re.compile(b'^\xff+\x00\xff*$')
# Maximum decompressed size for a iTXt or zTXt chunk.
# Eliminates decompression bombs where compressed chunks can expand 1000x
MAX_TEXT_CHUNK = ImageFile.SAFEBLOCK
# Set the maximum total text chunk size.
MAX_TEXT_MEMORY = 64 * MAX_TEXT_CHUNK
def _safe_zlib_decompress(s):
dobj = zlib.decompressobj()
plaintext = dobj.decompress(s, ImageFile.SAFEBLOCK)
plaintext = dobj.decompress(s, MAX_TEXT_CHUNK)
if dobj.unconsumed_tail:
raise ValueError("Decompressed Data Too Large")
return plaintext
@ -267,6 +273,14 @@ class PngStream(ChunkStream):
self.im_tile = None
self.im_palette = None
self.text_memory = 0
def check_text_memory(self, chunklen):
self.text_memory += chunklen
if self.text_memory > MAX_TEXT_MEMORY:
raise ValueError("Too much memory used in text chunks: %s>MAX_TEXT_MEMORY" %
self.text_memory)
def chunk_iCCP(self, pos, length):
# ICC profile
@ -379,6 +393,8 @@ class PngStream(ChunkStream):
v = v.decode('latin-1', 'replace')
self.im_info[k] = self.im_text[k] = v
self.check_text_memory(len(v))
return s
def chunk_zTXt(self, pos, length):
@ -408,6 +424,8 @@ class PngStream(ChunkStream):
v = v.decode('latin-1', 'replace')
self.im_info[k] = self.im_text[k] = v
self.check_text_memory(len(v))
return s
def chunk_iTXt(self, pos, length):
@ -443,6 +461,7 @@ class PngStream(ChunkStream):
return s
self.im_info[k] = self.im_text[k] = iTXt(v, lang, tk)
self.check_text_memory(len(v))
return s

View File

@ -1,13 +1,12 @@
from helper import unittest, PillowTestCase
import sys
from PIL import Image
from PIL import Image, PngImagePlugin
from io import BytesIO
import zlib
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:
@ -20,5 +19,30 @@ class TestPngDos(PillowTestCase):
for s in im.text.values():
self.assert_(len(s) < 1024*1024, "Text chunk larger than 1M")
def test_dos_total_memory(self):
im = Image.new('L',(1,1))
compressed_data = zlib.compress('a'*1024*1023)
info = PngImagePlugin.PngInfo()
for x in range(64):
info.add_text('t%s'%x, compressed_data, 1)
info.add_itxt('i%s'%x, compressed_data, zip=True)
b = BytesIO()
im.save(b, 'PNG', pnginfo=info)
b.seek(0)
try:
im2 = Image.open(b)
except ValueError as msg:
self.assert_("Too much memory" in msg)
return
total_len = 0
for txt in im2.text.values():
total_len += len(txt)
self.assert_(total_len < 64*1024*1024)
if __name__ == '__main__':
unittest.main()