Skip to content

Commit fcfb677

Browse files
itamarofacebook-github-bot
authored andcommitted
gh-95778: CVE-2020-10735: Prevent DoS by very large int() (#96501)
Summary: cherry-picked the upstream 3.10 backport ``` git cherry-pick 8f0fa4b eace09e ``` this one is python/cpython#96537 original commit message below -------- Integer to and from text conversions via CPython's bignum `int` type is not safe against denial of service attacks due to malicious input. Very large input strings with hundred thousands of digits can consume several CPU seconds. This PR comes fresh from a pile of work done in our private PSRT security response team repo. This backports python/cpython#96499 aka 511ca94 Signed-off-by: Christian Heimes [Red Hat] <christian@python.org> Tons-of-polishing-up-by: Gregory P. Smith [Google] <greg@krypto.org> Reviews via the private PSRT repo via many others (see the NEWS entry in the PR). <!-- gh-issue-number: gh-95778 --> * Issue: gh-95778 <!-- /gh-issue-number --> I wrote up [a one pager for the release managers](https://docs.google.com/document/d/1KjuF_aXlzPUxTK4BMgezGJ2Pn7uevfX7g0_mvgHlL7Y/edit#). Reviewed By: alexmalyshev Differential Revision: D39369518 fbshipit-source-id: 6ed2def
1 parent 0749bca commit fcfb677

27 files changed

+779
-21
lines changed

Doc/data/python3.10.abi

+4-1
Original file line numberDiff line numberDiff line change
@@ -4851,7 +4851,7 @@
48514851
<var-decl name='root_cframe' type-id='type-id-23' visibility='default' filepath='./Include/cpython/pystate.h' line='148' column='1'/>
48524852
</data-member>
48534853
</class-decl>
4854-
<class-decl name='_is' size-in-bits='908160' is-struct='yes' visibility='default' filepath='./Include/internal/pycore_interp.h' line='220' column='1' id='type-id-224'>
4854+
<class-decl name='_is' size-in-bits='908224' is-struct='yes' visibility='default' filepath='./Include/internal/pycore_interp.h' line='220' column='1' id='type-id-224'>
48554855
<data-member access='public' layout-offset-in-bits='0'>
48564856
<var-decl name='next' type-id='type-id-225' visibility='default' filepath='./Include/internal/pycore_interp.h' line='222' column='1'/>
48574857
</data-member>
@@ -5002,6 +5002,9 @@
50025002
<data-member access='public' layout-offset-in-bits='121728'>
50035003
<var-decl name='type_cache' type-id='type-id-249' visibility='default' filepath='./Include/internal/pycore_interp.h' line='313' column='1'/>
50045004
</data-member>
5005+
<data-member access='public' layout-offset-in-bits='908160'>
5006+
<var-decl name='int_max_str_digits' type-id='type-id-9' visibility='default' filepath='./Include/internal/pycore_interp.h' line='309' column='1'/>
5007+
</data-member>
50055008
</class-decl>
50065009
<pointer-type-def type-id='type-id-224' size-in-bits='64' id='type-id-225'/>
50075010
<class-decl name='pyruntimestate' size-in-bits='5376' is-struct='yes' visibility='default' filepath='./Include/internal/pycore_runtime.h' line='61' column='1' id='type-id-250'>

Doc/library/functions.rst

+8
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,14 @@ are always available. They are listed here in alphabetical order.
891891
.. versionchanged:: 3.8
892892
Falls back to :meth:`__index__` if :meth:`__int__` is not defined.
893893

894+
.. versionchanged:: 3.10.7
895+
:class:`int` string inputs and string representations can be limited to
896+
help avoid denial of service attacks. A :exc:`ValueError` is raised when
897+
the limit is exceeded while converting a string *x* to an :class:`int` or
898+
when converting an :class:`int` into a string would exceed the limit.
899+
See the :ref:`integer string conversion length limitation
900+
<int_max_str_digits>` documentation.
901+
894902

895903
.. function:: isinstance(object, classinfo)
896904

Doc/library/json.rst

+11
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ is a lightweight data interchange format inspired by
1818
`JavaScript <https://en.wikipedia.org/wiki/JavaScript>`_ object literal syntax
1919
(although it is not a strict subset of JavaScript [#rfc-errata]_ ).
2020

21+
.. warning::
22+
Be cautious when parsing JSON data from untrusted sources. A malicious
23+
JSON string may cause the decoder to consume considerable CPU and memory
24+
resources. Limiting the size of data to be parsed is recommended.
25+
2126
:mod:`json` exposes an API familiar to users of the standard library
2227
:mod:`marshal` and :mod:`pickle` modules.
2328

@@ -248,6 +253,12 @@ Basic Usage
248253
be used to use another datatype or parser for JSON integers
249254
(e.g. :class:`float`).
250255

256+
.. versionchanged:: 3.10.7
257+
The default *parse_int* of :func:`int` now limits the maximum length of
258+
the integer string via the interpreter's :ref:`integer string
259+
conversion length limitation <int_max_str_digits>` to help avoid denial
260+
of service attacks.
261+
251262
*parse_constant*, if specified, will be called with one of the following
252263
strings: ``'-Infinity'``, ``'Infinity'``, ``'NaN'``.
253264
This can be used to raise an exception if invalid JSON numbers

Doc/library/stdtypes.rst

+166
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,13 @@ class`. float also has the following additional methods.
584584
:exc:`OverflowError` on infinities and a :exc:`ValueError` on
585585
NaNs.
586586

587+
.. note::
588+
589+
The values returned by ``as_integer_ratio()`` can be huge. Attempts
590+
to render such integers into decimal strings may bump into the
591+
:ref:`integer string conversion length limitation
592+
<int_max_str_digits>`.
593+
587594
.. method:: float.is_integer()
588595

589596
Return ``True`` if the float instance is finite with integral
@@ -5406,6 +5413,165 @@ types, where they are relevant. Some of these are not reported by the
54065413
[<class 'bool'>]
54075414

54085415

5416+
.. _int_max_str_digits:
5417+
5418+
Integer string conversion length limitation
5419+
===========================================
5420+
5421+
CPython has a global limit for converting between :class:`int` and :class:`str`
5422+
to mitigate denial of service attacks. This limit *only* applies to decimal or
5423+
other non-power-of-two number bases. Hexadecimal, octal, and binary conversions
5424+
are unlimited. The limit can be configured.
5425+
5426+
The :class:`int` type in CPython is an abitrary length number stored in binary
5427+
form (commonly known as a "bignum"). There exists no algorithm that can convert
5428+
a string to a binary integer or a binary integer to a string in linear time,
5429+
*unless* the base is a power of 2. Even the best known algorithms for base 10
5430+
have sub-quadratic complexity. Converting a large value such as ``int('1' *
5431+
500_000)`` can take over a second on a fast CPU.
5432+
5433+
Limiting conversion size offers a practical way to avoid `CVE-2020-10735
5434+
<https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-10735>`_.
5435+
5436+
The limit is applied to the number of digit characters in the input or output
5437+
string when a non-linear conversion algorithm would be involved. Underscores
5438+
and the sign are not counted towards the limit.
5439+
5440+
When an operation would exceed the limit, a :exc:`ValueError` is raised:
5441+
5442+
.. doctest::
5443+
5444+
>>> import sys
5445+
>>> sys.set_int_max_str_digits(4300) # Illustrative, this is the default.
5446+
>>> _ = int('2' * 5432)
5447+
Traceback (most recent call last):
5448+
...
5449+
ValueError: Exceeds the limit (4300) for integer string conversion: value has 5432 digits.
5450+
>>> i = int('2' * 4300)
5451+
>>> len(str(i))
5452+
4300
5453+
>>> i_squared = i*i
5454+
>>> len(str(i_squared))
5455+
Traceback (most recent call last):
5456+
...
5457+
ValueError: Exceeds the limit (4300) for integer string conversion: value has 8599 digits.
5458+
>>> len(hex(i_squared))
5459+
7144
5460+
>>> assert int(hex(i_squared), base=16) == i*i # Hexadecimal is unlimited.
5461+
5462+
The default limit is 4300 digits as provided in
5463+
:data:`sys.int_info.default_max_str_digits <sys.int_info>`.
5464+
The lowest limit that can be configured is 640 digits as provided in
5465+
:data:`sys.int_info.str_digits_check_threshold <sys.int_info>`.
5466+
5467+
Verification:
5468+
5469+
.. doctest::
5470+
5471+
>>> import sys
5472+
>>> assert sys.int_info.default_max_str_digits == 4300, sys.int_info
5473+
>>> assert sys.int_info.str_digits_check_threshold == 640, sys.int_info
5474+
>>> msg = int('578966293710682886880994035146873798396722250538762761564'
5475+
... '9252925514383915483333812743580549779436104706260696366600'
5476+
... '571186405732').to_bytes(53, 'big')
5477+
...
5478+
5479+
.. versionadded:: 3.10.7
5480+
5481+
Affected APIs
5482+
-------------
5483+
5484+
The limition only applies to potentially slow conversions between :class:`int`
5485+
and :class:`str` or :class:`bytes`:
5486+
5487+
* ``int(string)`` with default base 10.
5488+
* ``int(string, base)`` for all bases that are not a power of 2.
5489+
* ``str(integer)``.
5490+
* ``repr(integer)``
5491+
* any other string conversion to base 10, for example ``f"{integer}"``,
5492+
``"{}".format(integer)``, or ``b"%d" % integer``.
5493+
5494+
The limitations do not apply to functions with a linear algorithm:
5495+
5496+
* ``int(string, base)`` with base 2, 4, 8, 16, or 32.
5497+
* :func:`int.from_bytes` and :func:`int.to_bytes`.
5498+
* :func:`hex`, :func:`oct`, :func:`bin`.
5499+
* :ref:`formatspec` for hex, octal, and binary numbers.
5500+
* :class:`str` to :class:`float`.
5501+
* :class:`str` to :class:`decimal.Decimal`.
5502+
5503+
Configuring the limit
5504+
---------------------
5505+
5506+
Before Python starts up you can use an environment variable or an interpreter
5507+
command line flag to configure the limit:
5508+
5509+
* :envvar:`PYTHONINTMAXSTRDIGITS`, e.g.
5510+
``PYTHONINTMAXSTRDIGITS=640 python3`` to set the limit to 640 or
5511+
``PYTHONINTMAXSTRDIGITS=0 python3`` to disable the limitation.
5512+
* :option:`-X int_max_str_digits <-X>`, e.g.
5513+
``python3 -X int_max_str_digits=640``
5514+
* :data:`sys.flags.int_max_str_digits` contains the value of
5515+
:envvar:`PYTHONINTMAXSTRDIGITS` or :option:`-X int_max_str_digits <-X>`.
5516+
If both the env var and the ``-X`` option are set, the ``-X`` option takes
5517+
precedence. A value of *-1* indicates that both were unset, thus a value of
5518+
:data:`sys.int_info.default_max_str_digits` was used during initilization.
5519+
5520+
From code, you can inspect the current limit and set a new one using these
5521+
:mod:`sys` APIs:
5522+
5523+
* :func:`sys.get_int_max_str_digits` and :func:`sys.set_int_max_str_digits` are
5524+
a getter and setter for the interpreter-wide limit. Subinterpreters have
5525+
their own limit.
5526+
5527+
Information about the default and minimum can be found in :attr:`sys.int_info`:
5528+
5529+
* :data:`sys.int_info.default_max_str_digits <sys.int_info>` is the compiled-in
5530+
default limit.
5531+
* :data:`sys.int_info.str_digits_check_threshold <sys.int_info>` is the lowest
5532+
accepted value for the limit (other than 0 which disables it).
5533+
5534+
.. versionadded:: 3.10.7
5535+
5536+
.. caution::
5537+
5538+
Setting a low limit *can* lead to problems. While rare, code exists that
5539+
contains integer constants in decimal in their source that exceed the
5540+
minimum threshold. A consequence of setting the limit is that Python source
5541+
code containing decimal integer literals longer than the limit will
5542+
encounter an error during parsing, usually at startup time or import time or
5543+
even at installation time - anytime an up to date ``.pyc`` does not already
5544+
exist for the code. A workaround for source that contains such large
5545+
constants is to convert them to ``0x`` hexadecimal form as it has no limit.
5546+
5547+
Test your application thoroughly if you use a low limit. Ensure your tests
5548+
run with the limit set early via the environment or flag so that it applies
5549+
during startup and even during any installation step that may invoke Python
5550+
to precompile ``.py`` sources to ``.pyc`` files.
5551+
5552+
Recommended configuration
5553+
-------------------------
5554+
5555+
The default :data:`sys.int_info.default_max_str_digits` is expected to be
5556+
reasonable for most applications. If your application requires a different
5557+
limit, set it from your main entry point using Python version agnostic code as
5558+
these APIs were added in security patch releases in versions before 3.11.
5559+
5560+
Example::
5561+
5562+
>>> import sys
5563+
>>> if hasattr(sys, "set_int_max_str_digits"):
5564+
... upper_bound = 68000
5565+
... lower_bound = 4004
5566+
... current_limit = sys.get_int_max_str_digits()
5567+
... if current_limit == 0 or current_limit > upper_bound:
5568+
... sys.set_int_max_str_digits(upper_bound)
5569+
... elif current_limit < lower_bound:
5570+
... sys.set_int_max_str_digits(lower_bound)
5571+
5572+
If you need to disable it entirely, set it to ``0``.
5573+
5574+
54095575
.. rubric:: Footnotes
54105576

54115577
.. [1] Additional information on these special methods may be found in the Python

Doc/library/sys.rst

+44-13
Original file line numberDiff line numberDiff line change
@@ -460,9 +460,9 @@ always available.
460460
The :term:`named tuple` *flags* exposes the status of command line
461461
flags. The attributes are read only.
462462

463-
============================= ================================================================
463+
============================= ==============================================================================================================
464464
attribute flag
465-
============================= ================================================================
465+
============================= ==============================================================================================================
466466
:const:`debug` :option:`-d`
467467
:const:`inspect` :option:`-i`
468468
:const:`interactive` :option:`-i`
@@ -478,7 +478,8 @@ always available.
478478
:const:`hash_randomization` :option:`-R`
479479
:const:`dev_mode` :option:`-X dev <-X>` (:ref:`Python Development Mode <devmode>`)
480480
:const:`utf8_mode` :option:`-X utf8 <-X>`
481-
============================= ================================================================
481+
:const:`int_max_str_digits` :option:`-X int_max_str_digits <-X>` (:ref:`integer string conversion length limitation <int_max_str_digits>`)
482+
============================= ==============================================================================================================
482483

483484
.. versionchanged:: 3.2
484485
Added ``quiet`` attribute for the new :option:`-q` flag.
@@ -497,6 +498,9 @@ always available.
497498
Mode <devmode>` and the ``utf8_mode`` attribute for the new :option:`-X`
498499
``utf8`` flag.
499500

501+
.. versionchanged:: 3.10.7
502+
Added the ``int_max_str_digits`` attribute.
503+
500504

501505
.. data:: float_info
502506

@@ -677,6 +681,13 @@ always available.
677681

678682
.. versionadded:: 3.6
679683

684+
.. function:: get_int_max_str_digits()
685+
686+
Returns the current value for the :ref:`integer string conversion length
687+
limitation <int_max_str_digits>`. See also :func:`set_int_max_str_digits`.
688+
689+
.. versionadded:: 3.10.7
690+
680691
.. function:: getrefcount(object)
681692

682693
Return the reference count of the *object*. The count returned is generally one
@@ -950,19 +961,31 @@ always available.
950961

951962
.. tabularcolumns:: |l|L|
952963

953-
+-------------------------+----------------------------------------------+
954-
| Attribute | Explanation |
955-
+=========================+==============================================+
956-
| :const:`bits_per_digit` | number of bits held in each digit. Python |
957-
| | integers are stored internally in base |
958-
| | ``2**int_info.bits_per_digit`` |
959-
+-------------------------+----------------------------------------------+
960-
| :const:`sizeof_digit` | size in bytes of the C type used to |
961-
| | represent a digit |
962-
+-------------------------+----------------------------------------------+
964+
+----------------------------------------+-----------------------------------------------+
965+
| Attribute | Explanation |
966+
+========================================+===============================================+
967+
| :const:`bits_per_digit` | number of bits held in each digit. Python |
968+
| | integers are stored internally in base |
969+
| | ``2**int_info.bits_per_digit`` |
970+
+----------------------------------------+-----------------------------------------------+
971+
| :const:`sizeof_digit` | size in bytes of the C type used to |
972+
| | represent a digit |
973+
+----------------------------------------+-----------------------------------------------+
974+
| :const:`default_max_str_digits` | default value for |
975+
| | :func:`sys.get_int_max_str_digits` when it |
976+
| | is not otherwise explicitly configured. |
977+
+----------------------------------------+-----------------------------------------------+
978+
| :const:`str_digits_check_threshold` | minimum non-zero value for |
979+
| | :func:`sys.set_int_max_str_digits`, |
980+
| | :envvar:`PYTHONINTMAXSTRDIGITS`, or |
981+
| | :option:`-X int_max_str_digits <-X>`. |
982+
+----------------------------------------+-----------------------------------------------+
963983

964984
.. versionadded:: 3.1
965985

986+
.. versionchanged:: 3.10.7
987+
Added ``default_max_str_digits`` and ``str_digits_check_threshold``.
988+
966989

967990
.. data:: __interactivehook__
968991

@@ -1254,6 +1277,14 @@ always available.
12541277

12551278
.. availability:: Unix.
12561279

1280+
.. function:: set_int_max_str_digits(n)
1281+
1282+
Set the :ref:`integer string conversion length limitation
1283+
<int_max_str_digits>` used by this interpreter. See also
1284+
:func:`get_int_max_str_digits`.
1285+
1286+
.. versionadded:: 3.10.7
1287+
12571288
.. function:: setprofile(profilefunc)
12581289

12591290
.. index::

Doc/library/test.rst

+10
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,16 @@ The :mod:`test.support` module defines the following functions:
942942
.. versionadded:: 3.10
943943

944944

945+
.. function:: adjust_int_max_str_digits(max_digits)
946+
947+
This function returns a context manager that will change the global
948+
:func:`sys.set_int_max_str_digits` setting for the duration of the
949+
context to allow execution of test code that needs a different limit
950+
on the number of digits when converting between an integer and string.
951+
952+
.. versionadded:: 3.10.7
953+
954+
945955
The :mod:`test.support` module defines the following classes:
946956

947957

0 commit comments

Comments
 (0)