Skip to content

Commit

Permalink
Merge pull request #6700 from hugovk/security-samples_per_pixel-sec
Browse files Browse the repository at this point in the history
  • Loading branch information
hugovk committed Oct 29, 2022
2 parents e055ef0 + 0846bfa commit 2444cdd
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 1 deletion.
Binary file not shown.
15 changes: 14 additions & 1 deletion Tests/test_file_tiff.py
Expand Up @@ -4,7 +4,7 @@

import pytest

from PIL import Image, ImageFile, TiffImagePlugin
from PIL import Image, ImageFile, TiffImagePlugin, UnidentifiedImageError
from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION

from .helper import (
Expand Down Expand Up @@ -858,6 +858,19 @@ def test_timeout(self):
im.load()
ImageFile.LOAD_TRUNCATED_IMAGES = False

@pytest.mark.parametrize(
"test_file",
[
"Tests/images/oom-225817ca0f8c663be7ab4b9e717b02c661e66834.tif",
],
)
@pytest.mark.timeout(2)
def test_oom(self, test_file):
with pytest.raises(UnidentifiedImageError):
with pytest.warns(UserWarning):
with Image.open(test_file):
pass


@pytest.mark.skipif(not is_win32(), reason="Windows only")
class TestFileTiffW32:
Expand Down
11 changes: 11 additions & 0 deletions docs/releasenotes/9.3.0.rst
Expand Up @@ -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
=============

Expand Down Expand Up @@ -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
10 changes: 10 additions & 0 deletions src/PIL/TiffImagePlugin.py
Expand Up @@ -257,6 +257,8 @@
(MM, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"),
}

MAX_SAMPLESPERPIXEL = max(len(key_tp[4]) for key_tp in OPEN_INFO.keys())

PREFIXES = [
b"MM\x00\x2A", # Valid TIFF header with big-endian byte order
b"II\x2A\x00", # Valid TIFF header with little-endian byte order
Expand Down Expand Up @@ -1396,6 +1398,14 @@ def _setup(self):
SAMPLESPERPIXEL,
3 if self._compression == "tiff_jpeg" and photo in (2, 6) else 1,
)

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
)
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.
Expand Down

0 comments on commit 2444cdd

Please sign in to comment.