mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 01:47:47 +03:00 
			
		
		
		
	Limit total text chunk size to 64k
This commit is contained in:
		
							parent
							
								
									b73c4b9e8b
								
							
						
					
					
						commit
						0b75526ffe
					
				| 
						 | 
					@ -72,9 +72,15 @@ _MODES = {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_simple_palette = re.compile(b'^\xff+\x00\xff*$')
 | 
					_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):
 | 
					def _safe_zlib_decompress(s):
 | 
				
			||||||
    dobj = zlib.decompressobj()
 | 
					    dobj = zlib.decompressobj()
 | 
				
			||||||
    plaintext = dobj.decompress(s, ImageFile.SAFEBLOCK)
 | 
					    plaintext = dobj.decompress(s, MAX_TEXT_CHUNK)
 | 
				
			||||||
    if dobj.unconsumed_tail:
 | 
					    if dobj.unconsumed_tail:
 | 
				
			||||||
        raise ValueError("Decompressed Data Too Large")
 | 
					        raise ValueError("Decompressed Data Too Large")
 | 
				
			||||||
    return plaintext
 | 
					    return plaintext
 | 
				
			||||||
| 
						 | 
					@ -267,6 +273,14 @@ class PngStream(ChunkStream):
 | 
				
			||||||
        self.im_tile = None
 | 
					        self.im_tile = None
 | 
				
			||||||
        self.im_palette = 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):
 | 
					    def chunk_iCCP(self, pos, length):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # ICC profile
 | 
					        # ICC profile
 | 
				
			||||||
| 
						 | 
					@ -379,6 +393,8 @@ class PngStream(ChunkStream):
 | 
				
			||||||
                v = v.decode('latin-1', 'replace')
 | 
					                v = v.decode('latin-1', 'replace')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.im_info[k] = self.im_text[k] = v
 | 
					            self.im_info[k] = self.im_text[k] = v
 | 
				
			||||||
 | 
					            self.check_text_memory(len(v))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return s
 | 
					        return s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def chunk_zTXt(self, pos, length):
 | 
					    def chunk_zTXt(self, pos, length):
 | 
				
			||||||
| 
						 | 
					@ -408,6 +424,8 @@ class PngStream(ChunkStream):
 | 
				
			||||||
                v = v.decode('latin-1', 'replace')
 | 
					                v = v.decode('latin-1', 'replace')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.im_info[k] = self.im_text[k] = v
 | 
					            self.im_info[k] = self.im_text[k] = v
 | 
				
			||||||
 | 
					            self.check_text_memory(len(v))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return s
 | 
					        return s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def chunk_iTXt(self, pos, length):
 | 
					    def chunk_iTXt(self, pos, length):
 | 
				
			||||||
| 
						 | 
					@ -443,7 +461,8 @@ class PngStream(ChunkStream):
 | 
				
			||||||
                return s
 | 
					                return s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.im_info[k] = self.im_text[k] = iTXt(v, lang, tk)
 | 
					        self.im_info[k] = self.im_text[k] = iTXt(v, lang, tk)
 | 
				
			||||||
 | 
					        self.check_text_memory(len(v))
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
        return s
 | 
					        return s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,13 +1,12 @@
 | 
				
			||||||
from helper import unittest, PillowTestCase
 | 
					from helper import unittest, PillowTestCase
 | 
				
			||||||
import sys
 | 
					import sys
 | 
				
			||||||
from PIL import Image
 | 
					from PIL import Image, PngImagePlugin
 | 
				
			||||||
from io import BytesIO
 | 
					from io import BytesIO
 | 
				
			||||||
 | 
					import zlib
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test_file = "Tests/images/png_decompression_dos.png"
 | 
					test_file = "Tests/images/png_decompression_dos.png"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS")
 | 
					 | 
				
			||||||
class TestPngDos(PillowTestCase):
 | 
					class TestPngDos(PillowTestCase):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_dos_text(self):
 | 
					    def test_dos_text(self):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
| 
						 | 
					@ -20,5 +19,30 @@ class TestPngDos(PillowTestCase):
 | 
				
			||||||
        for s in im.text.values():
 | 
					        for s in im.text.values():
 | 
				
			||||||
            self.assert_(len(s) < 1024*1024, "Text chunk larger than 1M")
 | 
					            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__':
 | 
					if __name__ == '__main__':
 | 
				
			||||||
    unittest.main()
 | 
					    unittest.main()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user