mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 09:57:43 +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*$')
 | 
			
		||||
 | 
			
		||||
# 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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user