CVE-2023-44271 (published 2023-11-03T05:15Z)

An issue was discovered in Pillow before 10.0.0. It is a Denial of Service that uncontrollably allocates memory to process a given task, potentially causing a service to crash by having it run out of memory. This occurs for truetype in ImageFont when textlength in an ImageDraw instance operates on a long text argument.

CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: CWE-770
cwe_name: Allocation of Resources Without Limits or Throttling
description: The product allocates a reusable resource or group of resources on behalf of an actor without imposing any restrictions on the size or number of resources that can be allocated, in violation of the intended security policy for that actor.
extended_description: Code frequently has to work with limited resources, so programmers must be careful to ensure that resources are not consumed too quickly, or too easily.  Without use of quotas, resource limits, or other protection mechanisms, it can be easy for an attacker to consume many resources by rapidly making many requests, or causing larger resources to be used than is needed. When too many resources are allocated, or if a single resource is too large, then it can prevent the code from working correctly, possibly leading to a denial of service.
         

Fix information:
repo_url: https://github.com/python-pillow/Pillow
commit_details: repo_url: https://github.com/python-pillow/Pillow
author: Andrew Murray
author_date: 2023-06-30 23:32:26+10:00
msg: Added ImageFont.MAX_STRING_LENGTH
repository: repo_url: https://github.com/python-pillow/Pillow
repo_name: python-pillow/Pillow
description: Python Imaging Library (Fork)
repo_language: Python
owner: python-pillow
file_changes: filename: test_imagefont.py
change_type: ModificationType.MODIFY
diff: @@ -1038,6 +1038,25 @@ def test_render_mono_size():
     assert_image_equal_tofile(im, "Tests/images/text_mono.gif")
 
 
+def test_too_many_characters(font):
+    with pytest.raises(ValueError):
+        font.getlength("A" * 1000001)
+    with pytest.raises(ValueError):
+        font.getbbox("A" * 1000001)
+    with pytest.raises(ValueError):
+        font.getmask2("A" * 1000001)
+
+    transposed_font = ImageFont.TransposedFont(font)
+    with pytest.raises(ValueError):
+        transposed_font.getlength("A" * 1000001)
+
+    default_font = ImageFont.load_default()
+    with pytest.raises(ValueError):
+        default_font.getlength("A" * 1000001)
+    with pytest.raises(ValueError):
+        default_font.getbbox("A" * 1000001)
+
+
 @pytest.mark.parametrize(
     "test_file",
     [

programming_language: Python
filename: ImageFont.rst
change_type: ModificationType.MODIFY
diff: @@ -18,6 +18,15 @@ OpenType fonts (as well as other font formats supported by the FreeType
 library). For earlier versions, TrueType support is only available as part of
 the imToolkit package.
 
+.. warning::
+    To protect against potential DOS attacks when using arbitrary strings as
+    text input, Pillow will raise a ``ValueError`` if the number of characters
+    is over a certain limit, :py:data:`MAX_STRING_LENGTH`.
+
+    This threshold can be changed by setting
+    :py:data:`MAX_STRING_LENGTH`. It can be disabled by setting
+    ``ImageFont.MAX_STRING_LENGTH = None``.
+
 Example
 -------
 
@@ -73,3 +82,12 @@ Constants
 
     Requires Raqm, you can check support using
     :py:func:`PIL.features.check_feature` with ``feature="raqm"``.
+
+Constants
+---------
+
+.. data:: MAX_STRING_LENGTH
+
+    Set to 1,000,000, to protect against potential DOS attacks. Pillow will
+    raise a ``ValueError`` if the number of characters is over this limit. The
+    check can be disabled by setting ``ImageFont.MAX_STRING_LENGTH = None``.

programming_language: Markdown
filename: 10.0.0.rst
change_type: ModificationType.MODIFY
diff: @@ -170,6 +170,18 @@ now been fixed.
 This effectively dates to the PIL fork, since problem images would still have
 been processed before Pillow started checking for decompression bombs.
 
+Added ImageFont.MAX_STRING_LENGTH
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To protect against potential DOS attacks when using arbitrary strings as text
+input, Pillow will now raise a ``ValueError`` if the number of characters
+passed into ImageFont methods is over a certain limit,
+:py:data:`PIL.ImageFont.MAX_STRING_LENGTH`.
+
+This threshold can be changed by setting
+:py:data:`PIL.ImageFont.MAX_STRING_LENGTH`. It can be disabled by setting
+``ImageFont.MAX_STRING_LENGTH = None``.
+
 Other Changes
 =============
 

programming_language: Markdown
filename: ImageFont.py
change_type: ModificationType.MODIFY
diff: @@ -41,6 +41,9 @@ class Layout(IntEnum):
     RAQM = 1
 
 
+MAX_STRING_LENGTH = 1000000
+
+
 try:
     from . import _imagingft as core
 except ImportError as ex:
@@ -49,6 +52,12 @@ except ImportError as ex:
     core = DeferredError(ex)
 
 
+def _string_length_check(text):
+    if MAX_STRING_LENGTH is not None and len(text) > MAX_STRING_LENGTH:
+        msg = "too many characters in string"
+        raise ValueError(msg)
+
+
 # FIXME: add support for pilfont2 format (see FontFile.py)
 
 # --------------------------------------------------------------------
@@ -152,6 +161,7 @@ class ImageFont:
 
         :return: ``(left, top, right, bottom)`` bounding box
         """
+        _string_length_check(text)
         width, height = self.font.getsize(text)
         return 0, 0, width, height
 
@@ -162,6 +172,7 @@ class ImageFont:
 
         .. versionadded:: 9.2.0
         """
+        _string_length_check(text)
         width, height = self.font.getsize(text)
         return width
 
@@ -309,6 +320,7 @@ class FreeTypeFont:
 
         :return: Width for horizontal, height for vertical text.
         """
+        _string_length_check(text)
         return self.font.getlength(text, mode, direction, features, language) / 64
 
     def getbbox(
@@ -368,6 +380,7 @@ class FreeTypeFont:
 
         :return: ``(left, top, right, bottom)`` bounding box
         """
+        _string_length_check(text)
         size, offset = self.font.getsize(
             text, mode, direction, features, language, anchor
         )
@@ -546,6 +559,7 @@ class FreeTypeFont:
                  :py:mod:`PIL.Image.core` interface module, and the text offset, the
                  gap between the starting coordinate and the first marking
         """
+        _string_length_check(text)
         if start is None:
             start = (0, 0)
         im, size, offset = self.font.render(
@@ -684,6 +698,7 @@ class TransposedFont:
         if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270):
             msg = "text length is undefined for text rotated by 90 or 270 degrees"
             raise ValueError(msg)
+        _string_length_check(text)
         return self.font.getlength(text, *args, **kwargs)
 
 

programming_language: Python