Package: pillow / 8.1.2+dfsg-0.3

CVE-2021-28675.patch Patch series | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
From 22e9bee4ef225c0edbb9323f94c26cee0c623497 Mon Sep 17 00:00:00 2001
From: Eric Soroos <eric-github@soroos.net>
Date: Sun, 7 Mar 2021 19:04:25 +0100
Subject: [PATCH] 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

--- pillow-8.1.2+dfsg.orig/src/PIL/ImageFile.py
+++ pillow-8.1.2+dfsg/src/PIL/ImageFile.py
@@ -555,12 +555,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))
@@ -568,9 +574,13 @@ 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)
 
 
+
+
 class PyCodecState:
     def __init__(self):
         self.xsize = 0
--- pillow-8.1.2+dfsg.orig/src/PIL/PsdImagePlugin.py
+++ pillow-8.1.2+dfsg/src/PIL/PsdImagePlugin.py
@@ -119,7 +119,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)
         self.n_frames = len(self.layers)
         self.is_animated = self.n_frames > 1
@@ -170,12 +171,20 @@ class PsdImageFile(ImageFile.ImageFile):
         finally:
             self.__fp = None
 
-
-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))
@@ -186,7 +195,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
 
@@ -219,16 +229,16 @@ 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:
-                file.seek(length - 16, io.SEEK_CUR)
+                fp.seek(length - 16, io.SEEK_CUR)
             combined += length + 4
 
             length = i32(read(4))
             if length:
-                file.seek(length, io.SEEK_CUR)
+                fp.seek(length, io.SEEK_CUR)
             combined += length + 4
 
             length = i8(read(1))
@@ -238,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
@@ -246,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