debdiff for my NMU.
diff -Nru pillow-8.1.2+dfsg/debian/changelog pillow-8.1.2+dfsg/debian/changelog
--- pillow-8.1.2+dfsg/debian/changelog	2021-04-24 15:51:24.000000000 +0200
+++ pillow-8.1.2+dfsg/debian/changelog	2021-06-13 18:11:04.000000000 +0200
@@ -1,3 +1,12 @@
+pillow (8.1.2+dfsg-0.2) unstable; urgency=medium
+
+  * Non-maintainer upload.
+  * Cherrypick security fixes from 8.2:
+    - CVE-2021-25287 / CVE-2021-25288 / CVE-2021-28675 / CVE-2021-28676
+      CVE-2021-28677 / CVE-2021-28678 (Closes: #989062)
+
+ -- Moritz Muehlenhoff <j...@debian.org>  Sun, 13 Jun 2021 18:11:04 +0200
+
 pillow (8.1.2+dfsg-0.1) unstable; urgency=medium
 
   * Non-maintainer upload.
diff -Nru pillow-8.1.2+dfsg/debian/patches/CVE-2021-25287_CVE-2021-25288.patch pillow-8.1.2+dfsg/debian/patches/CVE-2021-25287_CVE-2021-25288.patch
--- pillow-8.1.2+dfsg/debian/patches/CVE-2021-25287_CVE-2021-25288.patch	1970-01-01 01:00:00.000000000 +0100
+++ pillow-8.1.2+dfsg/debian/patches/CVE-2021-25287_CVE-2021-25288.patch	2021-06-13 18:08:32.000000000 +0200
@@ -0,0 +1,69 @@
+From 3bf5eddb89afdf690eceaa52bc4d3546ba9a5f87 Mon Sep 17 00:00:00 2001
+From: Eric Soroos <eric-git...@soroos.net>
+Date: Sun, 7 Mar 2021 12:32:12 +0100
+Subject: [PATCH] Fix OOB Read in Jpeg2KDecode CVE-2021-25287,CVE-2021-25288
+
+* For J2k images with multiple bands, it's legal in to have different
+  widths for each band, e.g. 1 byte for L, 4 bytes for A
+* This dates to Pillow 2.4.0
+
+--- pillow-8.1.2+dfsg.orig/src/libImaging/Jpeg2KDecode.c
++++ pillow-8.1.2+dfsg/src/libImaging/Jpeg2KDecode.c
+@@ -589,7 +589,7 @@ j2k_decode_entry(Imaging im, ImagingCode
+     j2k_unpacker_t unpack = NULL;
+     size_t buffer_size = 0, tile_bytes = 0;
+     unsigned n, tile_height, tile_width;
+-    int components;
++    int total_component_width = 0;
+ 
+ 
+     stream = opj_stream_create(BUFFER_SIZE, OPJ_TRUE);
+@@ -753,23 +753,40 @@ j2k_decode_entry(Imaging im, ImagingCode
+             goto quick_exit;
+         }
+ 
++        if (tile_info.nb_comps != image->numcomps) {
++            state->errcode = IMAGING_CODEC_BROKEN;
++            state->state = J2K_STATE_FAILED;
++            goto quick_exit;
++        }
++	
+         /* Sometimes the tile_info.datasize we get back from openjpeg
+-           is less than numcomps*w*h, and we overflow in the
++           is less than sum(comp_bytes)*w*h, and we overflow in the
+            shuffle stage */
+ 
+         tile_width = tile_info.x1 - tile_info.x0;
+         tile_height = tile_info.y1 - tile_info.y0;
+-        components = tile_info.nb_comps == 3 ? 4 : tile_info.nb_comps;
+-        if (( tile_width > UINT_MAX / components ) ||
+-            ( tile_height > UINT_MAX / components ) ||
+-            ( tile_width > UINT_MAX / (tile_height * components )) ||
+-            ( tile_height > UINT_MAX / (tile_width * components ))) {
++
++        /* Total component width = sum (component_width) e.g, it's
++         legal for an la file to have a 1 byte width for l, and 4 for
++         a. and then a malicious file could have a smaller tile_bytes
++        */
++
++        for (n=0; n < tile_info.nb_comps; n++) {
++            // see csize /acsize calcs
++            int csize = (image->comps[n].prec + 7) >> 3;
++            csize = (csize == 3) ? 4 : csize;
++            total_component_width += csize;
++        }
++        if ((tile_width > UINT_MAX / total_component_width) ||
++            (tile_height > UINT_MAX / total_component_width) ||
++            (tile_width > UINT_MAX / (tile_height * total_component_width)) ||
++            (tile_height > UINT_MAX / (tile_width * total_component_width))) {
+             state->errcode = IMAGING_CODEC_BROKEN;
+             state->state = J2K_STATE_FAILED;
+             goto quick_exit;
+         }
+-
+-        tile_bytes = tile_width * tile_height * components;
++	
++        tile_bytes = tile_width * tile_height * total_component_width;
+ 
+         if (tile_bytes > tile_info.data_size) {
+             tile_info.data_size = tile_bytes;
diff -Nru pillow-8.1.2+dfsg/debian/patches/CVE-2021-28675.patch pillow-8.1.2+dfsg/debian/patches/CVE-2021-28675.patch
--- pillow-8.1.2+dfsg/debian/patches/CVE-2021-28675.patch	1970-01-01 01:00:00.000000000 +0100
+++ pillow-8.1.2+dfsg/debian/patches/CVE-2021-28675.patch	2021-06-13 18:09:24.000000000 +0200
@@ -0,0 +1,132 @@
+From 22e9bee4ef225c0edbb9323f94c26cee0c623497 Mon Sep 17 00:00:00 2001
+From: Eric Soroos <eric-git...@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
diff -Nru pillow-8.1.2+dfsg/debian/patches/CVE-2021-28676.patch pillow-8.1.2+dfsg/debian/patches/CVE-2021-28676.patch
--- pillow-8.1.2+dfsg/debian/patches/CVE-2021-28676.patch	1970-01-01 01:00:00.000000000 +0100
+++ pillow-8.1.2+dfsg/debian/patches/CVE-2021-28676.patch	2021-06-13 18:10:02.000000000 +0200
@@ -0,0 +1,24 @@
+From bb6c11fb889e6c11b0ee122b828132ee763b5856 Mon Sep 17 00:00:00 2001
+From: Eric Soroos <eric-git...@soroos.net>
+Date: Thu, 11 Mar 2021 22:12:35 +0100
+Subject: [PATCH] Fix FLI DOS -- CVE-2021-28676
+
+* FliDecode did not properly check that the block advance was
+  non-zero, potentally leading to an infinite loop on load.
+* This dates to the PIL Fork
+* Found with oss-fuzz
+
+--- pillow-8.1.2+dfsg.orig/src/libImaging/FliDecode.c
++++ pillow-8.1.2+dfsg/src/libImaging/FliDecode.c
+@@ -242,6 +242,11 @@ ImagingFliDecode(Imaging im, ImagingCode
+                 return -1;
+         }
+         advance = I32(ptr);
++        if (advance == 0 ) {
++            // If there's no advance, we're in in infinite loop
++            state->errcode = IMAGING_CODEC_BROKEN;
++            return -1;
++        }
+         if (advance < 0 || advance > bytes) {
+             state->errcode = IMAGING_CODEC_OVERRUN;
+             return -1;
diff -Nru pillow-8.1.2+dfsg/debian/patches/CVE-2021-28677.patch pillow-8.1.2+dfsg/debian/patches/CVE-2021-28677.patch
--- pillow-8.1.2+dfsg/debian/patches/CVE-2021-28677.patch	1970-01-01 01:00:00.000000000 +0100
+++ pillow-8.1.2+dfsg/debian/patches/CVE-2021-28677.patch	2021-06-13 17:54:06.000000000 +0200
@@ -0,0 +1,41 @@
+From 5a5e6db0abf4e7a638fb1b3408c4e495a096cb92 Mon Sep 17 00:00:00 2001
+From: Eric Soroos <eric-git...@soroos.net>
+Date: Mon, 8 Mar 2021 20:31:41 +0100
+Subject: [PATCH] Fix EPS DOS on _open -- CVE-2021-28677
+
+* The readline used in EPS has to deal with any combination of \r and
+  \n as line endings. It used an accidentally quadratic method of
+  accumulating lines while looking for a line ending.
+* A malicious EPS file could use this to perform a DOS of Pillow in
+  the open phase, before an image was accepted for opening.
+* This dates to the PIL Fork
+
+diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py
+index dc61f48edc9..3bf8ee0ab35 100644
+--- a/src/PIL/EpsImagePlugin.py
++++ b/src/PIL/EpsImagePlugin.py
+@@ -170,12 +170,12 @@ def seek(self, offset, whence=io.SEEK_SET):
+         self.fp.seek(offset, whence)
+ 
+     def readline(self):
+-        s = self.char or b""
++        s = [self.char or b""]
+         self.char = None
+ 
+         c = self.fp.read(1)
+-        while c not in b"\r\n":
+-            s = s + c
++        while (c not in b"\r\n") and len(c):
++            s.append(c)
+             c = self.fp.read(1)
+ 
+         self.char = self.fp.read(1)
+@@ -183,7 +183,7 @@ def readline(self):
+         if self.char in b"\r\n":
+             self.char = None
+ 
+-        return s.decode("latin-1")
++        return b"".join(s).decode("latin-1")
+ 
+ 
+ def _accept(prefix):
diff -Nru pillow-8.1.2+dfsg/debian/patches/CVE-2021-28678.patch pillow-8.1.2+dfsg/debian/patches/CVE-2021-28678.patch
--- pillow-8.1.2+dfsg/debian/patches/CVE-2021-28678.patch	1970-01-01 01:00:00.000000000 +0100
+++ pillow-8.1.2+dfsg/debian/patches/CVE-2021-28678.patch	2021-06-13 17:54:06.000000000 +0200
@@ -0,0 +1,119 @@
+From 496245aa4365d0827390bd0b6fbd11287453b3a1 Mon Sep 17 00:00:00 2001
+From: Eric Soroos <eric-git...@soroos.net>
+Date: Sun, 7 Mar 2021 19:00:17 +0100
+Subject: [PATCH] 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
+
+diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py
+index 88aae80eb96..e07474621d9 100644
+--- a/src/PIL/BlpImagePlugin.py
++++ b/src/PIL/BlpImagePlugin.py
+@@ -286,33 +286,36 @@ def decode(self, buffer):
+             raise OSError("Truncated Blp file") from e
+         return 0, 0
+ 
++    def _safe_read(self, length):
++        return ImageFile._safe_read(self.fd, length)
++
+     def _read_palette(self):
+         ret = []
+         for i in range(256):
+             try:
+-                b, g, r, a = struct.unpack("<4B", self.fd.read(4))
++                b, g, r, a = struct.unpack("<4B", self._safe_read(4))
+             except struct.error:
+                 break
+             ret.append((b, g, r, a))
+         return ret
+ 
+     def _read_blp_header(self):
+-        (self._blp_compression,) = struct.unpack("<i", self.fd.read(4))
++        (self._blp_compression,) = struct.unpack("<i", self._safe_read(4))
+ 
+-        (self._blp_encoding,) = struct.unpack("<b", self.fd.read(1))
+-        (self._blp_alpha_depth,) = struct.unpack("<b", self.fd.read(1))
+-        (self._blp_alpha_encoding,) = struct.unpack("<b", self.fd.read(1))
+-        (self._blp_mips,) = struct.unpack("<b", self.fd.read(1))
++        (self._blp_encoding,) = struct.unpack("<b", self._safe_read(1))
++        (self._blp_alpha_depth,) = struct.unpack("<b", self._safe_read(1))
++        (self._blp_alpha_encoding,) = struct.unpack("<b", self._safe_read(1))
++        (self._blp_mips,) = struct.unpack("<b", self._safe_read(1))
+ 
+-        self.size = struct.unpack("<II", self.fd.read(8))
++        self.size = struct.unpack("<II", self._safe_read(8))
+ 
+         if self.magic == b"BLP1":
+             # Only present for BLP1
+-            (self._blp_encoding,) = struct.unpack("<i", self.fd.read(4))
+-            (self._blp_subtype,) = struct.unpack("<i", self.fd.read(4))
++            (self._blp_encoding,) = struct.unpack("<i", self._safe_read(4))
++            (self._blp_subtype,) = struct.unpack("<i", self._safe_read(4))
+ 
+-        self._blp_offsets = struct.unpack("<16I", self.fd.read(16 * 4))
+-        self._blp_lengths = struct.unpack("<16I", self.fd.read(16 * 4))
++        self._blp_offsets = struct.unpack("<16I", self._safe_read(16 * 4))
++        self._blp_lengths = struct.unpack("<16I", self._safe_read(16 * 4))
+ 
+ 
+ class BLP1Decoder(_BLPBaseDecoder):
+@@ -324,7 +327,7 @@ def _load(self):
+             if self._blp_encoding in (4, 5):
+                 data = bytearray()
+                 palette = self._read_palette()
+-                _data = BytesIO(self.fd.read(self._blp_lengths[0]))
++                _data = BytesIO(self._safe_read(self._blp_lengths[0]))
+                 while True:
+                     try:
+                         (offset,) = struct.unpack("<B", _data.read(1))
+@@ -346,10 +349,10 @@ def _load(self):
+     def _decode_jpeg_stream(self):
+         from PIL.JpegImagePlugin import JpegImageFile
+ 
+-        (jpeg_header_size,) = struct.unpack("<I", self.fd.read(4))
+-        jpeg_header = self.fd.read(jpeg_header_size)
+-        self.fd.read(self._blp_offsets[0] - self.fd.tell())  # What IS this?
+-        data = self.fd.read(self._blp_lengths[0])
++        (jpeg_header_size,) = struct.unpack("<I", self._safe_read(4))
++        jpeg_header = self._safe_read(jpeg_header_size)
++        self._safe_read(self._blp_offsets[0] - self.fd.tell())  # What IS this?
++        data = self._safe_read(self._blp_lengths[0])
+         data = jpeg_header + data
+         data = BytesIO(data)
+         image = JpegImageFile(data)
+@@ -370,7 +373,7 @@ def _load(self):
+             # Uncompressed or DirectX compression
+ 
+             if self._blp_encoding == BLP_ENCODING_UNCOMPRESSED:
+-                _data = BytesIO(self.fd.read(self._blp_lengths[0]))
++                _data = BytesIO(self._safe_read(self._blp_lengths[0]))
+                 while True:
+                     try:
+                         (offset,) = struct.unpack("<B", _data.read(1))
+@@ -384,20 +387,20 @@ def _load(self):
+                     linesize = (self.size[0] + 3) // 4 * 8
+                     for yb in range((self.size[1] + 3) // 4):
+                         for d in decode_dxt1(
+-                            self.fd.read(linesize), alpha=bool(self._blp_alpha_depth)
++                            self._safe_read(linesize), alpha=bool(self._blp_alpha_depth)
+                         ):
+                             data += d
+ 
+                 elif self._blp_alpha_encoding == BLP_ALPHA_ENCODING_DXT3:
+                     linesize = (self.size[0] + 3) // 4 * 16
+                     for yb in range((self.size[1] + 3) // 4):
+-                        for d in decode_dxt3(self.fd.read(linesize)):
++                        for d in decode_dxt3(self._safe_read(linesize)):
+                             data += d
+ 
+                 elif self._blp_alpha_encoding == BLP_ALPHA_ENCODING_DXT5:
+                     linesize = (self.size[0] + 3) // 4 * 16
+                     for yb in range((self.size[1] + 3) // 4):
+-                        for d in decode_dxt5(self.fd.read(linesize)):
++                        for d in decode_dxt5(self._safe_read(linesize)):
+                             data += d
+                 else:
+                     raise BLPFormatError(
diff -Nru pillow-8.1.2+dfsg/debian/patches/series pillow-8.1.2+dfsg/debian/patches/series
--- pillow-8.1.2+dfsg/debian/patches/series	2020-10-19 19:55:33.000000000 +0200
+++ pillow-8.1.2+dfsg/debian/patches/series	2021-06-13 18:10:51.000000000 +0200
@@ -2,3 +2,8 @@
 generate-webp-file
 js-script-file.diff
 no-sphinx-removed-in.diff
+CVE-2021-25287_CVE-2021-25288.patch
+CVE-2021-28675.patch
+CVE-2021-28676.patch
+CVE-2021-28677.patch
+CVE-2021-28678.patch

Reply via email to