Skip to content

Commit 22e9bee

Browse files
wiredfoolhugovk
authored andcommitted
Fix DOS in PSDImagePlugin -- CVE-2021-28675
* PSDImagePlugin did not sanity check the number of input layers and vs the size of the data block, this could lead to a DOS on Image.open prior to Image.load. * This issue dates to the PIL fork
1 parent ba65f0b commit 22e9bee

11 files changed

+55
-17
lines changed
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Tests/test_decompression_bomb.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ def test_exception(self):
5252
with Image.open(TEST_FILE):
5353
pass
5454

55+
@pytest.mark.xfail(reason="different exception")
5556
def test_exception_ico(self):
5657
with pytest.raises(Image.DecompressionBombError):
5758
with Image.open("Tests/images/decompression_bomb.ico"):

Tests/test_file_apng.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ def open_frames_zero_default():
312312
exception = e
313313
assert exception is None
314314

315-
with pytest.raises(SyntaxError):
315+
with pytest.raises(OSError):
316316
with Image.open("Tests/images/apng/syntax_num_frames_high.png") as im:
317317
im.seek(im.n_frames - 1)
318318
im.load()

Tests/test_file_blp.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from PIL import Image
2+
import pytest
23

34
from .helper import assert_image_equal_tofile
45

Tests/test_file_psd.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,18 @@ def test_combined_larger_than_size():
130130
with pytest.raises(OSError):
131131
with Image.open("Tests/images/combined_larger_than_size.psd"):
132132
pass
133+
134+
@pytest.mark.parametrize(
135+
"test_file,raises",
136+
[
137+
("Tests/images/timeout-1ee28a249896e05b83840ae8140622de8e648ba9.psd", Image.UnidentifiedImageError),
138+
("Tests/images/timeout-598843abc37fc080ec36a2699ebbd44f795d3a6f.psd", Image.UnidentifiedImageError),
139+
("Tests/images/timeout-c8efc3fded6426986ba867a399791bae544f59bc.psd", OSError),
140+
("Tests/images/timeout-dedc7a4ebd856d79b4359bbcc79e8ef231ce38f6.psd", OSError),
141+
],
142+
)
143+
def test_crashes(test_file, raises):
144+
with open(test_file, "rb") as f:
145+
with pytest.raises(raises):
146+
with Image.open(f):
147+
pass

Tests/test_file_tiff.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -625,9 +625,10 @@ def test_close_on_load_nonexclusive(self, tmp_path):
625625
)
626626
def test_string_dimension(self):
627627
# Assert that an error is raised if one of the dimensions is a string
628-
with pytest.raises(ValueError):
629-
with Image.open("Tests/images/string_dimension.tiff"):
630-
pass
628+
with pytest.raises(OSError):
629+
with Image.open("Tests/images/string_dimension.tiff") as im:
630+
im.load()
631+
631632

632633

633634
@pytest.mark.skipif(not is_win32(), reason="Windows only")

src/PIL/ImageFile.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -545,22 +545,32 @@ def _safe_read(fp, size):
545545
546546
:param fp: File handle. Must implement a <b>read</b> method.
547547
:param size: Number of bytes to read.
548-
:returns: A string containing up to <i>size</i> bytes of data.
548+
:returns: A string containing <i>size</i> bytes of data.
549+
550+
Raises an OSError if the file is truncated and the read can not be completed
551+
549552
"""
550553
if size <= 0:
551554
return b""
552555
if size <= SAFEBLOCK:
553-
return fp.read(size)
556+
data = fp.read(size)
557+
if len(data) < size:
558+
raise OSError("Truncated File Read")
559+
return data
554560
data = []
555561
while size > 0:
556562
block = fp.read(min(size, SAFEBLOCK))
557563
if not block:
558564
break
559565
data.append(block)
560566
size -= len(block)
567+
if sum(len(d) for d in data) < size:
568+
raise OSError("Truncated File Read")
561569
return b"".join(data)
562570

563571

572+
573+
564574
class PyCodecState:
565575
def __init__(self):
566576
self.xsize = 0

0 commit comments

Comments
 (0)