From 2f409261eb1228e166868f8f0b5da5cda52e55bf Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Thu, 17 Dec 2020 00:17:53 +0100 Subject: [PATCH] Fix for CVE CVE-2020-35655 - Read Overflow in PCX Decoding. * Don't trust the image to specify a buffer size --- Tests/images/ossfuzz-4836216264589312.pcx | Bin 0 -> 129 bytes Tests/test_image.py | 27 ++++++++++++---------- src/PIL/PcxImagePlugin.py | 9 ++++++-- 3 files changed, 22 insertions(+), 14 deletions(-) create mode 100644 Tests/images/ossfuzz-4836216264589312.pcx diff --git a/Tests/images/ossfuzz-4836216264589312.pcx b/Tests/images/ossfuzz-4836216264589312.pcx new file mode 100644 index 0000000000000000000000000000000000000000..fdde9716a0cbd84900f9499c2f8c6d13067d456a GIT binary patch literal 129 wcmd;LP+(+WP*4Ej|6rg11t1QLjY`90z%&y`Cj@}_2tCMbm "7.0.0" - def test_overrun(self): - """For overrun completeness, test as: - valgrind pytest -qq Tests/test_image.py::TestImage::test_overrun | grep decode.c - """ - for file in [ + @pytest.mark.parametrize("path", [ "fli_overrun.bin", "sgi_overrun.bin", "sgi_overrun_expandrow.bin", "sgi_overrun_expandrow2.bin", "pcx_overrun.bin", "pcx_overrun2.bin", + "ossfuzz-4836216264589312.pcx", "01r_00.pcx", - ]: - with Image.open(os.path.join("Tests/images", file)) as im: - try: - im.load() - assert False - except OSError as e: - assert str(e) == "buffer overrun when reading image file" + ]) + def test_overrun(self, path): + """For overrun completeness, test as: + valgrind pytest -qq Tests/test_image.py::TestImage::test_overrun | grep decode.c + """ + with Image.open(os.path.join("Tests/images", path)) as im: + try: + im.load() + assert False + except OSError as e: + assert (str(e) == "buffer overrun when reading image file" or + "image file is truncated" in str(e)) + def test_fli_overrun2(self): with Image.open("Tests/images/fli_overrun2.bin") as im: try: im.seek(1) diff --git a/src/PIL/PcxImagePlugin.py b/src/PIL/PcxImagePlugin.py index b337b7dde20..a24d44b4278 100644 --- a/src/PIL/PcxImagePlugin.py +++ b/src/PIL/PcxImagePlugin.py @@ -66,13 +66,13 @@ def _open(self): version = s[1] bits = s[3] planes = s[65] - stride = i16(s, 66) + ignored_stride = i16(s, 66) logger.debug( "PCX version %s, bits %s, planes %s, stride %s", version, bits, planes, - stride, + ignored_stride, ) self.info["dpi"] = i16(s, 12), i16(s, 14) @@ -110,6 +110,11 @@ def _open(self): self.mode = mode self._size = bbox[2] - bbox[0], bbox[3] - bbox[1] + # don't trust the passed in stride. Calculate for ourselves. + # CVE-2020-35655 + stride = (self._size[0] * bits + 7) // 8 + stride += stride % 2 + bbox = (0, 0) + self.size logger.debug("size: %sx%s", *self.size)