Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ImageFont.MAX_STRING_LENGTH #7244

Merged
merged 2 commits into from Jun 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 19 additions & 0 deletions Tests/test_imagefont.py
Expand Up @@ -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" * 1_000_001)
with pytest.raises(ValueError):
font.getbbox("A" * 1_000_001)
with pytest.raises(ValueError):
font.getmask2("A" * 1_000_001)

transposed_font = ImageFont.TransposedFont(font)
with pytest.raises(ValueError):
transposed_font.getlength("A" * 1_000_001)

default_font = ImageFont.load_default()
with pytest.raises(ValueError):
default_font.getlength("A" * 1_000_001)
with pytest.raises(ValueError):
default_font.getbbox("A" * 1_000_001)


@pytest.mark.parametrize(
"test_file",
[
Expand Down
18 changes: 18 additions & 0 deletions docs/reference/ImageFont.rst
Expand Up @@ -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
-------

Expand Down Expand Up @@ -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``.
12 changes: 12 additions & 0 deletions docs/releasenotes/10.0.0.rst
Expand Up @@ -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
=============

Expand Down
15 changes: 15 additions & 0 deletions src/PIL/ImageFont.py
Expand Up @@ -41,6 +41,9 @@ class Layout(IntEnum):
RAQM = 1


MAX_STRING_LENGTH = 1_000_000


try:
from . import _imagingft as core
except ImportError as ex:
Expand All @@ -49,6 +52,12 @@ class Layout(IntEnum):
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)

# --------------------------------------------------------------------
Expand Down Expand Up @@ -152,6 +161,7 @@ def getbbox(self, text, *args, **kwargs):

:return: ``(left, top, right, bottom)`` bounding box
"""
_string_length_check(text)
width, height = self.font.getsize(text)
return 0, 0, width, height

Expand All @@ -162,6 +172,7 @@ def getlength(self, text, *args, **kwargs):

.. versionadded:: 9.2.0
"""
_string_length_check(text)
width, height = self.font.getsize(text)
return width

Expand Down Expand Up @@ -309,6 +320,7 @@ def getlength(self, text, mode="", direction=None, features=None, language=None)

:return: Width for horizontal, height for vertical text.
"""
_string_length_check(text)
return self.font.getlength(text, mode, direction, features, language) / 64

def getbbox(
Expand Down Expand Up @@ -368,6 +380,7 @@ def getbbox(

:return: ``(left, top, right, bottom)`` bounding box
"""
_string_length_check(text)
size, offset = self.font.getsize(
text, mode, direction, features, language, anchor
)
Expand Down Expand Up @@ -546,6 +559,7 @@ def getmask2(
: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(
Expand Down Expand Up @@ -684,6 +698,7 @@ def getlength(self, text, *args, **kwargs):
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)


Expand Down