Merge pull request #6253 from radarhere/png_chunk_length

Raise ValueError if PNG chunks are truncated
This commit is contained in:
Hugo van Kemenade 2022-05-07 00:29:38 +03:00 committed by GitHub
commit 6b05a28482
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 32 additions and 0 deletions

View File

@ -635,6 +635,17 @@ class TestFilePng:
assert_image_equal_tofile(im, "Tests/images/bw_gradient.png") 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): def test_specify_bits(self, tmp_path):
im = hopper("P") im = hopper("P")

View File

@ -424,6 +424,10 @@ class PngStream(ChunkStream):
# image header # image header
s = ImageFile._safe_read(self.fp, length) 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) self.im_size = i32(s, 0), i32(s, 4)
try: try:
self.im_mode, self.im_rawmode = _MODES[(s[8], s[9])] self.im_mode, self.im_rawmode = _MODES[(s[8], s[9])]
@ -512,6 +516,10 @@ class PngStream(ChunkStream):
# pixels per unit # pixels per unit
s = ImageFile._safe_read(self.fp, length) 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) px, py = i32(s, 0), i32(s, 4)
unit = s[8] unit = s[8]
if unit == 1: # meter if unit == 1: # meter
@ -624,6 +632,10 @@ class PngStream(ChunkStream):
# APNG chunks # APNG chunks
def chunk_acTL(self, pos, length): def chunk_acTL(self, pos, length):
s = ImageFile._safe_read(self.fp, 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: if self.im_n_frames is not None:
self.im_n_frames = None self.im_n_frames = None
warnings.warn("Invalid APNG, will use default PNG image if possible") warnings.warn("Invalid APNG, will use default PNG image if possible")
@ -639,6 +651,10 @@ class PngStream(ChunkStream):
def chunk_fcTL(self, pos, length): def chunk_fcTL(self, pos, length):
s = ImageFile._safe_read(self.fp, 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) seq = i32(s)
if (self._seq_num is None and seq != 0) or ( if (self._seq_num is None and seq != 0) or (
self._seq_num is not None and self._seq_num != seq - 1 self._seq_num is not None and self._seq_num != seq - 1
@ -660,6 +676,11 @@ class PngStream(ChunkStream):
return s return s
def chunk_fdAT(self, pos, length): 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) s = ImageFile._safe_read(self.fp, 4)
seq = i32(s) seq = i32(s)
if self._seq_num != seq - 1: if self._seq_num != seq - 1: