From 1fe1bb49c452b0318cad12ea9d97c3bef188e9a7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 30 Jun 2023 23:32:26 +1000 Subject: [PATCH] Added ImageFont.MAX_STRING_LENGTH Upstream-status: Backport [https://github.com/python-pillow/Pillow/commit/1fe1bb49c452b0318cad12ea9d97c3bef188e9a7] CVE: CVE-2023-44271 Comment: Refresh hunk for test_imagefont.py, ImageFont.py and Remove hunk 10.0.0.rst because in our version it is 9.4.0 Signed-off-by: Pawan Badganchi Signed-off-by: Dnyandev Padalkar --- Tests/test_imagefont.py | 19 +++++++++++++++++++ docs/reference/ImageFont.rst | 18 ++++++++++++++++++ src/PIL/ImageFont.py | 15 +++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 7fa8ff8cbfd..c50447a153d 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -1107,6 +1107,25 @@ 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", [ diff --git a/docs/reference/ImageFont.rst b/docs/reference/ImageFont.rst index 946bd3c4bed..2abfa0cc997 100644 --- a/docs/reference/ImageFont.rst +++ b/docs/reference/ImageFont.rst @@ -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``. diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 3ddc1aaad64..1030985ebc4 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -43,6 +43,9 @@ RAQM = 1 +MAX_STRING_LENGTH = 1000000 + + def __getattr__(name): for enum, prefix in {Layout: "LAYOUT_"}.items(): if name.startswith(prefix): @@ -67,6 +67,12 @@ core = _ImagingFtNotInstalled() +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) + + _UNSPECIFIED = object() @@ -192,6 +192,7 @@ :return: ``(left, top, right, bottom)`` bounding box """ + _string_length_check(text) width, height = self.font.getsize(text) return 0, 0, width, height @@ -202,6 +202,7 @@ .. versionadded:: 9.2.0 """ + _string_length_check(text) width, height = self.font.getsize(text) return width @@ -359,6 +359,7 @@ :return: Width for horizontal, height for vertical text. """ + _string_length_check(text) return self.font.getlength(text, mode, direction, features, language) / 64 def getbbox( @@ -418,6 +418,7 @@ :return: ``(left, top, right, bottom)`` bounding box """ + _string_length_check(text) size, offset = self.font.getsize( text, mode, direction, features, language, anchor ) @@ -762,6 +762,7 @@ :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 fill is _UNSPECIFIED: fill = Image.core.fill else: @@ -924,6 +924,7 @@ 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)