Skip to content

Commit 6b05a28

Browse files
authored
Merge pull request #6253 from radarhere/png_chunk_length
Raise ValueError if PNG chunks are truncated
2 parents f846849 + 51bdc99 commit 6b05a28

File tree

2 files changed

+32
-0
lines changed

2 files changed

+32
-0
lines changed

Tests/test_file_png.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,17 @@ def test_padded_idat(self):
635635

636636
assert_image_equal_tofile(im, "Tests/images/bw_gradient.png")
637637

638+
@pytest.mark.parametrize("cid", (b"IHDR", b"pHYs", b"acTL", b"fcTL", b"fdAT"))
639+
def test_truncated_chunks(self, cid):
640+
fp = BytesIO()
641+
with PngImagePlugin.PngStream(fp) as png:
642+
with pytest.raises(ValueError):
643+
png.call(cid, 0, 0)
644+
645+
ImageFile.LOAD_TRUNCATED_IMAGES = True
646+
png.call(cid, 0, 0)
647+
ImageFile.LOAD_TRUNCATED_IMAGES = False
648+
638649
def test_specify_bits(self, tmp_path):
639650
im = hopper("P")
640651

src/PIL/PngImagePlugin.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,10 @@ def chunk_IHDR(self, pos, length):
424424

425425
# image header
426426
s = ImageFile._safe_read(self.fp, length)
427+
if length < 13:
428+
if ImageFile.LOAD_TRUNCATED_IMAGES:
429+
return s
430+
raise ValueError("Truncated IHDR chunk")
427431
self.im_size = i32(s, 0), i32(s, 4)
428432
try:
429433
self.im_mode, self.im_rawmode = _MODES[(s[8], s[9])]
@@ -512,6 +516,10 @@ def chunk_pHYs(self, pos, length):
512516

513517
# pixels per unit
514518
s = ImageFile._safe_read(self.fp, length)
519+
if length < 9:
520+
if ImageFile.LOAD_TRUNCATED_IMAGES:
521+
return s
522+
raise ValueError("Truncated pHYs chunk")
515523
px, py = i32(s, 0), i32(s, 4)
516524
unit = s[8]
517525
if unit == 1: # meter
@@ -624,6 +632,10 @@ def chunk_eXIf(self, pos, length):
624632
# APNG chunks
625633
def chunk_acTL(self, pos, length):
626634
s = ImageFile._safe_read(self.fp, length)
635+
if length < 8:
636+
if ImageFile.LOAD_TRUNCATED_IMAGES:
637+
return s
638+
raise ValueError("APNG contains truncated acTL chunk")
627639
if self.im_n_frames is not None:
628640
self.im_n_frames = None
629641
warnings.warn("Invalid APNG, will use default PNG image if possible")
@@ -639,6 +651,10 @@ def chunk_acTL(self, pos, length):
639651

640652
def chunk_fcTL(self, pos, length):
641653
s = ImageFile._safe_read(self.fp, length)
654+
if length < 26:
655+
if ImageFile.LOAD_TRUNCATED_IMAGES:
656+
return s
657+
raise ValueError("APNG contains truncated fcTL chunk")
642658
seq = i32(s)
643659
if (self._seq_num is None and seq != 0) or (
644660
self._seq_num is not None and self._seq_num != seq - 1
@@ -660,6 +676,11 @@ def chunk_fcTL(self, pos, length):
660676
return s
661677

662678
def chunk_fdAT(self, pos, length):
679+
if length < 4:
680+
if ImageFile.LOAD_TRUNCATED_IMAGES:
681+
s = ImageFile._safe_read(self.fp, length)
682+
return s
683+
raise ValueError("APNG contains truncated fDAT chunk")
663684
s = ImageFile._safe_read(self.fp, 4)
664685
seq = i32(s)
665686
if self._seq_num != seq - 1:

0 commit comments

Comments
 (0)