From 13f2c5ae14901c89c38f898496102afd9daeaf6d Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Fri, 28 Oct 2022 14:11:25 +0200 Subject: [PATCH 1/5] Prevent DOS with large SAMPLESPERPIXEL in Tiff IFD A large value in the SAMPLESPERPIXEL tag could lead to a memory and runtime DOS in TiffImagePlugin.py when setting up the context for image decoding. --- ...-225817ca0f8c663be7ab4b9e717b02c661e66834.tif | Bin 0 -> 88 bytes Tests/test_file_tiff.py | 15 ++++++++++++++- src/PIL/TiffImagePlugin.py | 8 ++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 Tests/images/oom-225817ca0f8c663be7ab4b9e717b02c661e66834.tif diff --git a/Tests/images/oom-225817ca0f8c663be7ab4b9e717b02c661e66834.tif b/Tests/images/oom-225817ca0f8c663be7ab4b9e717b02c661e66834.tif new file mode 100644 index 0000000000000000000000000000000000000000..01dca594f53e22fda9b11ed5b704326680af1b8c GIT binary patch literal 88 zcmebD)MDUZU|`^8U|?isU<9(bfS3`=2FWl%*#bZ|Gn5Td$A-ifWn=;Cox~s{WCDf& DV5 MAX_SAMPLESPERPIXEL: + # DOS check, samples_per_pixel can be a Long, and we extend the tuple below + logger.error("More samples per pixel than can be decoded: %s", samples_per_pixel) + raise SyntaxError("Invalid value for samples per pixel") + if samples_per_pixel < bps_actual_count: # If a file has more values in bps_tuple than expected, # remove the excess. From 05b175ef88c22f5c416bc9b8d5b897dea1abbf2c Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Fri, 28 Oct 2022 14:46:20 +0200 Subject: [PATCH 2/5] Tighter test case --- Tests/test_file_tiff.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 47c4e1b1357..752c1e61ed1 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -868,7 +868,7 @@ def test_timeout(self): def test_oom(self, test_file): with pytest.raises(UnidentifiedImageError): with Image.open(test_file) as im: - im.load() + pass From 00b25fd3ac3648bc28eff5d4c4d816e605e3f05f Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 28 Oct 2022 18:02:24 +0300 Subject: [PATCH 3/5] Hide UserWarning in logs Tests/test_file_tiff.py::TestFileTiff::test_oom[Tests/images/oom-225817ca0f8c663be7ab4b9e717b02c661e66834.tif] PIL/TiffImagePlugin.py:850: UserWarning: Corrupt EXIF data. Expecting to read 12 bytes but only got 6. warnings.warn(str(msg)) Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- Tests/test_file_tiff.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 752c1e61ed1..5953dfa1814 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -867,8 +867,9 @@ def test_timeout(self): @pytest.mark.timeout(2) def test_oom(self, test_file): with pytest.raises(UnidentifiedImageError): - with Image.open(test_file) as im: - pass + with pytest.warns(UserWarning): + with Image.open(test_file) as im: + pass From 799a6a01052cea3f417a571d7c64cd14acc18c64 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 28 Oct 2022 18:03:38 +0300 Subject: [PATCH 4/5] Fix linting --- Tests/test_file_tiff.py | 3 +-- src/PIL/TiffImagePlugin.py | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 5953dfa1814..4f3c8e39010 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -868,11 +868,10 @@ def test_timeout(self): def test_oom(self, test_file): with pytest.raises(UnidentifiedImageError): with pytest.warns(UserWarning): - with Image.open(test_file) as im: + with Image.open(test_file): pass - @pytest.mark.skipif(not is_win32(), reason="Windows only") class TestFileTiffW32: def test_fd_leak(self, tmp_path): diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 46166fc6335..1dfd5275fa1 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1401,7 +1401,9 @@ def _setup(self): if samples_per_pixel > MAX_SAMPLESPERPIXEL: # DOS check, samples_per_pixel can be a Long, and we extend the tuple below - logger.error("More samples per pixel than can be decoded: %s", samples_per_pixel) + logger.error( + "More samples per pixel than can be decoded: %s", samples_per_pixel + ) raise SyntaxError("Invalid value for samples per pixel") if samples_per_pixel < bps_actual_count: From 0846bfae48513f2f51ca8547ed3b8954fa501fda Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 28 Oct 2022 18:03:50 +0300 Subject: [PATCH 5/5] Add to release notes --- docs/releasenotes/9.3.0.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/releasenotes/9.3.0.rst b/docs/releasenotes/9.3.0.rst index e5a68ed9e98..410666fc0d8 100644 --- a/docs/releasenotes/9.3.0.rst +++ b/docs/releasenotes/9.3.0.rst @@ -49,6 +49,15 @@ decode the data in its natural CMYK mode, then convert it to RGB and rearrange the channels afterwards. Trying to load the data in an incorrect mode could result in a segmentation fault. This issue was introduced in Pillow 9.1.0. +Limit SAMPLESPERPIXEL to avoid runtime DOS +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A large value in the ``SAMPLESPERPIXEL`` tag could lead to a memory and runtime DOS in +``TiffImagePlugin.py`` when setting up the context for image decoding. +This was introduced in Pillow 9.2.0, found with `OSS-Fuzz`_ and fixed by limiting +``SAMPLESPERPIXEL`` to the number of planes that we can decode. + + Other Changes ============= @@ -88,3 +97,5 @@ Show all frames with ImageShow When calling :py:meth:`~PIL.Image.Image.show` or using :py:mod:`~PIL.ImageShow`, all frames will now be shown. + +.. _OSS-Fuzz: https://github.com/google/oss-fuzz