From 7fe3dff241c11206616bf6229be898854ce0d066 Mon Sep 17 00:00:00 2001 From: Lumir Balhar Date: Mon, 14 Jun 2021 11:33:36 +0200 Subject: [PATCH] CVE-2021-28675 --- src/PIL/ImageFile.py | 12 ++++++++++-- src/PIL/PsdImagePlugin.py | 33 +++++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 1a3c4aa..2cef9ee 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -522,12 +522,18 @@ def _safe_read(fp, size): :param fp: File handle. Must implement a read method. :param size: Number of bytes to read. - :returns: A string containing up to size bytes of data. + :returns: A string containing size bytes of data. + + Raises an OSError if the file is truncated and the read can not be completed + """ if size <= 0: return b"" if size <= SAFEBLOCK: - return fp.read(size) + data = fp.read(size) + if len(data) < size: + raise OSError("Truncated File Read") + return data data = [] while size > 0: block = fp.read(min(size, SAFEBLOCK)) @@ -535,6 +541,8 @@ def _safe_read(fp, size): break data.append(block) size -= len(block) + if sum(len(d) for d in data) < size: + raise OSError("Truncated File Read") return b"".join(data) diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py index fe2a2ff..add9996 100644 --- a/src/PIL/PsdImagePlugin.py +++ b/src/PIL/PsdImagePlugin.py @@ -18,6 +18,8 @@ __version__ = "0.4" +import io + from . import Image, ImageFile, ImagePalette from ._binary import i8, i16be as i16, i32be as i32 @@ -114,7 +116,8 @@ class PsdImageFile(ImageFile.ImageFile): end = self.fp.tell() + size size = i32(read(4)) if size: - self.layers = _layerinfo(self.fp) + _layer_data = io.BytesIO(ImageFile._safe_read(self.fp, size)) + self.layers = _layerinfo(_layer_data, size) self.fp.seek(end) # @@ -164,11 +167,20 @@ class PsdImageFile(ImageFile.ImageFile): Image.Image.load(self) -def _layerinfo(file): +def _layerinfo(fp, ct_bytes): # read layerinfo block layers = [] - read = file.read - for i in range(abs(i16(read(2)))): + + def read(size): + return ImageFile._safe_read(fp, size) + + ct = i16(read(2)) + + # sanity check + if ct_bytes < (abs(ct) * 20): + raise SyntaxError("Layer block too short for number of layers requested") + + for i in range(abs(ct)): # bounding box y0 = i32(read(4)) @@ -179,7 +191,8 @@ def _layerinfo(file): # image info info = [] mode = [] - types = list(range(i16(read(2)))) + ct_types = i16(read(2)) + types = list(range(ct_types)) if len(types) > 4: continue @@ -212,7 +225,7 @@ def _layerinfo(file): size = i32(read(4)) # length of the extra data field combined = 0 if size: - data_end = file.tell() + size + data_end = fp.tell() + size length = i32(read(4)) if length: @@ -220,12 +233,12 @@ def _layerinfo(file): mask_x = i32(read(4)) mask_h = i32(read(4)) - mask_y mask_w = i32(read(4)) - mask_x - file.seek(length - 16, 1) + fp.seek(length - 16, 1) combined += length + 4 length = i32(read(4)) if length: - file.seek(length, 1) + fp.seek(length, 1) combined += length + 4 length = i8(read(1)) @@ -235,7 +248,7 @@ def _layerinfo(file): name = read(length).decode('latin-1', 'replace') combined += length + 1 - file.seek(data_end) + fp.seek(data_end) layers.append((name, mode, (x0, y0, x1, y1))) # get tiles @@ -243,7 +256,7 @@ def _layerinfo(file): for name, mode, bbox in layers: tile = [] for m in mode: - t = _maketile(file, m, bbox, 1) + t = _maketile(fp, m, bbox, 1) if t: tile.extend(t) layers[i] = name, mode, bbox, tile -- 2.31.1