mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 01:47:47 +03:00 
			
		
		
		
	Merge pull request #2541 from uploadcare/fix-truncated-png-loading
Fix truncated png loading
This commit is contained in:
		
						commit
						5a671830d8
					
				| 
						 | 
					@ -113,7 +113,8 @@ class ChunkStream(object):
 | 
				
			||||||
            length = i32(s)
 | 
					            length = i32(s)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not is_cid(cid):
 | 
					        if not is_cid(cid):
 | 
				
			||||||
            raise SyntaxError("broken PNG file (chunk %s)" % repr(cid))
 | 
					            if not ImageFile.LOAD_TRUNCATED_IMAGES:
 | 
				
			||||||
 | 
					                raise SyntaxError("broken PNG file (chunk %s)" % repr(cid))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return cid, pos, length
 | 
					        return cid, pos, length
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,14 +1,16 @@
 | 
				
			||||||
from helper import unittest, PillowTestCase, hopper
 | 
					from helper import unittest, PillowTestCase, hopper
 | 
				
			||||||
 | 
					from PIL import Image, ImageFile, PngImagePlugin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from io import BytesIO
 | 
					from io import BytesIO
 | 
				
			||||||
 | 
					 | 
				
			||||||
from PIL import Image
 | 
					 | 
				
			||||||
from PIL import ImageFile
 | 
					 | 
				
			||||||
from PIL import PngImagePlugin
 | 
					 | 
				
			||||||
import zlib
 | 
					import zlib
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
codecs = dir(Image.core)
 | 
					codecs = dir(Image.core)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# For Truncated phng memory leak
 | 
				
			||||||
 | 
					MEM_LIMIT = 1  # max increase in MB
 | 
				
			||||||
 | 
					ITERATIONS = 100
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# sample png stream
 | 
					# sample png stream
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST_PNG_FILE = "Tests/images/hopper.png"
 | 
					TEST_PNG_FILE = "Tests/images/hopper.png"
 | 
				
			||||||
| 
						 | 
					@ -530,5 +532,33 @@ class TestFilePng(PillowTestCase):
 | 
				
			||||||
        self.assertLess(chunks.index(b"pHYs"), chunks.index(b"IDAT"))
 | 
					        self.assertLess(chunks.index(b"pHYs"), chunks.index(b"IDAT"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS")
 | 
				
			||||||
 | 
					class TestTruncatedPngPLeaks(PillowTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setUp(self):
 | 
				
			||||||
 | 
					        if "zip_encoder" not in codecs or "zip_decoder" not in codecs:
 | 
				
			||||||
 | 
					            self.skipTest("zip/deflate support not available")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_mem_usage(self):
 | 
				
			||||||
 | 
					        from resource import getpagesize, getrusage, RUSAGE_SELF
 | 
				
			||||||
 | 
					        mem = getrusage(RUSAGE_SELF).ru_maxrss
 | 
				
			||||||
 | 
					        return mem * getpagesize() / 1024 / 1024
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_leak_load(self):
 | 
				
			||||||
 | 
					        with open('Tests/images/hopper.png', 'rb') as f:
 | 
				
			||||||
 | 
					            DATA = BytesIO(f.read(16 * 1024))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ImageFile.LOAD_TRUNCATED_IMAGES = True
 | 
				
			||||||
 | 
					        start_mem = self._get_mem_usage()
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            for _ in range(ITERATIONS):
 | 
				
			||||||
 | 
					                with Image.open(DATA) as im:
 | 
				
			||||||
 | 
					                    im.load()
 | 
				
			||||||
 | 
					                mem = (self._get_mem_usage() - start_mem)
 | 
				
			||||||
 | 
					                self.assertLess(mem, MEM_LIMIT, msg='memory usage limit exceeded')
 | 
				
			||||||
 | 
					        finally:
 | 
				
			||||||
 | 
					            ImageFile.LOAD_TRUNCATED_IMAGES = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == '__main__':
 | 
					if __name__ == '__main__':
 | 
				
			||||||
    unittest.main()
 | 
					    unittest.main()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										1
									
								
								decode.c
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								decode.c
									
									
									
									
									
								
							| 
						 | 
					@ -779,6 +779,7 @@ PyImaging_ZipDecoderNew(PyObject* self, PyObject* args)
 | 
				
			||||||
        return NULL;
 | 
					        return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    decoder->decode = ImagingZipDecode;
 | 
					    decoder->decode = ImagingZipDecode;
 | 
				
			||||||
 | 
					    decoder->cleanup = ImagingZipDecodeCleanup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ((ZIPSTATE*)decoder->state.context)->interlaced = interlaced;
 | 
					    ((ZIPSTATE*)decoder->state.context)->interlaced = interlaced;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -455,6 +455,7 @@ extern int ImagingXbmEncode(Imaging im, ImagingCodecState state,
 | 
				
			||||||
#ifdef	HAVE_LIBZ
 | 
					#ifdef	HAVE_LIBZ
 | 
				
			||||||
extern int ImagingZipDecode(Imaging im, ImagingCodecState state,
 | 
					extern int ImagingZipDecode(Imaging im, ImagingCodecState state,
 | 
				
			||||||
			    UINT8* buffer, int bytes);
 | 
								    UINT8* buffer, int bytes);
 | 
				
			||||||
 | 
					extern int ImagingZipDecodeCleanup(ImagingCodecState state);
 | 
				
			||||||
extern int ImagingZipEncode(Imaging im, ImagingCodecState state,
 | 
					extern int ImagingZipEncode(Imaging im, ImagingCodecState state,
 | 
				
			||||||
			    UINT8* buffer, int bytes);
 | 
								    UINT8* buffer, int bytes);
 | 
				
			||||||
extern int ImagingZipEncodeCleanup(ImagingCodecState state);
 | 
					extern int ImagingZipEncodeCleanup(ImagingCodecState state);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -85,6 +85,8 @@ ImagingZipDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
 | 
				
			||||||
        err = inflateInit(&context->z_stream);
 | 
					        err = inflateInit(&context->z_stream);
 | 
				
			||||||
        if (err < 0) {
 | 
					        if (err < 0) {
 | 
				
			||||||
            state->errcode = IMAGING_CODEC_CONFIG;
 | 
					            state->errcode = IMAGING_CODEC_CONFIG;
 | 
				
			||||||
 | 
					            free(context->previous);
 | 
				
			||||||
 | 
					            context->previous = NULL;
 | 
				
			||||||
            return -1;
 | 
					            return -1;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -126,6 +128,7 @@ ImagingZipDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
                state->errcode = IMAGING_CODEC_CONFIG;
 | 
					                state->errcode = IMAGING_CODEC_CONFIG;
 | 
				
			||||||
            free(context->previous);
 | 
					            free(context->previous);
 | 
				
			||||||
 | 
					            context->previous = NULL;
 | 
				
			||||||
            inflateEnd(&context->z_stream);
 | 
					            inflateEnd(&context->z_stream);
 | 
				
			||||||
            return -1;
 | 
					            return -1;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -191,6 +194,7 @@ ImagingZipDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
 | 
				
			||||||
            default:
 | 
					            default:
 | 
				
			||||||
                state->errcode = IMAGING_CODEC_UNKNOWN;
 | 
					                state->errcode = IMAGING_CODEC_UNKNOWN;
 | 
				
			||||||
                free(context->previous);
 | 
					                free(context->previous);
 | 
				
			||||||
 | 
					                context->previous = NULL;
 | 
				
			||||||
                inflateEnd(&context->z_stream);
 | 
					                inflateEnd(&context->z_stream);
 | 
				
			||||||
                return -1;
 | 
					                return -1;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -258,6 +262,7 @@ ImagingZipDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
 | 
				
			||||||
                state->errcode = IMAGING_CODEC_BROKEN; */
 | 
					                state->errcode = IMAGING_CODEC_BROKEN; */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            free(context->previous);
 | 
					            free(context->previous);
 | 
				
			||||||
 | 
					            context->previous = NULL;
 | 
				
			||||||
            inflateEnd(&context->z_stream);
 | 
					            inflateEnd(&context->z_stream);
 | 
				
			||||||
            return -1; /* end of file (errcode=0) */
 | 
					            return -1; /* end of file (errcode=0) */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -274,4 +279,20 @@ ImagingZipDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int ImagingZipDecodeCleanup(ImagingCodecState state){
 | 
				
			||||||
 | 
					    /* called to free the decompression engine when the decode terminates
 | 
				
			||||||
 | 
					       due to a corrupt or truncated image
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    ZIPSTATE* context = (ZIPSTATE*) state->context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Clean up */
 | 
				
			||||||
 | 
					    if (context->previous) {
 | 
				
			||||||
 | 
					        inflateEnd(&context->z_stream);
 | 
				
			||||||
 | 
					        free(context->previous);
 | 
				
			||||||
 | 
					        context->previous = NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return -1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user