diff --git a/Tests/images/ossfuzz-4836216264589312.pcx b/Tests/images/ossfuzz-4836216264589312.pcx new file mode 100644 index 00000000000..fdde9716a0c Binary files /dev/null and b/Tests/images/ossfuzz-4836216264589312.pcx differ diff --git a/Tests/test_image.py b/Tests/test_image.py index 84c098cc8a4..f2a1917e856 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -775,26 +775,34 @@ def test_pillow_version(self, test_module): with pytest.warns(DeprecationWarning): assert test_module.PILLOW_VERSION > "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: + buffer_overrun = str(e) == "buffer overrun when reading image file" + truncated = "image file is truncated" in str(e) + + assert buffer_overrun or truncated + 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)