Skip to content

Commit

Permalink
Merge pull request #7244 from radarhere/imagefont_max_string_length
Browse files Browse the repository at this point in the history
Added ImageFont.MAX_STRING_LENGTH
  • Loading branch information
radarhere authored Jun 30, 2023
2 parents 7c945f5 + d398fed commit e37b250
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 0 deletions.
19 changes: 19 additions & 0 deletions Tests/test_imagefont.py
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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

0 comments on commit e37b250

Please sign in to comment.