diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index bb2b0d119..2a40ab7be 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -635,6 +635,17 @@ class TestFilePng: assert_image_equal_tofile(im, "Tests/images/bw_gradient.png") + @pytest.mark.parametrize("cid", (b"IHDR", b"pHYs", b"acTL", b"fcTL", b"fdAT")) + def test_truncated_chunks(self, cid): + fp = BytesIO() + with PngImagePlugin.PngStream(fp) as png: + with pytest.raises(ValueError): + png.call(cid, 0, 0) + + ImageFile.LOAD_TRUNCATED_IMAGES = True + png.call(cid, 0, 0) + ImageFile.LOAD_TRUNCATED_IMAGES = False + def test_specify_bits(self, tmp_path): im = hopper("P") diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index c939b86e7..01b4fd9ce 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -424,6 +424,10 @@ class PngStream(ChunkStream): # image header s = ImageFile._safe_read(self.fp, length) + if length < 13: + if ImageFile.LOAD_TRUNCATED_IMAGES: + return s + raise ValueError("Truncated IHDR chunk") self.im_size = i32(s, 0), i32(s, 4) try: self.im_mode, self.im_rawmode = _MODES[(s[8], s[9])] @@ -512,6 +516,10 @@ class PngStream(ChunkStream): # pixels per unit s = ImageFile._safe_read(self.fp, length) + if length < 9: + if ImageFile.LOAD_TRUNCATED_IMAGES: + return s + raise ValueError("Truncated pHYs chunk") px, py = i32(s, 0), i32(s, 4) unit = s[8] if unit == 1: # meter @@ -624,6 +632,10 @@ class PngStream(ChunkStream): # APNG chunks def chunk_acTL(self, pos, length): s = ImageFile._safe_read(self.fp, length) + if length < 8: + if ImageFile.LOAD_TRUNCATED_IMAGES: + return s + raise ValueError("APNG contains truncated acTL chunk") if self.im_n_frames is not None: self.im_n_frames = None warnings.warn("Invalid APNG, will use default PNG image if possible") @@ -639,6 +651,10 @@ class PngStream(ChunkStream): def chunk_fcTL(self, pos, length): s = ImageFile._safe_read(self.fp, length) + if length < 26: + if ImageFile.LOAD_TRUNCATED_IMAGES: + return s + raise ValueError("APNG contains truncated fcTL chunk") seq = i32(s) if (self._seq_num is None and seq != 0) or ( self._seq_num is not None and self._seq_num != seq - 1 @@ -660,6 +676,11 @@ class PngStream(ChunkStream): return s def chunk_fdAT(self, pos, length): + if length < 4: + if ImageFile.LOAD_TRUNCATED_IMAGES: + s = ImageFile._safe_read(self.fp, length) + return s + raise ValueError("APNG contains truncated fDAT chunk") s = ImageFile._safe_read(self.fp, 4) seq = i32(s) if self._seq_num != seq - 1: