Blob Blame History Raw
From 7fe3dff241c11206616bf6229be898854ce0d066 Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
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 <b>read</b> method.
     :param size: Number of bytes to read.
-    :returns: A string containing up to <i>size</i> bytes of data.
+    :returns: A string containing <i>size</i> 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