From 9ae8f6b7aa8ea4638cb675267cd20c5425dcfafc Mon Sep 17 00:00:00 2001
From: qz_cx <wangqingzheng@kylinos.cn>
Date: Thu, 17 Nov 2022 10:28:59 +0800
Subject: [PATCH] Merge pull request #6700 from
 hugovk/security-samples_per_pixel-sec

hugovk committed
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.
---
 Tests/test_file_tiff.py    | 14 +++++++++++++-
 src/PIL/TiffImagePlugin.py | 10 ++++++++++
 2 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py
index 5801e17..57fabfa 100644
--- a/Tests/test_file_tiff.py
+++ b/Tests/test_file_tiff.py
@@ -3,7 +3,7 @@ from io import BytesIO
 
 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 (
@@ -734,6 +734,18 @@ class TestFileTiff:
             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:
diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py
index 5df5c4f..f2afe63 100644
--- a/src/PIL/TiffImagePlugin.py
+++ b/src/PIL/TiffImagePlugin.py
@@ -252,6 +252,8 @@ OPEN_INFO = {
     (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
@@ -1310,6 +1312,14 @@ class TiffImageFile(ImageFile.ImageFile):
             SAMPLESPERPIXEL,
             3 if self._compression == "tiff_jpeg" and photo in (2, 6) else 1,
         )
+
+        if samplesPerPixel > MAX_SAMPLESPERPIXEL:
+            # DOS check, samplesPerPixel can be a Long, and we extend the tuple below
+            logger.error(
+                "More samples per pixel than can be decoded: %s", samplesPerPixel
+            )
+            raise SyntaxError("Invalid value for samples per pixel")
+
         if len(bps_tuple) != samplesPerPixel:
             raise SyntaxError("unknown data organization")
 
-- 
2.33.0

