[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#990690: marked as done (unblock: pillow/8.1.2+dfsg-0.2)



Your message dated Sun, 04 Jul 2021 21:57:10 +0000
with message-id <E1m0A6w-0007Ng-14@respighi.debian.org>
and subject line unblock pillow
has caused the Debian Bug report #990690,
regarding unblock: pillow/8.1.2+dfsg-0.2
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
990690: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=990690
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock
X-Debbugs-Cc: carnil@debian.org,jmm@debian.org

Hi Release team,

Please unblock package pillow

Moritz fixed several CVEs affecting pillow in unstable, and the fixes
should be present in bullseye before the relese to avoid an early need
oa possible DSA or having the CVEs just unfixed in bullseye.

Can you please unblock the package?

Regards,
Salvatore
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 <jmm@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-github@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-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
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-github@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-github@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-github@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

--- End Message ---
--- Begin Message ---
Unblocked.

--- End Message ---

Reply to: