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