Skip to content

Commit 496245a

Browse files
wiredfoolhugovk
authored andcommitted
Fix BLP DOS -- CVE-2021-28678
* BlpImagePlugin did not properly check that reads after jumping to file offsets returned data. This could lead to a DOS where the decoder could be run a large number of times on empty data * This dates to Pillow 5.1.0
1 parent 22e9bee commit 496245a

9 files changed

+42
-20
lines changed
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Tests/test_file_blp.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,22 @@ def test_load_blp2_dxt1():
1717
def test_load_blp2_dxt1a():
1818
with Image.open("Tests/images/blp/blp2_dxt1a.blp") as im:
1919
assert_image_equal_tofile(im, "Tests/images/blp/blp2_dxt1a.png")
20+
21+
22+
@pytest.mark.parametrize(
23+
"test_file",
24+
[
25+
"Tests/images/timeout-060745d3f534ad6e4128c51d336ea5489182c69d.blp",
26+
"Tests/images/timeout-31c8f86233ea728339c6e586be7af661a09b5b98.blp",
27+
"Tests/images/timeout-60d8b7c8469d59fc9ffff6b3a3dc0faeae6ea8ee.blp",
28+
"Tests/images/timeout-8073b430977660cdd48d96f6406ddfd4114e69c7.blp",
29+
"Tests/images/timeout-bba4f2e026b5786529370e5dfe9a11b1bf991f07.blp",
30+
"Tests/images/timeout-d6ec061c4afdef39d3edf6da8927240bb07fe9b7.blp",
31+
"Tests/images/timeout-ef9112a065e7183fa7faa2e18929b03e44ee16bf.blp",
32+
],
33+
)
34+
def test_crashes(test_file):
35+
with open(test_file, "rb") as f:
36+
with Image.open(f) as im:
37+
with pytest.raises(OSError):
38+
im.load()

src/PIL/BlpImagePlugin.py

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -286,33 +286,36 @@ def decode(self, buffer):
286286
raise OSError("Truncated Blp file") from e
287287
return 0, 0
288288

289+
def _safe_read(self, length):
290+
return ImageFile._safe_read(self.fd, length)
291+
289292
def _read_palette(self):
290293
ret = []
291294
for i in range(256):
292295
try:
293-
b, g, r, a = struct.unpack("<4B", self.fd.read(4))
296+
b, g, r, a = struct.unpack("<4B", self._safe_read(4))
294297
except struct.error:
295298
break
296299
ret.append((b, g, r, a))
297300
return ret
298301

299302
def _read_blp_header(self):
300-
(self._blp_compression,) = struct.unpack("<i", self.fd.read(4))
303+
(self._blp_compression,) = struct.unpack("<i", self._safe_read(4))
301304

302-
(self._blp_encoding,) = struct.unpack("<b", self.fd.read(1))
303-
(self._blp_alpha_depth,) = struct.unpack("<b", self.fd.read(1))
304-
(self._blp_alpha_encoding,) = struct.unpack("<b", self.fd.read(1))
305-
(self._blp_mips,) = struct.unpack("<b", self.fd.read(1))
305+
(self._blp_encoding,) = struct.unpack("<b", self._safe_read(1))
306+
(self._blp_alpha_depth,) = struct.unpack("<b", self._safe_read(1))
307+
(self._blp_alpha_encoding,) = struct.unpack("<b", self._safe_read(1))
308+
(self._blp_mips,) = struct.unpack("<b", self._safe_read(1))
306309

307-
self.size = struct.unpack("<II", self.fd.read(8))
310+
self.size = struct.unpack("<II", self._safe_read(8))
308311

309312
if self.magic == b"BLP1":
310313
# Only present for BLP1
311-
(self._blp_encoding,) = struct.unpack("<i", self.fd.read(4))
312-
(self._blp_subtype,) = struct.unpack("<i", self.fd.read(4))
314+
(self._blp_encoding,) = struct.unpack("<i", self._safe_read(4))
315+
(self._blp_subtype,) = struct.unpack("<i", self._safe_read(4))
313316

314-
self._blp_offsets = struct.unpack("<16I", self.fd.read(16 * 4))
315-
self._blp_lengths = struct.unpack("<16I", self.fd.read(16 * 4))
317+
self._blp_offsets = struct.unpack("<16I", self._safe_read(16 * 4))
318+
self._blp_lengths = struct.unpack("<16I", self._safe_read(16 * 4))
316319

317320

318321
class BLP1Decoder(_BLPBaseDecoder):
@@ -324,7 +327,7 @@ def _load(self):
324327
if self._blp_encoding in (4, 5):
325328
data = bytearray()
326329
palette = self._read_palette()
327-
_data = BytesIO(self.fd.read(self._blp_lengths[0]))
330+
_data = BytesIO(self._safe_read(self._blp_lengths[0]))
328331
while True:
329332
try:
330333
(offset,) = struct.unpack("<B", _data.read(1))
@@ -346,10 +349,10 @@ def _load(self):
346349
def _decode_jpeg_stream(self):
347350
from PIL.JpegImagePlugin import JpegImageFile
348351

349-
(jpeg_header_size,) = struct.unpack("<I", self.fd.read(4))
350-
jpeg_header = self.fd.read(jpeg_header_size)
351-
self.fd.read(self._blp_offsets[0] - self.fd.tell()) # What IS this?
352-
data = self.fd.read(self._blp_lengths[0])
352+
(jpeg_header_size,) = struct.unpack("<I", self._safe_read(4))
353+
jpeg_header = self._safe_read(jpeg_header_size)
354+
self._safe_read(self._blp_offsets[0] - self.fd.tell()) # What IS this?
355+
data = self._safe_read(self._blp_lengths[0])
353356
data = jpeg_header + data
354357
data = BytesIO(data)
355358
image = JpegImageFile(data)
@@ -370,7 +373,7 @@ def _load(self):
370373
# Uncompressed or DirectX compression
371374

372375
if self._blp_encoding == BLP_ENCODING_UNCOMPRESSED:
373-
_data = BytesIO(self.fd.read(self._blp_lengths[0]))
376+
_data = BytesIO(self._safe_read(self._blp_lengths[0]))
374377
while True:
375378
try:
376379
(offset,) = struct.unpack("<B", _data.read(1))
@@ -384,20 +387,20 @@ def _load(self):
384387
linesize = (self.size[0] + 3) // 4 * 8
385388
for yb in range((self.size[1] + 3) // 4):
386389
for d in decode_dxt1(
387-
self.fd.read(linesize), alpha=bool(self._blp_alpha_depth)
390+
self._safe_read(linesize), alpha=bool(self._blp_alpha_depth)
388391
):
389392
data += d
390393

391394
elif self._blp_alpha_encoding == BLP_ALPHA_ENCODING_DXT3:
392395
linesize = (self.size[0] + 3) // 4 * 16
393396
for yb in range((self.size[1] + 3) // 4):
394-
for d in decode_dxt3(self.fd.read(linesize)):
397+
for d in decode_dxt3(self._safe_read(linesize)):
395398
data += d
396399

397400
elif self._blp_alpha_encoding == BLP_ALPHA_ENCODING_DXT5:
398401
linesize = (self.size[0] + 3) // 4 * 16
399402
for yb in range((self.size[1] + 3) // 4):
400-
for d in decode_dxt5(self.fd.read(linesize)):
403+
for d in decode_dxt5(self._safe_read(linesize)):
401404
data += d
402405
else:
403406
raise BLPFormatError(

0 commit comments

Comments
 (0)