diff --git a/00001-rpath.patch b/00001-rpath.patch index f19b00d77ef12b63e1706bba12765ec348fc9f26..778c0771b1fafa6db83a6c66069285171a6a0c05 100644 --- a/00001-rpath.patch +++ b/00001-rpath.patch @@ -1,5 +1,15 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: David Malcolm +Date: Wed, 13 Jan 2010 21:25:18 +0000 +Subject: [PATCH] 00001: Fixup distutils/unixccompiler.py to remove standard + library path from rpath Was Patch0 in ivazquez' python3000 specfile + +--- + Lib/distutils/unixccompiler.py | 9 +++++++++ + 1 file changed, 9 insertions(+) + diff --git a/Lib/distutils/unixccompiler.py b/Lib/distutils/unixccompiler.py -index f0792de..4d83793 100644 +index d00c48981e..0283a28c19 100644 --- a/Lib/distutils/unixccompiler.py +++ b/Lib/distutils/unixccompiler.py @@ -82,6 +82,15 @@ class UnixCCompiler(CCompiler): @@ -18,6 +28,3 @@ index f0792de..4d83793 100644 def preprocess(self, source, output_file=None, macros=None, include_dirs=None, extra_preargs=None, extra_postargs=None): fixed_args = self._fix_compile_args(None, macros, include_dirs) --- -1.8.3.1 - diff --git a/00251-change-user-install-location.patch b/00251-change-user-install-location.patch index ac6901d62e51bc7a3498e4d0ce598a6585a616b3..53096ec807fccfab6459042ae18f3067bc2062c8 100644 --- a/00251-change-user-install-location.patch +++ b/00251-change-user-install-location.patch @@ -1,42 +1,70 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Lumir Balhar +From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Mon, 15 Feb 2021 12:19:27 +0100 Subject: [PATCH] 00251: Change user install location MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -Change the values of sysconfig's "posix_prefix" install scheme to /usr/local -when RPM build or venv/virtualenv is not detected, -to make pip, sysconfig and distutils install into an isolated location. +Set values of base and platbase in sysconfig from /usr +to /usr/local when RPM build is not detected +to make pip and similar tools install into separate location. -The original values are saved as an additional "rpm_prefix" install scheme. - -The site module adds the /usr/local paths to sys.path when site packages are -enabled and RPM build is not detected. +Set values of prefix and exec_prefix in distutils install command +to /usr/local if executable is /usr/bin/python* and RPM build +is not detected to make distutils and pypa/distutils install into separate location. Fedora Change: https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe +Downstream only. -Rewrote in Fedora 36+ to patch sysconfig instead of distutils, -see https://discuss.python.org/t/pep-632-deprecate-distutils-module/5134/104 +We've tried to rework in Fedora 36/Python 3.10 to follow https://bugs.python.org/issue43976 +but we have identified serious problems with that approach, +see https://bugzilla.redhat.com/2026979 or https://bugzilla.redhat.com/2097183 -Downstream only for now, waiting for https://bugs.python.org/issue43976 +pypa/distutils integration: https://github.com/pypa/distutils/pull/70 Co-authored-by: Petr Viktorin Co-authored-by: Miro Hrončok Co-authored-by: Michal Cyprian Co-authored-by: Lumír Balhar --- - Lib/site.py | 9 ++++++++- - Lib/sysconfig.py | 25 +++++++++++++++++++++++++ - Lib/test/test_sysconfig.py | 4 +++- - 3 files changed, 36 insertions(+), 2 deletions(-) + Lib/distutils/command/install.py | 8 ++++-- + Lib/site.py | 9 +++++- + Lib/sysconfig.py | 49 +++++++++++++++++++++++++++++++- + Lib/test/test_sysconfig.py | 17 +++++++++-- + 4 files changed, 77 insertions(+), 6 deletions(-) +diff --git a/Lib/distutils/command/install.py b/Lib/distutils/command/install.py +index 01d5331a63..79f70f0de4 100644 +--- a/Lib/distutils/command/install.py ++++ b/Lib/distutils/command/install.py +@@ -159,6 +159,8 @@ class install(Command): + + negative_opt = {'no-compile' : 'compile'} + ++ # Allow Fedora to add components to the prefix ++ _prefix_addition = getattr(sysconfig, '_prefix_addition', '') + + def initialize_options(self): + """Initializes options.""" +@@ -441,8 +443,10 @@ def finalize_unix(self): + raise DistutilsOptionError( + "must not supply exec-prefix without prefix") + +- self.prefix = os.path.normpath(sys.prefix) +- self.exec_prefix = os.path.normpath(sys.exec_prefix) ++ self.prefix = ( ++ os.path.normpath(sys.prefix) + self._prefix_addition) ++ self.exec_prefix = ( ++ os.path.normpath(sys.exec_prefix) + self._prefix_addition) + + else: + if self.exec_prefix is None: diff --git a/Lib/site.py b/Lib/site.py -index 939893eb5e..d1316c3355 100644 +index 69670d9d7f..104cb93899 100644 --- a/Lib/site.py +++ b/Lib/site.py -@@ -380,8 +380,15 @@ def getsitepackages(prefixes=None): +@@ -377,8 +377,15 @@ def getsitepackages(prefixes=None): return sitepackages def addsitepackages(known_paths, prefixes=None): @@ -54,55 +82,118 @@ index 939893eb5e..d1316c3355 100644 if os.path.isdir(sitedir): addsitedir(sitedir, known_paths) diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py -index daf9f00006..40e4edf0ae 100644 +index ebe3711827..55af57b335 100644 --- a/Lib/sysconfig.py +++ b/Lib/sysconfig.py -@@ -58,6 +58,31 @@ - }, - } +@@ -103,6 +103,11 @@ + else: + _INSTALL_SCHEMES['venv'] = _INSTALL_SCHEMES['posix_venv'] -+# backup the original posix_prefix as rpm_prefix -+# RPM packages use it and we need to be able to read it even when changed ++# For a brief period of time in the Fedora 36 life cycle, ++# this installation scheme existed and was documented in the release notes. ++# For backwards compatibility, we keep it here (at least on 3.10 and 3.11). +_INSTALL_SCHEMES['rpm_prefix'] = _INSTALL_SCHEMES['posix_prefix'] -+# Virtualenv >= 20.10.0 favors the "venv" scheme over the defaults when creating virtual environments. -+# See: https://github.com/pypa/virtualenv/commit/8da79db86d8a5c74d03667a40e64ff832076445e -+# See: https://bugs.python.org/issue45413 -+# "venv" should be the same as the unpatched posix_prefix for us, -+# so new virtual environments aren't created with paths like venv/local/bin/python. -+_INSTALL_SCHEMES['venv'] = _INSTALL_SCHEMES['posix_prefix'] + + + # NOTE: site.py has copy of this function. + # Sync it when modify this function. +@@ -162,6 +167,19 @@ def joinuser(*args): + }, + } + ++# This is used by distutils.command.install in the stdlib ++# as well as pypa/distutils (e.g. bundled in setuptools). ++# The self.prefix value is set to sys.prefix + /local/ ++# if neither RPM build nor virtual environment is ++# detected to make distutils install packages ++# into the separate location. ++# https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe +if (not (hasattr(sys, 'real_prefix') or + sys.prefix != sys.base_prefix) and + 'RPM_BUILD_ROOT' not in os.environ): -+ _INSTALL_SCHEMES['posix_prefix'] = { -+ 'stdlib': '{installed_base}/{platlibdir}/python{py_version_short}', -+ 'platstdlib': '{platbase}/{platlibdir}/python{py_version_short}', -+ 'purelib': '{base}/local/lib/python{py_version_short}/site-packages', -+ 'platlib': '{platbase}/local/{platlibdir}/python{py_version_short}/site-packages', -+ 'include': -+ '{installed_base}/include/python{py_version_short}{abiflags}', -+ 'platinclude': -+ '{installed_platbase}/include/python{py_version_short}{abiflags}', -+ 'scripts': '{base}/local/bin', -+ 'data': '{base}/local', -+ } ++ _prefix_addition = '/local' ++ ++ + _SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include', + 'scripts', 'data') - # NOTE: site.py has copy of this function. - # Sync it when modify this function. +@@ -258,11 +276,40 @@ def _extend_dict(target_dict, other_dict): + target_dict[key] = value + + ++_CONFIG_VARS_LOCAL = None ++ ++ ++def _config_vars_local(): ++ # This function returns the config vars with prefixes amended to /usr/local ++ # https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe ++ global _CONFIG_VARS_LOCAL ++ if _CONFIG_VARS_LOCAL is None: ++ _CONFIG_VARS_LOCAL = dict(get_config_vars()) ++ _CONFIG_VARS_LOCAL['base'] = '/usr/local' ++ _CONFIG_VARS_LOCAL['platbase'] = '/usr/local' ++ return _CONFIG_VARS_LOCAL ++ ++ + def _expand_vars(scheme, vars): + res = {} + if vars is None: + vars = {} +- _extend_dict(vars, get_config_vars()) ++ ++ # when we are not in a virtual environment or an RPM build ++ # we change '/usr' to '/usr/local' ++ # to avoid surprises, we explicitly check for the /usr/ prefix ++ # Python virtual environments have different prefixes ++ # we only do this for posix_prefix, not to mangle the venv scheme ++ # posix_prefix is used by sudo pip install ++ # we only change the defaults here, so explicit --prefix will take precedence ++ # https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe ++ if (scheme == 'posix_prefix' and ++ _PREFIX == '/usr' and ++ 'RPM_BUILD_ROOT' not in os.environ): ++ _extend_dict(vars, _config_vars_local()) ++ else: ++ _extend_dict(vars, get_config_vars()) ++ + if os.name == 'nt': + # On Windows we want to substitute 'lib' for schemes rather + # than the native value (without modifying vars, in case it diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py -index 9408657c91..fd49b2bcce 100644 +index d96371d242..72b028435f 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py -@@ -263,7 +263,7 @@ def test_get_config_h_filename(self): +@@ -111,8 +111,19 @@ def test_get_path(self): + for scheme in _INSTALL_SCHEMES: + for name in _INSTALL_SCHEMES[scheme]: + expected = _INSTALL_SCHEMES[scheme][name].format(**config_vars) ++ tested = get_path(name, scheme) ++ # https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe ++ if tested.startswith('/usr/local'): ++ # /usr/local should only be used in posix_prefix ++ self.assertEqual(scheme, 'posix_prefix') ++ # Fedora CI runs tests for venv and virtualenv that check for other prefixes ++ self.assertEqual(sys.prefix, '/usr') ++ # When building the RPM of Python, %check runs this with RPM_BUILD_ROOT set ++ # Fedora CI runs this with RPM_BUILD_ROOT unset ++ self.assertNotIn('RPM_BUILD_ROOT', os.environ) ++ tested = tested.replace('/usr/local', '/usr') + self.assertEqual( +- os.path.normpath(get_path(name, scheme)), ++ os.path.normpath(tested), + os.path.normpath(expected), + ) + +@@ -336,7 +347,7 @@ def test_get_config_h_filename(self): self.assertTrue(os.path.isfile(config_h), config_h) def test_get_scheme_names(self): -- wanted = ['nt', 'posix_home', 'posix_prefix'] -+ wanted = ['nt', 'posix_home', 'posix_prefix', 'rpm_prefix', 'venv'] +- wanted = ['nt', 'posix_home', 'posix_prefix', 'posix_venv', 'nt_venv', 'venv'] ++ wanted = ['nt', 'posix_home', 'posix_prefix', 'posix_venv', 'nt_venv', 'venv', 'rpm_prefix'] if HAS_USER_BASE: wanted.extend(['nt_user', 'osx_framework_user', 'posix_user']) self.assertEqual(get_scheme_names(), tuple(sorted(wanted))) -@@ -274,6 +274,8 @@ def test_symlink(self): # Issue 7880 +@@ -348,6 +359,8 @@ def test_symlink(self): # Issue 7880 cmd = "-c", "import sysconfig; print(sysconfig.get_platform())" self.assertEqual(py.call_real(*cmd), py.call_link(*cmd)) diff --git a/Python-3.10.2.tar.xz b/Python-3.11.1.tar.xz similarity index 65% rename from Python-3.10.2.tar.xz rename to Python-3.11.1.tar.xz index c35c1fc12a86bbc9b3c7242a28489bacf2d52675..9932def2b06f70c7121bf90939e979324122ee46 100644 Binary files a/Python-3.10.2.tar.xz and b/Python-3.11.1.tar.xz differ diff --git a/backport-0001-CVE-2020-10735.patch b/backport-0001-CVE-2020-10735.patch deleted file mode 100644 index 1769f6a1152683cfd230cb8b4f62a00c31a857e4..0000000000000000000000000000000000000000 --- a/backport-0001-CVE-2020-10735.patch +++ /dev/null @@ -1,1395 +0,0 @@ -From 8f0fa4bd10aba723aff988720cd26b93be99bc12 Mon Sep 17 00:00:00 2001 -From: "Gregory P. Smith" -Date: Fri, 2 Sep 2022 09:51:49 -0700 -Subject: [PATCH] [3.10] gh-95778: CVE-2020-10735: Prevent DoS by very large - int() (#96501) - -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 https://github.com/python/cpython/pull/96499 aka 511ca9452033ef95bc7d7fc404b8161068226002 - -Signed-off-by: Christian Heimes [Red Hat] -Tons-of-polishing-up-by: Gregory P. Smith [Google] -Reviews via the private PSRT repo via many others (see the NEWS entry in the PR). - - -* Issue: gh-95778 - - -I wrote up [a one pager for the release managers](https://docs.google.com/document/d/1KjuF_aXlzPUxTK4BMgezGJ2Pn7uevfX7g0_mvgHlL7Y/edit#). ---- - Doc/data/python3.10.abi | 5 +- - Doc/library/functions.rst | 8 + - Doc/library/json.rst | 11 ++ - Doc/library/stdtypes.rst | 166 ++++++++++++++++++ - Doc/library/sys.rst | 57 ++++-- - Doc/library/test.rst | 10 ++ - Doc/using/cmdline.rst | 14 +- - Doc/whatsnew/3.10.rst | 16 ++ - Include/internal/pycore_initconfig.h | 2 + - Include/internal/pycore_interp.h | 2 + - Include/internal/pycore_long.h | 35 ++++ - Lib/idlelib/idle_test/test_sidebar.py | 4 +- - Lib/test/support/__init__.py | 11 ++ - Lib/test/test_ast.py | 8 + - Lib/test/test_cmd_line.py | 33 ++++ - Lib/test/test_compile.py | 13 ++ - Lib/test/test_decimal.py | 18 ++ - Lib/test/test_int.py | 114 ++++++++++++ - Lib/test/test_json/test_decode.py | 9 + - Lib/test/test_sys.py | 10 +- - Lib/test/test_xmlrpc.py | 10 ++ - ...08-07-16-53-38.gh-issue-95778.ch010gps.rst | 14 ++ - Objects/longobject.c | 46 ++++- - Parser/pegen.c | 23 +++ - Python/clinic/sysmodule.c.h | 55 +++++- - Python/initconfig.c | 60 +++++++ - Python/sysmodule.c | 46 ++++- - 27 files changed, 779 insertions(+), 21 deletions(-) - create mode 100644 Misc/NEWS.d/next/Security/2022-08-07-16-53-38.gh-issue-95778.ch010gps.rst - -diff --git a/Doc/data/python3.10.abi b/Doc/data/python3.10.abi -index b7886ae..16fbb39 100644 ---- a/Doc/data/python3.10.abi -+++ b/Doc/data/python3.10.abi -@@ -4851,7 +4851,7 @@ - - - -- -+ - - - -@@ -5002,6 +5002,9 @@ - - - -+ -+ -+ - - - -diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst -index 9a9c87e..b237f7b 100644 ---- a/Doc/library/functions.rst -+++ b/Doc/library/functions.rst -@@ -891,6 +891,14 @@ are always available. They are listed here in alphabetical order. - .. versionchanged:: 3.8 - Falls back to :meth:`__index__` if :meth:`__int__` is not defined. - -+ .. versionchanged:: 3.10.7 -+ :class:`int` string inputs and string representations can be limited to -+ help avoid denial of service attacks. A :exc:`ValueError` is raised when -+ the limit is exceeded while converting a string *x* to an :class:`int` or -+ when converting an :class:`int` into a string would exceed the limit. -+ See the :ref:`integer string conversion length limitation -+ ` documentation. -+ - - .. function:: isinstance(object, classinfo) - -diff --git a/Doc/library/json.rst b/Doc/library/json.rst -index 1810e04..471f632 100644 ---- a/Doc/library/json.rst -+++ b/Doc/library/json.rst -@@ -18,6 +18,11 @@ is a lightweight data interchange format inspired by - `JavaScript `_ object literal syntax - (although it is not a strict subset of JavaScript [#rfc-errata]_ ). - -+.. warning:: -+ Be cautious when parsing JSON data from untrusted sources. A malicious -+ JSON string may cause the decoder to consume considerable CPU and memory -+ resources. Limiting the size of data to be parsed is recommended. -+ - :mod:`json` exposes an API familiar to users of the standard library - :mod:`marshal` and :mod:`pickle` modules. - -@@ -255,6 +260,12 @@ Basic Usage - be used to use another datatype or parser for JSON integers - (e.g. :class:`float`). - -+ .. versionchanged:: 3.10.7 -+ The default *parse_int* of :func:`int` now limits the maximum length of -+ the integer string via the interpreter's :ref:`integer string -+ conversion length limitation ` to help avoid denial -+ of service attacks. -+ - *parse_constant*, if specified, will be called with one of the following - strings: ``'-Infinity'``, ``'Infinity'``, ``'NaN'``. - This can be used to raise an exception if invalid JSON numbers -diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst -index 8fa252b..2723f58 100644 ---- a/Doc/library/stdtypes.rst -+++ b/Doc/library/stdtypes.rst -@@ -584,6 +584,13 @@ class`. float also has the following additional methods. - :exc:`OverflowError` on infinities and a :exc:`ValueError` on - NaNs. - -+ .. note:: -+ -+ The values returned by ``as_integer_ratio()`` can be huge. Attempts -+ to render such integers into decimal strings may bump into the -+ :ref:`integer string conversion length limitation -+ `. -+ - .. method:: float.is_integer() - - Return ``True`` if the float instance is finite with integral -@@ -5368,6 +5375,165 @@ types, where they are relevant. Some of these are not reported by the - [] - - -+.. _int_max_str_digits: -+ -+Integer string conversion length limitation -+=========================================== -+ -+CPython has a global limit for converting between :class:`int` and :class:`str` -+to mitigate denial of service attacks. This limit *only* applies to decimal or -+other non-power-of-two number bases. Hexadecimal, octal, and binary conversions -+are unlimited. The limit can be configured. -+ -+The :class:`int` type in CPython is an abitrary length number stored in binary -+form (commonly known as a "bignum"). There exists no algorithm that can convert -+a string to a binary integer or a binary integer to a string in linear time, -+*unless* the base is a power of 2. Even the best known algorithms for base 10 -+have sub-quadratic complexity. Converting a large value such as ``int('1' * -+500_000)`` can take over a second on a fast CPU. -+ -+Limiting conversion size offers a practical way to avoid `CVE-2020-10735 -+`_. -+ -+The limit is applied to the number of digit characters in the input or output -+string when a non-linear conversion algorithm would be involved. Underscores -+and the sign are not counted towards the limit. -+ -+When an operation would exceed the limit, a :exc:`ValueError` is raised: -+ -+.. doctest:: -+ -+ >>> import sys -+ >>> sys.set_int_max_str_digits(4300) # Illustrative, this is the default. -+ >>> _ = int('2' * 5432) -+ Traceback (most recent call last): -+ ... -+ ValueError: Exceeds the limit (4300) for integer string conversion: value has 5432 digits. -+ >>> i = int('2' * 4300) -+ >>> len(str(i)) -+ 4300 -+ >>> i_squared = i*i -+ >>> len(str(i_squared)) -+ Traceback (most recent call last): -+ ... -+ ValueError: Exceeds the limit (4300) for integer string conversion: value has 8599 digits. -+ >>> len(hex(i_squared)) -+ 7144 -+ >>> assert int(hex(i_squared), base=16) == i*i # Hexadecimal is unlimited. -+ -+The default limit is 4300 digits as provided in -+:data:`sys.int_info.default_max_str_digits `. -+The lowest limit that can be configured is 640 digits as provided in -+:data:`sys.int_info.str_digits_check_threshold `. -+ -+Verification: -+ -+.. doctest:: -+ -+ >>> import sys -+ >>> assert sys.int_info.default_max_str_digits == 4300, sys.int_info -+ >>> assert sys.int_info.str_digits_check_threshold == 640, sys.int_info -+ >>> msg = int('578966293710682886880994035146873798396722250538762761564' -+ ... '9252925514383915483333812743580549779436104706260696366600' -+ ... '571186405732').to_bytes(53, 'big') -+ ... -+ -+.. versionadded:: 3.10.7 -+ -+Affected APIs -+------------- -+ -+The limition only applies to potentially slow conversions between :class:`int` -+and :class:`str` or :class:`bytes`: -+ -+* ``int(string)`` with default base 10. -+* ``int(string, base)`` for all bases that are not a power of 2. -+* ``str(integer)``. -+* ``repr(integer)`` -+* any other string conversion to base 10, for example ``f"{integer}"``, -+ ``"{}".format(integer)``, or ``b"%d" % integer``. -+ -+The limitations do not apply to functions with a linear algorithm: -+ -+* ``int(string, base)`` with base 2, 4, 8, 16, or 32. -+* :func:`int.from_bytes` and :func:`int.to_bytes`. -+* :func:`hex`, :func:`oct`, :func:`bin`. -+* :ref:`formatspec` for hex, octal, and binary numbers. -+* :class:`str` to :class:`float`. -+* :class:`str` to :class:`decimal.Decimal`. -+ -+Configuring the limit -+--------------------- -+ -+Before Python starts up you can use an environment variable or an interpreter -+command line flag to configure the limit: -+ -+* :envvar:`PYTHONINTMAXSTRDIGITS`, e.g. -+ ``PYTHONINTMAXSTRDIGITS=640 python3`` to set the limit to 640 or -+ ``PYTHONINTMAXSTRDIGITS=0 python3`` to disable the limitation. -+* :option:`-X int_max_str_digits <-X>`, e.g. -+ ``python3 -X int_max_str_digits=640`` -+* :data:`sys.flags.int_max_str_digits` contains the value of -+ :envvar:`PYTHONINTMAXSTRDIGITS` or :option:`-X int_max_str_digits <-X>`. -+ If both the env var and the ``-X`` option are set, the ``-X`` option takes -+ precedence. A value of *-1* indicates that both were unset, thus a value of -+ :data:`sys.int_info.default_max_str_digits` was used during initilization. -+ -+From code, you can inspect the current limit and set a new one using these -+:mod:`sys` APIs: -+ -+* :func:`sys.get_int_max_str_digits` and :func:`sys.set_int_max_str_digits` are -+ a getter and setter for the interpreter-wide limit. Subinterpreters have -+ their own limit. -+ -+Information about the default and minimum can be found in :attr:`sys.int_info`: -+ -+* :data:`sys.int_info.default_max_str_digits ` is the compiled-in -+ default limit. -+* :data:`sys.int_info.str_digits_check_threshold ` is the lowest -+ accepted value for the limit (other than 0 which disables it). -+ -+.. versionadded:: 3.10.7 -+ -+.. caution:: -+ -+ Setting a low limit *can* lead to problems. While rare, code exists that -+ contains integer constants in decimal in their source that exceed the -+ minimum threshold. A consequence of setting the limit is that Python source -+ code containing decimal integer literals longer than the limit will -+ encounter an error during parsing, usually at startup time or import time or -+ even at installation time - anytime an up to date ``.pyc`` does not already -+ exist for the code. A workaround for source that contains such large -+ constants is to convert them to ``0x`` hexadecimal form as it has no limit. -+ -+ Test your application thoroughly if you use a low limit. Ensure your tests -+ run with the limit set early via the environment or flag so that it applies -+ during startup and even during any installation step that may invoke Python -+ to precompile ``.py`` sources to ``.pyc`` files. -+ -+Recommended configuration -+------------------------- -+ -+The default :data:`sys.int_info.default_max_str_digits` is expected to be -+reasonable for most applications. If your application requires a different -+limit, set it from your main entry point using Python version agnostic code as -+these APIs were added in security patch releases in versions before 3.11. -+ -+Example:: -+ -+ >>> import sys -+ >>> if hasattr(sys, "set_int_max_str_digits"): -+ ... upper_bound = 68000 -+ ... lower_bound = 4004 -+ ... current_limit = sys.get_int_max_str_digits() -+ ... if current_limit == 0 or current_limit > upper_bound: -+ ... sys.set_int_max_str_digits(upper_bound) -+ ... elif current_limit < lower_bound: -+ ... sys.set_int_max_str_digits(lower_bound) -+ -+If you need to disable it entirely, set it to ``0``. -+ -+ - .. rubric:: Footnotes - - .. [1] Additional information on these special methods may be found in the Python -diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst -index 29cb54b..d0b35ab 100644 ---- a/Doc/library/sys.rst -+++ b/Doc/library/sys.rst -@@ -462,9 +462,9 @@ always available. - The :term:`named tuple` *flags* exposes the status of command line - flags. The attributes are read only. - -- ============================= ================================================================ -+ ============================= ============================================================================================================== - attribute flag -- ============================= ================================================================ -+ ============================= ============================================================================================================== - :const:`debug` :option:`-d` - :const:`inspect` :option:`-i` - :const:`interactive` :option:`-i` -@@ -480,7 +480,8 @@ always available. - :const:`hash_randomization` :option:`-R` - :const:`dev_mode` :option:`-X dev <-X>` (:ref:`Python Development Mode `) - :const:`utf8_mode` :option:`-X utf8 <-X>` -- ============================= ================================================================ -+ :const:`int_max_str_digits` :option:`-X int_max_str_digits <-X>` (:ref:`integer string conversion length limitation `) -+ ============================= ============================================================================================================== - - .. versionchanged:: 3.2 - Added ``quiet`` attribute for the new :option:`-q` flag. -@@ -499,6 +500,9 @@ always available. - Mode ` and the ``utf8_mode`` attribute for the new :option:`-X` - ``utf8`` flag. - -+ .. versionchanged:: 3.10.7 -+ Added the ``int_max_str_digits`` attribute. -+ - - .. data:: float_info - -@@ -679,6 +683,13 @@ always available. - - .. versionadded:: 3.6 - -+.. function:: get_int_max_str_digits() -+ -+ Returns the current value for the :ref:`integer string conversion length -+ limitation `. See also :func:`set_int_max_str_digits`. -+ -+ .. versionadded:: 3.10.7 -+ - .. function:: getrefcount(object) - - Return the reference count of the *object*. The count returned is generally one -@@ -952,19 +963,31 @@ always available. - - .. tabularcolumns:: |l|L| - -- +-------------------------+----------------------------------------------+ -- | Attribute | Explanation | -- +=========================+==============================================+ -- | :const:`bits_per_digit` | number of bits held in each digit. Python | -- | | integers are stored internally in base | -- | | ``2**int_info.bits_per_digit`` | -- +-------------------------+----------------------------------------------+ -- | :const:`sizeof_digit` | size in bytes of the C type used to | -- | | represent a digit | -- +-------------------------+----------------------------------------------+ -+ +----------------------------------------+-----------------------------------------------+ -+ | Attribute | Explanation | -+ +========================================+===============================================+ -+ | :const:`bits_per_digit` | number of bits held in each digit. Python | -+ | | integers are stored internally in base | -+ | | ``2**int_info.bits_per_digit`` | -+ +----------------------------------------+-----------------------------------------------+ -+ | :const:`sizeof_digit` | size in bytes of the C type used to | -+ | | represent a digit | -+ +----------------------------------------+-----------------------------------------------+ -+ | :const:`default_max_str_digits` | default value for | -+ | | :func:`sys.get_int_max_str_digits` when it | -+ | | is not otherwise explicitly configured. | -+ +----------------------------------------+-----------------------------------------------+ -+ | :const:`str_digits_check_threshold` | minimum non-zero value for | -+ | | :func:`sys.set_int_max_str_digits`, | -+ | | :envvar:`PYTHONINTMAXSTRDIGITS`, or | -+ | | :option:`-X int_max_str_digits <-X>`. | -+ +----------------------------------------+-----------------------------------------------+ - - .. versionadded:: 3.1 - -+ .. versionchanged:: 3.10.7 -+ Added ``default_max_str_digits`` and ``str_digits_check_threshold``. -+ - - .. data:: __interactivehook__ - -@@ -1256,6 +1279,14 @@ always available. - - .. availability:: Unix. - -+.. function:: set_int_max_str_digits(n) -+ -+ Set the :ref:`integer string conversion length limitation -+ ` used by this interpreter. See also -+ :func:`get_int_max_str_digits`. -+ -+ .. versionadded:: 3.10.7 -+ - .. function:: setprofile(profilefunc) - - .. index:: -diff --git a/Doc/library/test.rst b/Doc/library/test.rst -index a8dc354..fff48bb 100644 ---- a/Doc/library/test.rst -+++ b/Doc/library/test.rst -@@ -935,6 +935,16 @@ The :mod:`test.support` module defines the following functions: - .. versionadded:: 3.10 - - -+.. function:: adjust_int_max_str_digits(max_digits) -+ -+ This function returns a context manager that will change the global -+ :func:`sys.set_int_max_str_digits` setting for the duration of the -+ context to allow execution of test code that needs a different limit -+ on the number of digits when converting between an integer and string. -+ -+ .. versionadded:: 3.10.7 -+ -+ - The :mod:`test.support` module defines the following classes: - - -diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst -index c4f65eb..1a0eb25 100644 ---- a/Doc/using/cmdline.rst -+++ b/Doc/using/cmdline.rst -@@ -457,6 +457,9 @@ Miscellaneous options - stored in a traceback of a trace. Use ``-X tracemalloc=NFRAME`` to start - tracing with a traceback limit of *NFRAME* frames. See the - :func:`tracemalloc.start` for more information. -+ * ``-X int_max_str_digits`` configures the :ref:`integer string conversion -+ length limitation `. See also -+ :envvar:`PYTHONINTMAXSTRDIGITS`. - * ``-X importtime`` to show how long each import takes. It shows module - name, cumulative time (including nested imports) and self time (excluding - nested imports). Note that its output may be broken in multi-threaded -@@ -506,6 +509,9 @@ Miscellaneous options - .. versionadded:: 3.10 - The ``-X warn_default_encoding`` option. - -+ .. versionadded:: 3.10.7 -+ The ``-X int_max_str_digits`` option. -+ - .. deprecated-removed:: 3.9 3.10 - The ``-X oldparser`` option. - -@@ -519,7 +525,6 @@ Options you shouldn't use - - .. _Jython: http://www.jython.org/ - -- - .. _using-on-envvars: - - Environment variables -@@ -676,6 +681,13 @@ conflict. - - .. versionadded:: 3.2.3 - -+.. envvar:: PYTHONINTMAXSTRDIGITS -+ -+ If this variable is set to an integer, it is used to configure the -+ interpreter's global :ref:`integer string conversion length limitation -+ `. -+ -+ .. versionadded:: 3.10.7 - - .. envvar:: PYTHONIOENCODING - -diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst -index c07523c..dd4da45 100644 ---- a/Doc/whatsnew/3.10.rst -+++ b/Doc/whatsnew/3.10.rst -@@ -2303,3 +2303,19 @@ Removed - - * The ``PyThreadState.use_tracing`` member has been removed to optimize Python. - (Contributed by Mark Shannon in :issue:`43760`.) -+ -+ -+Notable security feature in 3.10.7 -+================================== -+ -+Converting between :class:`int` and :class:`str` in bases other than 2 -+(binary), 4, 8 (octal), 16 (hexadecimal), or 32 such as base 10 (decimal) -+now raises a :exc:`ValueError` if the number of digits in string form is -+above a limit to avoid potential denial of service attacks due to the -+algorithmic complexity. This is a mitigation for `CVE-2020-10735 -+`_. -+This limit can be configured or disabled by environment variable, command -+line flag, or :mod:`sys` APIs. See the :ref:`integer string conversion -+length limitation ` documentation. The default limit -+is 4300 digits in string form. -+ -diff --git a/Include/internal/pycore_initconfig.h b/Include/internal/pycore_initconfig.h -index 4b009e8..21c71bf 100644 ---- a/Include/internal/pycore_initconfig.h -+++ b/Include/internal/pycore_initconfig.h -@@ -165,6 +165,8 @@ extern PyStatus _PyConfig_SetPyArgv( - PyAPI_FUNC(PyObject*) _PyConfig_AsDict(const PyConfig *config); - PyAPI_FUNC(int) _PyConfig_FromDict(PyConfig *config, PyObject *dict); - -+extern int _Py_global_config_int_max_str_digits; -+ - - /* --- Function used for testing ---------------------------------- */ - -diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h -index 4307b61..a564b12 100644 ---- a/Include/internal/pycore_interp.h -+++ b/Include/internal/pycore_interp.h -@@ -305,6 +305,8 @@ struct _is { - - struct ast_state ast; - struct type_cache type_cache; -+ -+ int int_max_str_digits; - }; - - extern void _PyInterpreterState_ClearModules(PyInterpreterState *interp); -diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h -index 2bea3a5..ee9a592 100644 ---- a/Include/internal/pycore_long.h -+++ b/Include/internal/pycore_long.h -@@ -11,6 +11,41 @@ extern "C" { - #include "pycore_interp.h" // PyInterpreterState.small_ints - #include "pycore_pystate.h" // _PyThreadState_GET() - -+/* -+ * Default int base conversion size limitation: Denial of Service prevention. -+ * -+ * Chosen such that this isn't wildly slow on modern hardware and so that -+ * everyone's existing deployed numpy test suite passes before -+ * https://github.com/numpy/numpy/issues/22098 is widely available. -+ * -+ * $ python -m timeit -s 's = * "1"*4300' 'int(s)' -+ * 2000 loops, best of 5: 125 usec per loop -+ * $ python -m timeit -s 's = * "1"*4300; v = int(s)' 'str(v)' -+ * 1000 loops, best of 5: 311 usec per loop -+ * (zen2 cloud VM) -+ * -+ * 4300 decimal digits fits a ~14284 bit number. -+ */ -+#define _PY_LONG_DEFAULT_MAX_STR_DIGITS 4300 -+/* -+ * Threshold for max digits check. For performance reasons int() and -+ * int.__str__() don't checks values that are smaller than this -+ * threshold. Acts as a guaranteed minimum size limit for bignums that -+ * applications can expect from CPython. -+ * -+ * % python -m timeit -s 's = "1"*640; v = int(s)' 'str(int(s))' -+ * 20000 loops, best of 5: 12 usec per loop -+ * -+ * "640 digits should be enough for anyone." - gps -+ * fits a ~2126 bit decimal number. -+ */ -+#define _PY_LONG_MAX_STR_DIGITS_THRESHOLD 640 -+ -+#if ((_PY_LONG_DEFAULT_MAX_STR_DIGITS != 0) && \ -+ (_PY_LONG_DEFAULT_MAX_STR_DIGITS < _PY_LONG_MAX_STR_DIGITS_THRESHOLD)) -+# error "_PY_LONG_DEFAULT_MAX_STR_DIGITS smaller than threshold." -+#endif -+ - // Don't call this function but _PyLong_GetZero() and _PyLong_GetOne() - static inline PyObject* __PyLong_GetSmallInt_internal(int value) - { -diff --git a/Lib/idlelib/idle_test/test_sidebar.py b/Lib/idlelib/idle_test/test_sidebar.py -index 53ac3eb..2c50134 100644 ---- a/Lib/idlelib/idle_test/test_sidebar.py -+++ b/Lib/idlelib/idle_test/test_sidebar.py -@@ -6,6 +6,7 @@ from itertools import chain - import unittest - import unittest.mock - from test.support import requires, swap_attr -+from test import support - import tkinter as tk - from idlelib.idle_test.tkinter_testing_utils import run_in_tk_mainloop - -@@ -615,7 +616,8 @@ class ShellSidebarTest(unittest.TestCase): - - @run_in_tk_mainloop() - def test_very_long_wrapped_line(self): -- with swap_attr(self.shell, 'squeezer', None): -+ with support.adjust_int_max_str_digits(11_111), \ -+ swap_attr(self.shell, 'squeezer', None): - self.do_input('x = ' + '1'*10_000 + '\n') - yield - self.assertEqual(self.get_sidebar_lines(), ['>>>']) -diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py -index 44c5bd5..c6000be 100644 ---- a/Lib/test/support/__init__.py -+++ b/Lib/test/support/__init__.py -@@ -2066,3 +2066,14 @@ def clear_ignored_deprecations(*tokens: object) -> None: - if warnings.filters != new_filters: - warnings.filters[:] = new_filters - warnings._filters_mutated() -+ -+ -+@contextlib.contextmanager -+def adjust_int_max_str_digits(max_digits): -+ """Temporarily change the integer string conversion length limit.""" -+ current = sys.get_int_max_str_digits() -+ try: -+ sys.set_int_max_str_digits(max_digits) -+ yield -+ finally: -+ sys.set_int_max_str_digits(current) -diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py -index 39fc7e9..af3335a 100644 ---- a/Lib/test/test_ast.py -+++ b/Lib/test/test_ast.py -@@ -999,6 +999,14 @@ Module( - self.assertRaises(ValueError, ast.literal_eval, '+True') - self.assertRaises(ValueError, ast.literal_eval, '2+3') - -+ def test_literal_eval_str_int_limit(self): -+ with support.adjust_int_max_str_digits(4000): -+ ast.literal_eval('3'*4000) # no error -+ with self.assertRaises(SyntaxError) as err_ctx: -+ ast.literal_eval('3'*4001) -+ self.assertIn('Exceeds the limit ', str(err_ctx.exception)) -+ self.assertIn(' Consider hexadecimal ', str(err_ctx.exception)) -+ - def test_literal_eval_complex(self): - # Issue #4907 - self.assertEqual(ast.literal_eval('6j'), 6j) -diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py -index d93e98f..a88ca60 100644 ---- a/Lib/test/test_cmd_line.py -+++ b/Lib/test/test_cmd_line.py -@@ -814,6 +814,39 @@ class CmdLineTest(unittest.TestCase): - self.assertTrue(proc.stderr.startswith(err_msg), proc.stderr) - self.assertNotEqual(proc.returncode, 0) - -+ def test_int_max_str_digits(self): -+ code = "import sys; print(sys.flags.int_max_str_digits, sys.get_int_max_str_digits())" -+ -+ assert_python_failure('-X', 'int_max_str_digits', '-c', code) -+ assert_python_failure('-X', 'int_max_str_digits=foo', '-c', code) -+ assert_python_failure('-X', 'int_max_str_digits=100', '-c', code) -+ -+ assert_python_failure('-c', code, PYTHONINTMAXSTRDIGITS='foo') -+ assert_python_failure('-c', code, PYTHONINTMAXSTRDIGITS='100') -+ -+ def res2int(res): -+ out = res.out.strip().decode("utf-8") -+ return tuple(int(i) for i in out.split()) -+ -+ res = assert_python_ok('-c', code) -+ self.assertEqual(res2int(res), (-1, sys.get_int_max_str_digits())) -+ res = assert_python_ok('-X', 'int_max_str_digits=0', '-c', code) -+ self.assertEqual(res2int(res), (0, 0)) -+ res = assert_python_ok('-X', 'int_max_str_digits=4000', '-c', code) -+ self.assertEqual(res2int(res), (4000, 4000)) -+ res = assert_python_ok('-X', 'int_max_str_digits=100000', '-c', code) -+ self.assertEqual(res2int(res), (100000, 100000)) -+ -+ res = assert_python_ok('-c', code, PYTHONINTMAXSTRDIGITS='0') -+ self.assertEqual(res2int(res), (0, 0)) -+ res = assert_python_ok('-c', code, PYTHONINTMAXSTRDIGITS='4000') -+ self.assertEqual(res2int(res), (4000, 4000)) -+ res = assert_python_ok( -+ '-X', 'int_max_str_digits=6000', '-c', code, -+ PYTHONINTMAXSTRDIGITS='4000' -+ ) -+ self.assertEqual(res2int(res), (6000, 6000)) -+ - - @unittest.skipIf(interpreter_requires_environment(), - 'Cannot run -I tests when PYTHON env vars are required.') -diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py -index 5f80a58..3162683 100644 ---- a/Lib/test/test_compile.py -+++ b/Lib/test/test_compile.py -@@ -191,6 +191,19 @@ if 1: - self.assertEqual(eval("0o777"), 511) - self.assertEqual(eval("-0o0000010"), -8) - -+ def test_int_literals_too_long(self): -+ n = 3000 -+ source = f"a = 1\nb = 2\nc = {'3'*n}\nd = 4" -+ with support.adjust_int_max_str_digits(n): -+ compile(source, "", "exec") # no errors. -+ with support.adjust_int_max_str_digits(n-1): -+ with self.assertRaises(SyntaxError) as err_ctx: -+ compile(source, "", "exec") -+ exc = err_ctx.exception -+ self.assertEqual(exc.lineno, 3) -+ self.assertIn('Exceeds the limit ', str(exc)) -+ self.assertIn(' Consider hexadecimal ', str(exc)) -+ - def test_unary_minus(self): - # Verify treatment of unary minus on negative numbers SF bug #660455 - if sys.maxsize == 2147483647: -diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py -index b6173a5..9d77cc3 100644 ---- a/Lib/test/test_decimal.py -+++ b/Lib/test/test_decimal.py -@@ -2474,6 +2474,15 @@ class CUsabilityTest(UsabilityTest): - class PyUsabilityTest(UsabilityTest): - decimal = P - -+ def setUp(self): -+ super().setUp() -+ self._previous_int_limit = sys.get_int_max_str_digits() -+ sys.set_int_max_str_digits(7000) -+ -+ def tearDown(self): -+ sys.set_int_max_str_digits(self._previous_int_limit) -+ super().tearDown() -+ - class PythonAPItests(unittest.TestCase): - - def test_abc(self): -@@ -4533,6 +4542,15 @@ class CCoverage(Coverage): - class PyCoverage(Coverage): - decimal = P - -+ def setUp(self): -+ super().setUp() -+ self._previous_int_limit = sys.get_int_max_str_digits() -+ sys.set_int_max_str_digits(7000) -+ -+ def tearDown(self): -+ sys.set_int_max_str_digits(self._previous_int_limit) -+ super().tearDown() -+ - class PyFunctionality(unittest.TestCase): - """Extra functionality in decimal.py""" - -diff --git a/Lib/test/test_int.py b/Lib/test/test_int.py -index d6be64e..2f678ed 100644 ---- a/Lib/test/test_int.py -+++ b/Lib/test/test_int.py -@@ -568,5 +568,119 @@ class IntTestCases(unittest.TestCase): - self.assertEqual(int('1_2_3_4_5_6_7', 32), 1144132807) - - -+class IntStrDigitLimitsTests(unittest.TestCase): -+ -+ int_class = int # Override this in subclasses to reuse the suite. -+ -+ def setUp(self): -+ super().setUp() -+ self._previous_limit = sys.get_int_max_str_digits() -+ sys.set_int_max_str_digits(2048) -+ -+ def tearDown(self): -+ sys.set_int_max_str_digits(self._previous_limit) -+ super().tearDown() -+ -+ def test_disabled_limit(self): -+ self.assertGreater(sys.get_int_max_str_digits(), 0) -+ self.assertLess(sys.get_int_max_str_digits(), 20_000) -+ with support.adjust_int_max_str_digits(0): -+ self.assertEqual(sys.get_int_max_str_digits(), 0) -+ i = self.int_class('1' * 20_000) -+ str(i) -+ self.assertGreater(sys.get_int_max_str_digits(), 0) -+ -+ def test_max_str_digits_edge_cases(self): -+ """Ignore the +/- sign and space padding.""" -+ int_class = self.int_class -+ maxdigits = sys.get_int_max_str_digits() -+ -+ int_class('1' * maxdigits) -+ int_class(' ' + '1' * maxdigits) -+ int_class('1' * maxdigits + ' ') -+ int_class('+' + '1' * maxdigits) -+ int_class('-' + '1' * maxdigits) -+ self.assertEqual(len(str(10 ** (maxdigits - 1))), maxdigits) -+ -+ def check(self, i, base=None): -+ with self.assertRaises(ValueError): -+ if base is None: -+ self.int_class(i) -+ else: -+ self.int_class(i, base) -+ -+ def test_max_str_digits(self): -+ maxdigits = sys.get_int_max_str_digits() -+ -+ self.check('1' * (maxdigits + 1)) -+ self.check(' ' + '1' * (maxdigits + 1)) -+ self.check('1' * (maxdigits + 1) + ' ') -+ self.check('+' + '1' * (maxdigits + 1)) -+ self.check('-' + '1' * (maxdigits + 1)) -+ self.check('1' * (maxdigits + 1)) -+ -+ i = 10 ** maxdigits -+ with self.assertRaises(ValueError): -+ str(i) -+ -+ def test_power_of_two_bases_unlimited(self): -+ """The limit does not apply to power of 2 bases.""" -+ maxdigits = sys.get_int_max_str_digits() -+ -+ for base in (2, 4, 8, 16, 32): -+ with self.subTest(base=base): -+ self.int_class('1' * (maxdigits + 1), base) -+ assert maxdigits < 100_000 -+ self.int_class('1' * 100_000, base) -+ -+ def test_underscores_ignored(self): -+ maxdigits = sys.get_int_max_str_digits() -+ -+ triples = maxdigits // 3 -+ s = '111' * triples -+ s_ = '1_11' * triples -+ self.int_class(s) # succeeds -+ self.int_class(s_) # succeeds -+ self.check(f'{s}111') -+ self.check(f'{s_}_111') -+ -+ def test_sign_not_counted(self): -+ int_class = self.int_class -+ max_digits = sys.get_int_max_str_digits() -+ s = '5' * max_digits -+ i = int_class(s) -+ pos_i = int_class(f'+{s}') -+ assert i == pos_i -+ neg_i = int_class(f'-{s}') -+ assert -pos_i == neg_i -+ str(pos_i) -+ str(neg_i) -+ -+ def _other_base_helper(self, base): -+ int_class = self.int_class -+ max_digits = sys.get_int_max_str_digits() -+ s = '2' * max_digits -+ i = int_class(s, base) -+ if base > 10: -+ with self.assertRaises(ValueError): -+ str(i) -+ elif base < 10: -+ str(i) -+ with self.assertRaises(ValueError) as err: -+ int_class(f'{s}1', base) -+ -+ def test_int_from_other_bases(self): -+ base = 3 -+ with self.subTest(base=base): -+ self._other_base_helper(base) -+ base = 36 -+ with self.subTest(base=base): -+ self._other_base_helper(base) -+ -+ -+class IntSubclassStrDigitLimitsTests(IntStrDigitLimitsTests): -+ int_class = IntSubclass -+ -+ - if __name__ == "__main__": - unittest.main() -diff --git a/Lib/test/test_json/test_decode.py b/Lib/test/test_json/test_decode.py -index fdb9e62..124045b 100644 ---- a/Lib/test/test_json/test_decode.py -+++ b/Lib/test/test_json/test_decode.py -@@ -2,6 +2,7 @@ import decimal - from io import StringIO - from collections import OrderedDict - from test.test_json import PyTest, CTest -+from test import support - - - class TestDecode: -@@ -95,5 +96,13 @@ class TestDecode: - d = self.json.JSONDecoder() - self.assertRaises(ValueError, d.raw_decode, 'a'*42, -50000) - -+ def test_limit_int(self): -+ maxdigits = 5000 -+ with support.adjust_int_max_str_digits(maxdigits): -+ self.loads('1' * maxdigits) -+ with self.assertRaises(ValueError): -+ self.loads('1' * (maxdigits + 1)) -+ -+ - class TestPyDecode(TestDecode, PyTest): pass - class TestCDecode(TestDecode, CTest): pass -diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py -index 35da72c..bdac8b6 100644 ---- a/Lib/test/test_sys.py -+++ b/Lib/test/test_sys.py -@@ -483,11 +483,17 @@ class SysModuleTest(unittest.TestCase): - self.assertIsInstance(sys.executable, str) - self.assertEqual(len(sys.float_info), 11) - self.assertEqual(sys.float_info.radix, 2) -- self.assertEqual(len(sys.int_info), 2) -+ self.assertEqual(len(sys.int_info), 4) - self.assertTrue(sys.int_info.bits_per_digit % 5 == 0) - self.assertTrue(sys.int_info.sizeof_digit >= 1) -+ self.assertGreaterEqual(sys.int_info.default_max_str_digits, 500) -+ self.assertGreaterEqual(sys.int_info.str_digits_check_threshold, 100) -+ self.assertGreater(sys.int_info.default_max_str_digits, -+ sys.int_info.str_digits_check_threshold) - self.assertEqual(type(sys.int_info.bits_per_digit), int) - self.assertEqual(type(sys.int_info.sizeof_digit), int) -+ self.assertIsInstance(sys.int_info.default_max_str_digits, int) -+ self.assertIsInstance(sys.int_info.str_digits_check_threshold, int) - self.assertIsInstance(sys.hexversion, int) - - self.assertEqual(len(sys.hash_info), 9) -@@ -592,7 +598,7 @@ class SysModuleTest(unittest.TestCase): - "dont_write_bytecode", "no_user_site", "no_site", - "ignore_environment", "verbose", "bytes_warning", "quiet", - "hash_randomization", "isolated", "dev_mode", "utf8_mode", -- "warn_default_encoding") -+ "warn_default_encoding", "int_max_str_digits") - for attr in attrs: - self.assertTrue(hasattr(sys.flags, attr), attr) - attr_type = bool if attr == "dev_mode" else int -diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py -index 1f06f5f..e38bffe 100644 ---- a/Lib/test/test_xmlrpc.py -+++ b/Lib/test/test_xmlrpc.py -@@ -287,6 +287,16 @@ class XMLRPCTestCase(unittest.TestCase): - check('9876543210.0123456789', - decimal.Decimal('9876543210.0123456789')) - -+ def test_limit_int(self): -+ check = self.check_loads -+ maxdigits = 5000 -+ with support.adjust_int_max_str_digits(maxdigits): -+ s = '1' * (maxdigits + 1) -+ with self.assertRaises(ValueError): -+ check(f'{s}', None) -+ with self.assertRaises(ValueError): -+ check(f'{s}', None) -+ - def test_get_host_info(self): - # see bug #3613, this raised a TypeError - transp = xmlrpc.client.Transport() -diff --git a/Misc/NEWS.d/next/Security/2022-08-07-16-53-38.gh-issue-95778.ch010gps.rst b/Misc/NEWS.d/next/Security/2022-08-07-16-53-38.gh-issue-95778.ch010gps.rst -new file mode 100644 -index 0000000..ea3b85d ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2022-08-07-16-53-38.gh-issue-95778.ch010gps.rst -@@ -0,0 +1,14 @@ -+Converting between :class:`int` and :class:`str` in bases other than 2 -+(binary), 4, 8 (octal), 16 (hexadecimal), or 32 such as base 10 (decimal) now -+raises a :exc:`ValueError` if the number of digits in string form is above a -+limit to avoid potential denial of service attacks due to the algorithmic -+complexity. This is a mitigation for `CVE-2020-10735 -+`_. -+ -+This new limit can be configured or disabled by environment variable, command -+line flag, or :mod:`sys` APIs. See the :ref:`integer string conversion length -+limitation ` documentation. The default limit is 4300 -+digits in string form. -+ -+Patch by Gregory P. Smith [Google] and Christian Heimes [Red Hat] with feedback -+from Victor Stinner, Thomas Wouters, Steve Dower, and Ned Deily. -diff --git a/Objects/longobject.c b/Objects/longobject.c -index 685bd56..780ea81 100644 ---- a/Objects/longobject.c -+++ b/Objects/longobject.c -@@ -4,6 +4,7 @@ - - #include "Python.h" - #include "pycore_bitutils.h" // _Py_popcount32() -+#include "pycore_initconfig.h" // _Py_global_config_int_max_str_digits - #include "pycore_interp.h" // _PY_NSMALLPOSINTS - #include "pycore_long.h" // __PyLong_GetSmallInt_internal() - #include "pycore_object.h" // _PyObject_InitVar() -@@ -35,6 +36,8 @@ _Py_IDENTIFIER(big); - #define IS_SMALL_INT(ival) (-NSMALLNEGINTS <= (ival) && (ival) < NSMALLPOSINTS) - #define IS_SMALL_UINT(ival) ((ival) < NSMALLPOSINTS) - -+#define _MAX_STR_DIGITS_ERROR_FMT "Exceeds the limit (%d) for integer string conversion: value has %zd digits" -+ - static PyObject * - get_small_int(sdigit ival) - { -@@ -1660,6 +1663,17 @@ long_to_decimal_string_internal(PyObject *aa, - tenpow *= 10; - strlen++; - } -+ if (strlen > _PY_LONG_MAX_STR_DIGITS_THRESHOLD) { -+ PyInterpreterState *interp = _PyInterpreterState_GET(); -+ int max_str_digits = interp->int_max_str_digits; -+ Py_ssize_t strlen_nosign = strlen - negative; -+ if ((max_str_digits > 0) && (strlen_nosign > max_str_digits)) { -+ Py_DECREF(scratch); -+ PyErr_Format(PyExc_ValueError, _MAX_STR_DIGITS_ERROR_FMT, -+ max_str_digits, strlen_nosign); -+ return -1; -+ } -+ } - if (writer) { - if (_PyUnicodeWriter_Prepare(writer, strlen, '9') == -1) { - Py_DECREF(scratch); -@@ -2173,6 +2187,7 @@ PyLong_FromString(const char *str, char **pend, int base) - - start = str; - if ((base & (base - 1)) == 0) { -+ /* binary bases are not limited by int_max_str_digits */ - int res = long_from_binary_base(&str, base, &z); - if (res < 0) { - /* Syntax error. */ -@@ -2324,6 +2339,17 @@ digit beyond the first. - goto onError; - } - -+ /* Limit the size to avoid excessive computation attacks. */ -+ if (digits > _PY_LONG_MAX_STR_DIGITS_THRESHOLD) { -+ PyInterpreterState *interp = _PyInterpreterState_GET(); -+ int max_str_digits = interp->int_max_str_digits; -+ if ((max_str_digits > 0) && (digits > max_str_digits)) { -+ PyErr_Format(PyExc_ValueError, _MAX_STR_DIGITS_ERROR_FMT, -+ max_str_digits, digits); -+ return NULL; -+ } -+ } -+ - /* Create an int object that can contain the largest possible - * integer with this base and length. Note that there's no - * need to initialize z->ob_digit -- no slot is read up before -@@ -4944,6 +4970,7 @@ long_new_impl(PyTypeObject *type, PyObject *x, PyObject *obase) - } - return PyLong_FromLong(0L); - } -+ /* default base and limit, forward to standard implementation */ - if (obase == NULL) - return PyNumber_Long(x); - -@@ -5674,6 +5701,8 @@ internal representation of integers. The attributes are read only."); - static PyStructSequence_Field int_info_fields[] = { - {"bits_per_digit", "size of a digit in bits"}, - {"sizeof_digit", "size in bytes of the C type used to represent a digit"}, -+ {"default_max_str_digits", "maximum string conversion digits limitation"}, -+ {"str_digits_check_threshold", "minimum positive value for int_max_str_digits"}, - {NULL, NULL} - }; - -@@ -5681,7 +5710,7 @@ static PyStructSequence_Desc int_info_desc = { - "sys.int_info", /* name */ - int_info__doc__, /* doc */ - int_info_fields, /* fields */ -- 2 /* number of fields */ -+ 4 /* number of fields */ - }; - - PyObject * -@@ -5696,6 +5725,17 @@ PyLong_GetInfo(void) - PyLong_FromLong(PyLong_SHIFT)); - PyStructSequence_SET_ITEM(int_info, field++, - PyLong_FromLong(sizeof(digit))); -+ /* -+ * The following two fields were added after investigating uses of -+ * sys.int_info in the wild: Exceedingly rarely used. The ONLY use found was -+ * numba using sys.int_info.bits_per_digit as attribute access rather than -+ * sequence unpacking. Cython and sympy also refer to sys.int_info but only -+ * as info for debugging. No concern about adding these in a backport. -+ */ -+ PyStructSequence_SET_ITEM(int_info, field++, -+ PyLong_FromLong(_PY_LONG_DEFAULT_MAX_STR_DIGITS)); -+ PyStructSequence_SET_ITEM(int_info, field++, -+ PyLong_FromLong(_PY_LONG_MAX_STR_DIGITS_THRESHOLD)); - if (PyErr_Occurred()) { - Py_CLEAR(int_info); - return NULL; -@@ -5720,6 +5760,10 @@ _PyLong_Init(PyInterpreterState *interp) - - interp->small_ints[i] = v; - } -+ interp->int_max_str_digits = _Py_global_config_int_max_str_digits; -+ if (interp->int_max_str_digits == -1) { -+ interp->int_max_str_digits = _PY_LONG_DEFAULT_MAX_STR_DIGITS; -+ } - return 0; - } - -diff --git a/Parser/pegen.c b/Parser/pegen.c -index e507415..71940be 100644 ---- a/Parser/pegen.c -+++ b/Parser/pegen.c -@@ -1,5 +1,6 @@ - #include - #include "pycore_ast.h" // _PyAST_Validate(), -+#include "pycore_pystate.h" // _PyThreadState_GET() - #include - #include "tokenizer.h" - -@@ -1118,6 +1119,28 @@ _PyPegen_number_token(Parser *p) - - if (c == NULL) { - p->error_indicator = 1; -+ PyThreadState *tstate = _PyThreadState_GET(); -+ // The only way a ValueError should happen in _this_ code is via -+ // PyLong_FromString hitting a length limit. -+ if (tstate->curexc_type == PyExc_ValueError && -+ tstate->curexc_value != NULL) { -+ PyObject *type, *value, *tb; -+ // This acts as PyErr_Clear() as we're replacing curexc. -+ PyErr_Fetch(&type, &value, &tb); -+ Py_XDECREF(tb); -+ Py_DECREF(type); -+ /* Intentionally omitting columns to avoid a wall of 1000s of '^'s -+ * on the error message. Nobody is going to overlook their huge -+ * numeric literal once given the line. */ -+ RAISE_ERROR_KNOWN_LOCATION( -+ p, PyExc_SyntaxError, -+ t->lineno, -1 /* col_offset */, -+ t->end_lineno, -1 /* end_col_offset */, -+ "%S - Consider hexadecimal for huge integer literals " -+ "to avoid decimal conversion limits.", -+ value); -+ Py_DECREF(value); -+ } - return NULL; - } - -diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h -index 04c8481..2a6ad89 100644 ---- a/Python/clinic/sysmodule.c.h -+++ b/Python/clinic/sysmodule.c.h -@@ -647,6 +647,59 @@ exit: - - #endif /* defined(USE_MALLOPT) */ - -+PyDoc_STRVAR(sys_get_int_max_str_digits__doc__, -+"get_int_max_str_digits($module, /)\n" -+"--\n" -+"\n" -+"Set the maximum string digits limit for non-binary int<->str conversions."); -+ -+#define SYS_GET_INT_MAX_STR_DIGITS_METHODDEF \ -+ {"get_int_max_str_digits", (PyCFunction)sys_get_int_max_str_digits, METH_NOARGS, sys_get_int_max_str_digits__doc__}, -+ -+static PyObject * -+sys_get_int_max_str_digits_impl(PyObject *module); -+ -+static PyObject * -+sys_get_int_max_str_digits(PyObject *module, PyObject *Py_UNUSED(ignored)) -+{ -+ return sys_get_int_max_str_digits_impl(module); -+} -+ -+PyDoc_STRVAR(sys_set_int_max_str_digits__doc__, -+"set_int_max_str_digits($module, /, maxdigits)\n" -+"--\n" -+"\n" -+"Set the maximum string digits limit for non-binary int<->str conversions."); -+ -+#define SYS_SET_INT_MAX_STR_DIGITS_METHODDEF \ -+ {"set_int_max_str_digits", (PyCFunction)(void(*)(void))sys_set_int_max_str_digits, METH_FASTCALL|METH_KEYWORDS, sys_set_int_max_str_digits__doc__}, -+ -+static PyObject * -+sys_set_int_max_str_digits_impl(PyObject *module, int maxdigits); -+ -+static PyObject * -+sys_set_int_max_str_digits(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) -+{ -+ PyObject *return_value = NULL; -+ static const char * const _keywords[] = {"maxdigits", NULL}; -+ static _PyArg_Parser _parser = {NULL, _keywords, "set_int_max_str_digits", 0}; -+ PyObject *argsbuf[1]; -+ int maxdigits; -+ -+ args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); -+ if (!args) { -+ goto exit; -+ } -+ maxdigits = _PyLong_AsInt(args[0]); -+ if (maxdigits == -1 && PyErr_Occurred()) { -+ goto exit; -+ } -+ return_value = sys_set_int_max_str_digits_impl(module, maxdigits); -+ -+exit: -+ return return_value; -+} -+ - PyDoc_STRVAR(sys_getrefcount__doc__, - "getrefcount($module, object, /)\n" - "--\n" -@@ -983,4 +1036,4 @@ sys__deactivate_opcache(PyObject *module, PyObject *Py_UNUSED(ignored)) - #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF - #define SYS_GETANDROIDAPILEVEL_METHODDEF - #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */ --/*[clinic end generated code: output=68c62b9ca317a0c8 input=a9049054013a1b77]*/ -+/*[clinic end generated code: output=6230a1e3a4415744 input=a9049054013a1b77]*/ -diff --git a/Python/initconfig.c b/Python/initconfig.c -index b298611..87b9210 100644 ---- a/Python/initconfig.c -+++ b/Python/initconfig.c -@@ -3,6 +3,7 @@ - #include "pycore_getopt.h" // _PyOS_GetOpt() - #include "pycore_initconfig.h" // _PyStatus_OK() - #include "pycore_interp.h" // _PyInterpreterState.runtime -+#include "pycore_long.h" // _PY_LONG_MAX_STR_DIGITS_THRESHOLD - #include "pycore_pathconfig.h" // _Py_path_config - #include "pycore_pyerrors.h" // _PyErr_Fetch() - #include "pycore_pylifecycle.h" // _Py_PreInitializeFromConfig() -@@ -95,6 +96,9 @@ static const char usage_3[] = "\ - -X pycache_prefix=PATH: enable writing .pyc files to a parallel tree rooted at the\n\ - given directory instead of to the code tree\n\ - -X warn_default_encoding: enable opt-in EncodingWarning for 'encoding=None'\n\ -+ -X int_max_str_digits=number: limit the size of int<->str conversions.\n\ -+ This helps avoid denial of service attacks when parsing untrusted data.\n\ -+ The default is sys.int_info.default_max_str_digits. 0 disables.\n\ - \n\ - --check-hash-based-pycs always|default|never:\n\ - control how Python invalidates hash-based .pyc files\n\ -@@ -121,6 +125,10 @@ static const char usage_6[] = - " to seed the hashes of str and bytes objects. It can also be set to an\n" - " integer in the range [0,4294967295] to get hash values with a\n" - " predictable seed.\n" -+"PYTHONINTMAXSTRDIGITS: limits the maximum digit characters in an int value\n" -+" when converting from a string and when converting an int back to a str.\n" -+" A value of 0 disables the limit. Conversions to or from bases 2, 4, 8,\n" -+" 16, and 32 are never limited.\n" - "PYTHONMALLOC: set the Python memory allocators and/or install debug hooks\n" - " on Python memory allocators. Use PYTHONMALLOC=debug to install debug\n" - " hooks.\n" -@@ -722,6 +730,10 @@ _PyConfig_InitCompatConfig(PyConfig *config) - #endif - } - -+/* Excluded from public struct PyConfig for backporting reasons. */ -+/* default to unconfigured, _PyLong_InitTypes() does the rest */ -+int _Py_global_config_int_max_str_digits = -1; -+ - - static void - config_init_defaults(PyConfig *config) -@@ -1758,6 +1770,48 @@ config_init_tracemalloc(PyConfig *config) - return _PyStatus_OK(); - } - -+static PyStatus -+config_init_int_max_str_digits(PyConfig *config) -+{ -+ int maxdigits; -+ int valid = 0; -+ -+ const char *env = config_get_env(config, "PYTHONINTMAXSTRDIGITS"); -+ if (env) { -+ if (!_Py_str_to_int(env, &maxdigits)) { -+ valid = ((maxdigits == 0) || (maxdigits >= _PY_LONG_MAX_STR_DIGITS_THRESHOLD)); -+ } -+ if (!valid) { -+#define STRINGIFY(VAL) _STRINGIFY(VAL) -+#define _STRINGIFY(VAL) #VAL -+ return _PyStatus_ERR( -+ "PYTHONINTMAXSTRDIGITS: invalid limit; must be >= " -+ STRINGIFY(_PY_LONG_MAX_STR_DIGITS_THRESHOLD) -+ " or 0 for unlimited."); -+ } -+ _Py_global_config_int_max_str_digits = maxdigits; -+ } -+ -+ const wchar_t *xoption = config_get_xoption(config, L"int_max_str_digits"); -+ if (xoption) { -+ const wchar_t *sep = wcschr(xoption, L'='); -+ if (sep) { -+ if (!config_wstr_to_int(sep + 1, &maxdigits)) { -+ valid = ((maxdigits == 0) || (maxdigits >= _PY_LONG_MAX_STR_DIGITS_THRESHOLD)); -+ } -+ } -+ if (!valid) { -+ return _PyStatus_ERR( -+ "-X int_max_str_digits: invalid limit; must be >= " -+ STRINGIFY(_PY_LONG_MAX_STR_DIGITS_THRESHOLD) -+ " or 0 for unlimited."); -+#undef _STRINGIFY -+#undef STRINGIFY -+ } -+ _Py_global_config_int_max_str_digits = maxdigits; -+ } -+ return _PyStatus_OK(); -+} - - static PyStatus - config_init_pycache_prefix(PyConfig *config) -@@ -1809,6 +1863,12 @@ config_read_complex_options(PyConfig *config) - return status; - } - } -+ if (_Py_global_config_int_max_str_digits < 0) { -+ status = config_init_int_max_str_digits(config); -+ if (_PyStatus_EXCEPTION(status)) { -+ return status; -+ } -+ } - - if (config->pycache_prefix == NULL) { - status = config_init_pycache_prefix(config); -diff --git a/Python/sysmodule.c b/Python/sysmodule.c -index ac49f78..1d5a06a 100644 ---- a/Python/sysmodule.c -+++ b/Python/sysmodule.c -@@ -17,6 +17,7 @@ Data members: - #include "Python.h" - #include "pycore_ceval.h" // _Py_RecursionLimitLowerWaterMark() - #include "pycore_initconfig.h" // _PyStatus_EXCEPTION() -+#include "pycore_long.h" // _PY_LONG_MAX_STR_DIGITS_THRESHOLD - #include "pycore_object.h" // _PyObject_IS_GC() - #include "pycore_pathconfig.h" // _PyPathConfig_ComputeSysPath0() - #include "pycore_pyerrors.h" // _PyErr_Fetch() -@@ -1652,6 +1653,45 @@ sys_mdebug_impl(PyObject *module, int flag) - } - #endif /* USE_MALLOPT */ - -+ -+/*[clinic input] -+sys.get_int_max_str_digits -+ -+Set the maximum string digits limit for non-binary int<->str conversions. -+[clinic start generated code]*/ -+ -+static PyObject * -+sys_get_int_max_str_digits_impl(PyObject *module) -+/*[clinic end generated code: output=0042f5e8ae0e8631 input=8dab13e2023e60d5]*/ -+{ -+ PyInterpreterState *interp = _PyInterpreterState_GET(); -+ return PyLong_FromSsize_t(interp->int_max_str_digits); -+} -+ -+/*[clinic input] -+sys.set_int_max_str_digits -+ -+ maxdigits: int -+ -+Set the maximum string digits limit for non-binary int<->str conversions. -+[clinic start generated code]*/ -+ -+static PyObject * -+sys_set_int_max_str_digits_impl(PyObject *module, int maxdigits) -+/*[clinic end generated code: output=734d4c2511f2a56d input=d7e3f325db6910c5]*/ -+{ -+ PyThreadState *tstate = _PyThreadState_GET(); -+ if ((!maxdigits) || (maxdigits >= _PY_LONG_MAX_STR_DIGITS_THRESHOLD)) { -+ tstate->interp->int_max_str_digits = maxdigits; -+ Py_RETURN_NONE; -+ } else { -+ PyErr_Format( -+ PyExc_ValueError, "maxdigits must be 0 or larger than %d", -+ _PY_LONG_MAX_STR_DIGITS_THRESHOLD); -+ return NULL; -+ } -+} -+ - size_t - _PySys_GetSizeOf(PyObject *o) - { -@@ -2027,6 +2067,8 @@ static PyMethodDef sys_methods[] = { - SYS_GETANDROIDAPILEVEL_METHODDEF - SYS_UNRAISABLEHOOK_METHODDEF - SYS__DEACTIVATE_OPCACHE_METHODDEF -+ SYS_GET_INT_MAX_STR_DIGITS_METHODDEF -+ SYS_SET_INT_MAX_STR_DIGITS_METHODDEF - {NULL, NULL} /* sentinel */ - }; - -@@ -2516,6 +2558,7 @@ static PyStructSequence_Field flags_fields[] = { - {"dev_mode", "-X dev"}, - {"utf8_mode", "-X utf8"}, - {"warn_default_encoding", "-X warn_default_encoding"}, -+ {"int_max_str_digits", "-X int_max_str_digits"}, - {0} - }; - -@@ -2523,7 +2566,7 @@ static PyStructSequence_Desc flags_desc = { - "sys.flags", /* name */ - flags__doc__, /* doc */ - flags_fields, /* fields */ -- 16 -+ 17 - }; - - static int -@@ -2563,6 +2606,7 @@ set_flags_from_config(PyInterpreterState *interp, PyObject *flags) - SetFlagObj(PyBool_FromLong(config->dev_mode)); - SetFlag(preconfig->utf8_mode); - SetFlag(config->warn_default_encoding); -+ SetFlag(_Py_global_config_int_max_str_digits); - #undef SetFlagObj - #undef SetFlag - return 0; --- -2.36.1 - diff --git a/backport-0002-CVE-2020-10735.patch b/backport-0002-CVE-2020-10735.patch deleted file mode 100644 index 89716a11518b158a59daac06af1a3516b5901e5e..0000000000000000000000000000000000000000 --- a/backport-0002-CVE-2020-10735.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 3a56a938f375f17f39baeb4b1529793dd0eb79b8 Mon Sep 17 00:00:00 2001 -From: "Miss Islington (bot)" - <31488909+miss-islington@users.noreply.github.com> -Date: Sun, 4 Sep 2022 00:12:34 -0700 -Subject: [PATCH] gh-95778: remove unneeded doc note on float.as_integer_ratio - (GH-96553) - -Per mdickinson@'s comment on the main branch PR. -(cherry picked from commit 69bb83c2bf254f92491d527ccec1ff41897add56) - -Co-authored-by: Gregory P. Smith ---- - Doc/library/stdtypes.rst | 7 ------- - 1 file changed, 7 deletions(-) - -diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst -index bc365bb..c82a6ad 100644 ---- a/Doc/library/stdtypes.rst -+++ b/Doc/library/stdtypes.rst -@@ -584,13 +584,6 @@ class`. float also has the following additional methods. - :exc:`OverflowError` on infinities and a :exc:`ValueError` on - NaNs. - -- .. note:: -- -- The values returned by ``as_integer_ratio()`` can be huge. Attempts -- to render such integers into decimal strings may bump into the -- :ref:`integer string conversion length limitation -- `. -- - .. method:: float.is_integer() - - Return ``True`` if the float instance is finite with integral --- -1.8.3.1 - diff --git a/backport-0003-CVE-2020-10735.patch b/backport-0003-CVE-2020-10735.patch deleted file mode 100644 index 1e0d788557c8b94fbba54e88ecf7e2dc9e0ded6a..0000000000000000000000000000000000000000 --- a/backport-0003-CVE-2020-10735.patch +++ /dev/null @@ -1,226 +0,0 @@ -From eace09e63ed7978dbdfeb1ae537fac505e6b5b0e Mon Sep 17 00:00:00 2001 -From: "Gregory P. Smith" -Date: Sun, 4 Sep 2022 09:54:56 -0700 -Subject: [PATCH] [3.10] gh-95778: Correctly pre-check for int-to-str - conversion (GH-96537) (#96563) - -Converting a large enough `int` to a decimal string raises `ValueError` as expected. However, the raise comes _after_ the quadratic-time base-conversion algorithm has run to completion. For effective DOS prevention, we need some kind of check before entering the quadratic-time loop. Oops! =) - -The quick fix: essentially we catch _most_ values that exceed the threshold up front. Those that slip through will still be on the small side (read: sufficiently fast), and will get caught by the existing check so that the limit remains exact. - -The justification for the current check. The C code check is: -```c -max_str_digits / (3 * PyLong_SHIFT) <= (size_a - 11) / 10 -``` - -In GitHub markdown math-speak, writing $M$ for `max_str_digits`, $L$ for `PyLong_SHIFT` and $s$ for `size_a`, that check is: -$$\left\lfloor\frac{M}{3L}\right\rfloor \le \left\lfloor\frac{s - 11}{10}\right\rfloor$$ - -From this it follows that -$$\frac{M}{3L} < \frac{s-1}{10}$$ -hence that -$$\frac{L(s-1)}{M} > \frac{10}{3} > \log_2(10).$$ -So -$$2^{L(s-1)} > 10^M.$$ -But our input integer $a$ satisfies $|a| \ge 2^{L(s-1)}$, so $|a|$ is larger than $10^M$. This shows that we don't accidentally capture anything _below_ the intended limit in the check. - - -* Issue: gh-95778 - - -Co-authored-by: Gregory P. Smith [Google LLC] -(cherry picked from commit b126196838bbaf5f4d35120e0e6bcde435b0b480) - -Co-authored-by: Mark Dickinson ---- - Include/internal/pycore_long.h | 4 +- - Lib/test/test_int.py | 82 ++++++++++++++++++++++ - ...2022-08-07-16-53-38.gh-issue-95778.ch010gps.rst | 2 +- - Objects/longobject.c | 26 +++++-- - 4 files changed, 107 insertions(+), 7 deletions(-) - -diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h -index ee9a592..90069f8 100644 ---- a/Include/internal/pycore_long.h -+++ b/Include/internal/pycore_long.h -@@ -18,9 +18,9 @@ - * everyone's existing deployed numpy test suite passes before - * https://github.com/numpy/numpy/issues/22098 is widely available. - * -- * $ python -m timeit -s 's = * "1"*4300' 'int(s)' -+ * $ python -m timeit -s 's = "1"*4300' 'int(s)' - * 2000 loops, best of 5: 125 usec per loop -- * $ python -m timeit -s 's = * "1"*4300; v = int(s)' 'str(v)' -+ * $ python -m timeit -s 's = "1"*4300; v = int(s)' 'str(v)' - * 1000 loops, best of 5: 311 usec per loop - * (zen2 cloud VM) - * -diff --git a/Lib/test/test_int.py b/Lib/test/test_int.py -index 2f678ed..a578ca8 100644 ---- a/Lib/test/test_int.py -+++ b/Lib/test/test_int.py -@@ -1,4 +1,5 @@ - import sys -+import time - - import unittest - from test import support -@@ -623,6 +624,87 @@ def test_max_str_digits(self): - with self.assertRaises(ValueError): - str(i) - -+ def test_denial_of_service_prevented_int_to_str(self): -+ """Regression test: ensure we fail before performing O(N**2) work.""" -+ maxdigits = sys.get_int_max_str_digits() -+ assert maxdigits < 50_000, maxdigits # A test prerequisite. -+ get_time = time.process_time -+ if get_time() <= 0: # some platforms like WASM lack process_time() -+ get_time = time.monotonic -+ -+ huge_int = int(f'0x{"c"*65_000}', base=16) # 78268 decimal digits. -+ digits = 78_268 -+ with support.adjust_int_max_str_digits(digits): -+ start = get_time() -+ huge_decimal = str(huge_int) -+ seconds_to_convert = get_time() - start -+ self.assertEqual(len(huge_decimal), digits) -+ # Ensuring that we chose a slow enough conversion to measure. -+ # It takes 0.1 seconds on a Zen based cloud VM in an opt build. -+ if seconds_to_convert < 0.005: -+ raise unittest.SkipTest('"slow" conversion took only ' -+ f'{seconds_to_convert} seconds.') -+ -+ # We test with the limit almost at the size needed to check performance. -+ # The performant limit check is slightly fuzzy, give it a some room. -+ with support.adjust_int_max_str_digits(int(.995 * digits)): -+ with self.assertRaises(ValueError) as err: -+ start = get_time() -+ str(huge_int) -+ seconds_to_fail_huge = get_time() - start -+ self.assertIn('conversion', str(err.exception)) -+ self.assertLess(seconds_to_fail_huge, seconds_to_convert/8) -+ -+ # Now we test that a conversion that would take 30x as long also fails -+ # in a similarly fast fashion. -+ extra_huge_int = int(f'0x{"c"*500_000}', base=16) # 602060 digits. -+ with self.assertRaises(ValueError) as err: -+ start = get_time() -+ # If not limited, 8 seconds said Zen based cloud VM. -+ str(extra_huge_int) -+ seconds_to_fail_extra_huge = get_time() - start -+ self.assertIn('conversion', str(err.exception)) -+ self.assertLess(seconds_to_fail_extra_huge, seconds_to_convert/8) -+ -+ def test_denial_of_service_prevented_str_to_int(self): -+ """Regression test: ensure we fail before performing O(N**2) work.""" -+ maxdigits = sys.get_int_max_str_digits() -+ assert maxdigits < 100_000, maxdigits # A test prerequisite. -+ get_time = time.process_time -+ if get_time() <= 0: # some platforms like WASM lack process_time() -+ get_time = time.monotonic -+ -+ digits = 133700 -+ huge = '8'*digits -+ with support.adjust_int_max_str_digits(digits): -+ start = get_time() -+ int(huge) -+ seconds_to_convert = get_time() - start -+ # Ensuring that we chose a slow enough conversion to measure. -+ # It takes 0.1 seconds on a Zen based cloud VM in an opt build. -+ if seconds_to_convert < 0.005: -+ raise unittest.SkipTest('"slow" conversion took only ' -+ f'{seconds_to_convert} seconds.') -+ -+ with support.adjust_int_max_str_digits(digits - 1): -+ with self.assertRaises(ValueError) as err: -+ start = get_time() -+ int(huge) -+ seconds_to_fail_huge = get_time() - start -+ self.assertIn('conversion', str(err.exception)) -+ self.assertLess(seconds_to_fail_huge, seconds_to_convert/8) -+ -+ # Now we test that a conversion that would take 30x as long also fails -+ # in a similarly fast fashion. -+ extra_huge = '7'*1_200_000 -+ with self.assertRaises(ValueError) as err: -+ start = get_time() -+ # If not limited, 8 seconds in the Zen based cloud VM. -+ int(extra_huge) -+ seconds_to_fail_extra_huge = get_time() - start -+ self.assertIn('conversion', str(err.exception)) -+ self.assertLess(seconds_to_fail_extra_huge, seconds_to_convert/8) -+ - def test_power_of_two_bases_unlimited(self): - """The limit does not apply to power of 2 bases.""" - maxdigits = sys.get_int_max_str_digits() -diff --git a/Misc/NEWS.d/next/Security/2022-08-07-16-53-38.gh-issue-95778.ch010gps.rst b/Misc/NEWS.d/next/Security/2022-08-07-16-53-38.gh-issue-95778.ch010gps.rst -index ea3b85d..8eb8a34 100644 ---- a/Misc/NEWS.d/next/Security/2022-08-07-16-53-38.gh-issue-95778.ch010gps.rst -+++ b/Misc/NEWS.d/next/Security/2022-08-07-16-53-38.gh-issue-95778.ch010gps.rst -@@ -11,4 +11,4 @@ limitation ` documentation. The default limit is 4300 - digits in string form. - - Patch by Gregory P. Smith [Google] and Christian Heimes [Red Hat] with feedback --from Victor Stinner, Thomas Wouters, Steve Dower, and Ned Deily. -+from Victor Stinner, Thomas Wouters, Steve Dower, Ned Deily, and Mark Dickinson. -diff --git a/Objects/longobject.c b/Objects/longobject.c -index 780ea81..aea5edc 100644 ---- a/Objects/longobject.c -+++ b/Objects/longobject.c -@@ -36,7 +36,8 @@ class int "PyObject *" "&PyLong_Type" - #define IS_SMALL_INT(ival) (-NSMALLNEGINTS <= (ival) && (ival) < NSMALLPOSINTS) - #define IS_SMALL_UINT(ival) ((ival) < NSMALLPOSINTS) - --#define _MAX_STR_DIGITS_ERROR_FMT "Exceeds the limit (%d) for integer string conversion: value has %zd digits" -+#define _MAX_STR_DIGITS_ERROR_FMT_TO_INT "Exceeds the limit (%d) for integer string conversion: value has %zd digits" -+#define _MAX_STR_DIGITS_ERROR_FMT_TO_STR "Exceeds the limit (%d) for integer string conversion" - - static PyObject * - get_small_int(sdigit ival) -@@ -1604,6 +1605,23 @@ class int "PyObject *" "&PyLong_Type" - size_a = Py_ABS(Py_SIZE(a)); - negative = Py_SIZE(a) < 0; - -+ /* quick and dirty pre-check for overflowing the decimal digit limit, -+ based on the inequality 10/3 >= log2(10) -+ -+ explanation in https://github.com/python/cpython/pull/96537 -+ */ -+ if (size_a >= 10 * _PY_LONG_MAX_STR_DIGITS_THRESHOLD -+ / (3 * PyLong_SHIFT) + 2) { -+ PyInterpreterState *interp = _PyInterpreterState_GET(); -+ int max_str_digits = interp->int_max_str_digits; -+ if ((max_str_digits > 0) && -+ (max_str_digits / (3 * PyLong_SHIFT) <= (size_a - 11) / 10)) { -+ PyErr_Format(PyExc_ValueError, _MAX_STR_DIGITS_ERROR_FMT_TO_STR, -+ max_str_digits); -+ return -1; -+ } -+ } -+ - /* quick and dirty upper bound for the number of digits - required to express a in base _PyLong_DECIMAL_BASE: - -@@ -1669,8 +1687,8 @@ class int "PyObject *" "&PyLong_Type" - Py_ssize_t strlen_nosign = strlen - negative; - if ((max_str_digits > 0) && (strlen_nosign > max_str_digits)) { - Py_DECREF(scratch); -- PyErr_Format(PyExc_ValueError, _MAX_STR_DIGITS_ERROR_FMT, -- max_str_digits, strlen_nosign); -+ PyErr_Format(PyExc_ValueError, _MAX_STR_DIGITS_ERROR_FMT_TO_STR, -+ max_str_digits); - return -1; - } - } -@@ -2344,7 +2362,7 @@ that triggers it(!). Instead the code was tested by artificially allocating - PyInterpreterState *interp = _PyInterpreterState_GET(); - int max_str_digits = interp->int_max_str_digits; - if ((max_str_digits > 0) && (digits > max_str_digits)) { -- PyErr_Format(PyExc_ValueError, _MAX_STR_DIGITS_ERROR_FMT, -+ PyErr_Format(PyExc_ValueError, _MAX_STR_DIGITS_ERROR_FMT_TO_INT, - max_str_digits, digits); - return NULL; - } --- -1.8.3.1 - diff --git a/backport-CVE-2015-20107.patch b/backport-CVE-2015-20107.patch deleted file mode 100644 index 56235c6ff2ac0936ff474e331386e0814c7ab8e2..0000000000000000000000000000000000000000 --- a/backport-CVE-2015-20107.patch +++ /dev/null @@ -1,149 +0,0 @@ -From b9509ba7a9c668b984dab876c7926fe1dc5aa0ba Mon Sep 17 00:00:00 2001 -From: Petr Viktorin -Date: Fri, 3 Jun 2022 11:43:35 +0200 -Subject: [PATCH] gh-68966: Make mailcap refuse to match unsafe - filenames/types/params (GH-91993) - ---- - Doc/library/mailcap.rst | 12 +++++++++ - Lib/mailcap.py | 26 +++++++++++++++++-- - Lib/test/test_mailcap.py | 8 ++++-- - ...2-04-27-18-25-30.gh-issue-68966.gjS8zs.rst | 4 +++ - 4 files changed, 46 insertions(+), 4 deletions(-) - create mode 100644 Misc/NEWS.d/next/Security/2022-04-27-18-25-30.gh-issue-68966.gjS8zs.rst - -diff --git a/Doc/library/mailcap.rst b/Doc/library/mailcap.rst -index 5490c8468d..bfaedb4609 100644 ---- a/Doc/library/mailcap.rst -+++ b/Doc/library/mailcap.rst -@@ -60,6 +60,18 @@ standard. However, mailcap files are supported on most Unix systems. - use) to determine whether or not the mailcap line applies. :func:`findmatch` - will automatically check such conditions and skip the entry if the check fails. - -+ .. versionchanged:: 3.11 -+ -+ To prevent security issues with shell metacharacters (symbols that have -+ special effects in a shell command line), ``findmatch`` will refuse -+ to inject ASCII characters other than alphanumerics and ``@+=:,./-_`` -+ into the returned command line. -+ -+ If a disallowed character appears in *filename*, ``findmatch`` will always -+ return ``(None, None)`` as if no entry was found. -+ If such a character appears elsewhere (a value in *plist* or in *MIMEtype*), -+ ``findmatch`` will ignore all mailcap entries which use that value. -+ A :mod:`warning ` will be raised in either case. - - .. function:: getcaps() - -diff --git a/Lib/mailcap.py b/Lib/mailcap.py -index 856b6a5547..7278ea7051 100644 ---- a/Lib/mailcap.py -+++ b/Lib/mailcap.py -@@ -2,6 +2,7 @@ - - import os - import warnings -+import re - - __all__ = ["getcaps","findmatch"] - -@@ -19,6 +20,11 @@ def lineno_sort_key(entry): - else: - return 1, 0 - -+_find_unsafe = re.compile(r'[^\xa1-\U0010FFFF\w@+=:,./-]').search -+ -+class UnsafeMailcapInput(Warning): -+ """Warning raised when refusing unsafe input""" -+ - - # Part 1: top-level interface. - -@@ -171,15 +177,22 @@ def findmatch(caps, MIMEtype, key='view', filename="/dev/null", plist=[]): - entry to use. - - """ -+ if _find_unsafe(filename): -+ msg = "Refusing to use mailcap with filename %r. Use a safe temporary filename." % (filename,) -+ warnings.warn(msg, UnsafeMailcapInput) -+ return None, None - entries = lookup(caps, MIMEtype, key) - # XXX This code should somehow check for the needsterminal flag. - for e in entries: - if 'test' in e: - test = subst(e['test'], filename, plist) -+ if test is None: -+ continue - if test and os.system(test) != 0: - continue - command = subst(e[key], MIMEtype, filename, plist) -- return command, e -+ if command is not None: -+ return command, e - return None, None - - def lookup(caps, MIMEtype, key=None): -@@ -212,6 +225,10 @@ def subst(field, MIMEtype, filename, plist=[]): - elif c == 's': - res = res + filename - elif c == 't': -+ if _find_unsafe(MIMEtype): -+ msg = "Refusing to substitute MIME type %r into a shell command." % (MIMEtype,) -+ warnings.warn(msg, UnsafeMailcapInput) -+ return None - res = res + MIMEtype - elif c == '{': - start = i -@@ -219,7 +236,12 @@ def subst(field, MIMEtype, filename, plist=[]): - i = i+1 - name = field[start:i] - i = i+1 -- res = res + findparam(name, plist) -+ param = findparam(name, plist) -+ if _find_unsafe(param): -+ msg = "Refusing to substitute parameter %r (%s) into a shell command" % (param, name) -+ warnings.warn(msg, UnsafeMailcapInput) -+ return None -+ res = res + param - # XXX To do: - # %n == number of parts if type is multipart/* - # %F == list of alternating type and filename for parts -diff --git a/Lib/test/test_mailcap.py b/Lib/test/test_mailcap.py -index d3995b1472..8185f4a780 100644 ---- a/Lib/test/test_mailcap.py -+++ b/Lib/test/test_mailcap.py -@@ -128,7 +128,8 @@ def test_subst(self): - (["", "audio/*", "foo.txt"], ""), - (["echo foo", "audio/*", "foo.txt"], "echo foo"), - (["echo %s", "audio/*", "foo.txt"], "echo foo.txt"), -- (["echo %t", "audio/*", "foo.txt"], "echo audio/*"), -+ (["echo %t", "audio/*", "foo.txt"], None), -+ (["echo %t", "audio/wav", "foo.txt"], "echo audio/wav"), - (["echo \\%t", "audio/*", "foo.txt"], "echo %t"), - (["echo foo", "audio/*", "foo.txt", plist], "echo foo"), - (["echo %{total}", "audio/*", "foo.txt", plist], "echo 3") -@@ -212,7 +213,10 @@ def test_findmatch(self): - ('"An audio fragment"', audio_basic_entry)), - ([c, "audio/*"], - {"filename": fname}, -- ("/usr/local/bin/showaudio audio/*", audio_entry)), -+ (None, None)), -+ ([c, "audio/wav"], -+ {"filename": fname}, -+ ("/usr/local/bin/showaudio audio/wav", audio_entry)), - ([c, "message/external-body"], - {"plist": plist}, - ("showexternal /dev/null default john python.org /tmp foo bar", message_entry)) -diff --git a/Misc/NEWS.d/next/Security/2022-04-27-18-25-30.gh-issue-68966.gjS8zs.rst b/Misc/NEWS.d/next/Security/2022-04-27-18-25-30.gh-issue-68966.gjS8zs.rst -new file mode 100644 -index 0000000000..da81a1f699 ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2022-04-27-18-25-30.gh-issue-68966.gjS8zs.rst -@@ -0,0 +1,4 @@ -+The deprecated mailcap module now refuses to inject unsafe text (filenames, -+MIME types, parameters) into shell commands. Instead of using such text, it -+will warn and act as if a match was not found (or for test commands, as if -+the test failed). --- -2.23.0 - diff --git a/backport-CVE-2021-28861.patch b/backport-CVE-2021-28861.patch deleted file mode 100644 index c683f28e33b4750263b074c71895fa3b81fb375d..0000000000000000000000000000000000000000 --- a/backport-CVE-2021-28861.patch +++ /dev/null @@ -1,131 +0,0 @@ -From 5715382d3a89ca118ce2e224d8c69550d21fe51b Mon Sep 17 00:00:00 2001 -From: "Miss Islington (bot)" - <31488909+miss-islington@users.noreply.github.com> -Date: Tue, 21 Jun 2022 14:36:55 -0700 -Subject: [PATCH] gh-87389: Fix an open redirection vulnerability in - http.server. (GH-93879) - -Fix an open redirection vulnerability in the `http.server` module when -an URI path starts with `//` that could produce a 301 Location header -with a misleading target. Vulnerability discovered, and logic fix -proposed, by Hamza Avvan (@hamzaavvan). - -Test and comments authored by Gregory P. Smith [Google]. -(cherry picked from commit 4abab6b603dd38bec1168e9a37c40a48ec89508e) - -Co-authored-by: Gregory P. Smith ---- - Lib/http/server.py | 7 +++ - Lib/test/test_httpservers.py | 53 +++++++++++++++++++++- - .../2022-06-15-20-09-23.gh-issue-87389.QVaC3f.rst | 3 ++ - 3 files changed, 61 insertions(+), 2 deletions(-) - create mode 100644 Misc/NEWS.d/next/Security/2022-06-15-20-09-23.gh-issue-87389.QVaC3f.rst - -diff --git a/Lib/http/server.py b/Lib/http/server.py -index e985dfd..78748c6 100644 ---- a/Lib/http/server.py -+++ b/Lib/http/server.py -@@ -332,6 +332,13 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): - return False - self.command, self.path = command, path - -+ # gh-87389: The purpose of replacing '//' with '/' is to protect -+ # against open redirect attacks possibly triggered if the path starts -+ # with '//' because http clients treat //path as an absolute URI -+ # without scheme (similar to http://path) rather than a path. -+ if self.path.startswith('//'): -+ self.path = '/' + self.path.lstrip('/') # Reduce to a single / -+ - # Examine the headers and look for a Connection directive. - try: - self.headers = http.client.parse_headers(self.rfile, -diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py -index 1cc020f..8fdbab4 100644 ---- a/Lib/test/test_httpservers.py -+++ b/Lib/test/test_httpservers.py -@@ -333,7 +333,7 @@ class SimpleHTTPServerTestCase(BaseTestCase): - pass - - def setUp(self): -- BaseTestCase.setUp(self) -+ super().setUp() - self.cwd = os.getcwd() - basetempdir = tempfile.gettempdir() - os.chdir(basetempdir) -@@ -361,7 +361,7 @@ class SimpleHTTPServerTestCase(BaseTestCase): - except: - pass - finally: -- BaseTestCase.tearDown(self) -+ super().tearDown() - - def check_status_and_reason(self, response, status, data=None): - def close_conn(): -@@ -417,6 +417,55 @@ class SimpleHTTPServerTestCase(BaseTestCase): - self.check_status_and_reason(response, HTTPStatus.OK, - data=os_helper.TESTFN_UNDECODABLE) - -+ def test_get_dir_redirect_location_domain_injection_bug(self): -+ """Ensure //evil.co/..%2f../../X does not put //evil.co/ in Location. -+ -+ //netloc/ in a Location header is a redirect to a new host. -+ https://github.com/python/cpython/issues/87389 -+ -+ This checks that a path resolving to a directory on our server cannot -+ resolve into a redirect to another server. -+ """ -+ os.mkdir(os.path.join(self.tempdir, 'existing_directory')) -+ url = f'/python.org/..%2f..%2f..%2f..%2f..%2f../%0a%0d/../{self.tempdir_name}/existing_directory' -+ expected_location = f'{url}/' # /python.org.../ single slash single prefix, trailing slash -+ # Canonicalizes to /tmp/tempdir_name/existing_directory which does -+ # exist and is a dir, triggering the 301 redirect logic. -+ response = self.request(url) -+ self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY) -+ location = response.getheader('Location') -+ self.assertEqual(location, expected_location, msg='non-attack failed!') -+ -+ # //python.org... multi-slash prefix, no trailing slash -+ attack_url = f'/{url}' -+ response = self.request(attack_url) -+ self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY) -+ location = response.getheader('Location') -+ self.assertFalse(location.startswith('//'), msg=location) -+ self.assertEqual(location, expected_location, -+ msg='Expected Location header to start with a single / and ' -+ 'end with a / as this is a directory redirect.') -+ -+ # ///python.org... triple-slash prefix, no trailing slash -+ attack3_url = f'//{url}' -+ response = self.request(attack3_url) -+ self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY) -+ self.assertEqual(response.getheader('Location'), expected_location) -+ -+ # If the second word in the http request (Request-URI for the http -+ # method) is a full URI, we don't worry about it, as that'll be parsed -+ # and reassembled as a full URI within BaseHTTPRequestHandler.send_head -+ # so no errant scheme-less //netloc//evil.co/ domain mixup can happen. -+ attack_scheme_netloc_2slash_url = f'https://pypi.org/{url}' -+ expected_scheme_netloc_location = f'{attack_scheme_netloc_2slash_url}/' -+ response = self.request(attack_scheme_netloc_2slash_url) -+ self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY) -+ location = response.getheader('Location') -+ # We're just ensuring that the scheme and domain make it through, if -+ # there are or aren't multiple slashes at the start of the path that -+ # follows that isn't important in this Location: header. -+ self.assertTrue(location.startswith('https://pypi.org/'), msg=location) -+ - def test_get(self): - #constructs the path relative to the root directory of the HTTPServer - response = self.request(self.base_url + '/test') -diff --git a/Misc/NEWS.d/next/Security/2022-06-15-20-09-23.gh-issue-87389.QVaC3f.rst b/Misc/NEWS.d/next/Security/2022-06-15-20-09-23.gh-issue-87389.QVaC3f.rst -new file mode 100644 -index 0000000..029d437 ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2022-06-15-20-09-23.gh-issue-87389.QVaC3f.rst -@@ -0,0 +1,3 @@ -+:mod:`http.server`: Fix an open redirection vulnerability in the HTTP server -+when an URI path starts with ``//``. Vulnerability discovered, and initial -+fix proposed, by Hamza Avvan. --- -1.8.3.1 - diff --git a/backport-CVE-2022-37454.patch b/backport-CVE-2022-37454.patch deleted file mode 100644 index c0cc6c674b5bd1b838cd1109a4abd673f55a77c6..0000000000000000000000000000000000000000 --- a/backport-CVE-2022-37454.patch +++ /dev/null @@ -1,103 +0,0 @@ -From 0e4e058602d93b88256ff90bbef501ba20be9dd3 Mon Sep 17 00:00:00 2001 -From: Theo Buehler -Date: Fri, 21 Oct 2022 21:26:01 +0200 -Subject: [PATCH] [3.10] gh-98517: Fix buffer overflows in _sha3 module -(#98519) - -This is a port of the applicable part of XKCP's fix [1] for -CVE-2022-37454 and avoids the segmentation fault and the infinite -loop in the test cases published in [2]. - -[1]: -https://github.com/XKCP/XKCP/commit/fdc6fef075f4e81d6b1bc38364248975e08e340a -[2]: https://mouha.be/sha-3-buffer-overflow/ - -Regression test added by: Gregory P. Smith [Google LLC] - - ---- - Lib/test/test_hashlib.py | 9 +++++++++ - .../2022-10-21-13-31-47.gh-issue-98517.SXXGfV.rst | 1 + - Modules/_sha3/kcp/KeccakSponge.inc | 15 ++++++++------- - 3 files changed, 18 insertions(+), 7 deletions(-) - create mode 100644 Misc/NEWS.d/next/Security/2022-10-21-13-31-47.gh-issue-98517.SXXGfV.rst - -diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py -index 110eb48..378127a 100644 ---- a/Lib/test/test_hashlib.py -+++ b/Lib/test/test_hashlib.py -@@ -481,6 +481,15 @@ class HashLibTestCase(unittest.TestCase): - def test_case_md5_uintmax(self, size): - self.check('md5', b'A'*size, '28138d306ff1b8281f1a9067e1a1a2b3') - -+ @unittest.skipIf(sys.maxsize < _4G - 1, 'test cannot run on 32-bit systems') -+ @bigmemtest(size=_4G - 1, memuse=1, dry_run=False) -+ def test_sha3_update_overflow(self, size): -+ """Regression test for gh-98517 CVE-2022-37454.""" -+ h = hashlib.sha3_224() -+ h.update(b'\x01') -+ h.update(b'\x01'*0xffff_ffff) -+ self.assertEqual(h.hexdigest(), '80762e8ce6700f114fec0f621fd97c4b9c00147fa052215294cceeed') -+ - # use the three examples from Federal Information Processing Standards - # Publication 180-1, Secure Hash Standard, 1995 April 17 - # http://www.itl.nist.gov/div897/pubs/fip180-1.htm -diff --git a/Misc/NEWS.d/next/Security/2022-10-21-13-31-47.gh-issue-98517.SXXGfV.rst b/Misc/NEWS.d/next/Security/2022-10-21-13-31-47.gh-issue-98517.SXXGfV.rst -new file mode 100644 -index 0000000..2d23a6a ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2022-10-21-13-31-47.gh-issue-98517.SXXGfV.rst -@@ -0,0 +1 @@ -+Port XKCP's fix for the buffer overflows in SHA-3 (CVE-2022-37454). -diff --git a/Modules/_sha3/kcp/KeccakSponge.inc b/Modules/_sha3/kcp/KeccakSponge.inc -index e10739d..cf92e4d 100644 ---- a/Modules/_sha3/kcp/KeccakSponge.inc -+++ b/Modules/_sha3/kcp/KeccakSponge.inc -@@ -171,7 +171,7 @@ int SpongeAbsorb(SpongeInstance *instance, const unsigned char *data, size_t dat - i = 0; - curData = data; - while(i < dataByteLen) { -- if ((instance->byteIOIndex == 0) && (dataByteLen >= (i + rateInBytes))) { -+ if ((instance->byteIOIndex == 0) && (dataByteLen-i >= rateInBytes)) { - #ifdef SnP_FastLoop_Absorb - /* processing full blocks first */ - -@@ -199,10 +199,10 @@ int SpongeAbsorb(SpongeInstance *instance, const unsigned char *data, size_t dat - } - else { - /* normal lane: using the message queue */ -- -- partialBlock = (unsigned int)(dataByteLen - i); -- if (partialBlock+instance->byteIOIndex > rateInBytes) -+ if (dataByteLen-i > rateInBytes-instance->byteIOIndex) - partialBlock = rateInBytes-instance->byteIOIndex; -+ else -+ partialBlock = (unsigned int)(dataByteLen - i); - #ifdef KeccakReference - displayBytes(1, "Block to be absorbed (part)", curData, partialBlock); - #endif -@@ -281,7 +281,7 @@ int SpongeSqueeze(SpongeInstance *instance, unsigned char *data, size_t dataByte - i = 0; - curData = data; - while(i < dataByteLen) { -- if ((instance->byteIOIndex == rateInBytes) && (dataByteLen >= (i + rateInBytes))) { -+ if ((instance->byteIOIndex == rateInBytes) && (dataByteLen-i >= rateInBytes)) { - for(j=dataByteLen-i; j>=rateInBytes; j-=rateInBytes) { - SnP_Permute(instance->state); - SnP_ExtractBytes(instance->state, curData, 0, rateInBytes); -@@ -299,9 +299,10 @@ int SpongeSqueeze(SpongeInstance *instance, unsigned char *data, size_t dataByte - SnP_Permute(instance->state); - instance->byteIOIndex = 0; - } -- partialBlock = (unsigned int)(dataByteLen - i); -- if (partialBlock+instance->byteIOIndex > rateInBytes) -+ if (dataByteLen-i > rateInBytes-instance->byteIOIndex) - partialBlock = rateInBytes-instance->byteIOIndex; -+ else -+ partialBlock = (unsigned int)(dataByteLen - i); - i += partialBlock; - - SnP_ExtractBytes(instance->state, curData, instance->byteIOIndex, partialBlock); --- -2.27.0 - diff --git a/backport-CVE-2022-42919.patch b/backport-CVE-2022-42919.patch deleted file mode 100644 index ee7ec4c33995066b86938cb98455df7da98d53eb..0000000000000000000000000000000000000000 --- a/backport-CVE-2022-42919.patch +++ /dev/null @@ -1,71 +0,0 @@ -From eae692eed18892309bcc25a2c0f8980038305ea2 Mon Sep 17 00:00:00 2001 -From: "Miss Islington (bot)" - <31488909+miss-islington@users.noreply.github.com> -Date: Thu, 20 Oct 2022 16:55:51 -0700 -Subject: [PATCH] [3.10] gh-97514: Don't use Linux abstract sockets for - multiprocessing (GH-98501) (GH-98503) - -Linux abstract sockets are insecure as they lack any form of filesystem -permissions so their use allows anyone on the system to inject code into -the process. - -This removes the default preference for abstract sockets in -multiprocessing introduced in Python 3.9+ via -https://github.com/python/cpython/pull/18866 while fixing -https://github.com/python/cpython/issues/84031. - -Explicit use of an abstract socket by a user now generates a -RuntimeWarning. If we choose to keep this warning, it should be -backported to the 3.7 and 3.8 branches. -(cherry picked from commit 49f61068f49747164988ffc5a442d2a63874fc17) - - -Co-authored-by: Gregory P. Smith - -Automerge-Triggered-By: GH:gpshead ---- - Lib/multiprocessing/connection.py | 5 ----- - .../2022-09-07-10-42-00.gh-issue-97514.Yggdsl.rst | 15 +++++++++++++++ - 2 files changed, 15 insertions(+), 5 deletions(-) - create mode 100644 Misc/NEWS.d/next/Security/2022-09-07-10-42-00.gh-issue-97514.Yggdsl.rst - -diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py -index 510e4b5aba..8e2facf92a 100644 ---- a/Lib/multiprocessing/connection.py -+++ b/Lib/multiprocessing/connection.py -@@ -73,11 +73,6 @@ def arbitrary_address(family): - if family == 'AF_INET': - return ('localhost', 0) - elif family == 'AF_UNIX': -- # Prefer abstract sockets if possible to avoid problems with the address -- # size. When coding portable applications, some implementations have -- # sun_path as short as 92 bytes in the sockaddr_un struct. -- if util.abstract_sockets_supported: -- return f"\0listener-{os.getpid()}-{next(_mmap_counter)}" - return tempfile.mktemp(prefix='listener-', dir=util.get_temp_dir()) - elif family == 'AF_PIPE': - return tempfile.mktemp(prefix=r'\\.\pipe\pyc-%d-%d-' % -diff --git a/Misc/NEWS.d/next/Security/2022-09-07-10-42-00.gh-issue-97514.Yggdsl.rst b/Misc/NEWS.d/next/Security/2022-09-07-10-42-00.gh-issue-97514.Yggdsl.rst -new file mode 100644 -index 0000000000..02d95b5705 ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2022-09-07-10-42-00.gh-issue-97514.Yggdsl.rst -@@ -0,0 +1,15 @@ -+On Linux the :mod:`multiprocessing` module returns to using filesystem backed -+unix domain sockets for communication with the *forkserver* process instead of -+the Linux abstract socket namespace. Only code that chooses to use the -+:ref:`"forkserver" start method ` is affected. -+ -+Abstract sockets have no permissions and could allow any user on the system in -+the same `network namespace -+`_ (often the -+whole system) to inject code into the multiprocessing *forkserver* process. -+This was a potential privilege escalation. Filesystem based socket permissions -+restrict this to the *forkserver* process user as was the default in Python 3.8 -+and earlier. -+ -+This prevents Linux `CVE-2022-42919 -+`_. --- -2.27.0 - diff --git a/backport-CVE-2022-45061.patch b/backport-CVE-2022-45061.patch deleted file mode 100644 index 7bfc0b82af53aad1dd770d631ea4ab3b9a4e423e..0000000000000000000000000000000000000000 --- a/backport-CVE-2022-45061.patch +++ /dev/null @@ -1,98 +0,0 @@ -From 9bb8e18ca46fe66fa6802602f8a7228a24dd785f Mon Sep 17 00:00:00 2001 -From: "Miss Islington (bot)" - <31488909+miss-islington@users.noreply.github.com> -Date: Mon, 7 Nov 2022 19:23:16 -0800 -Subject: [PATCH] [3.11] gh-98433: Fix quadratic time idna decoding. (GH-99092) - (GH-99222) - -There was an unnecessary quadratic loop in idna decoding. This restores -the behavior to linear. - -(cherry picked from commit d315722564927c7202dd6e111dc79eaf14240b0d) - -(cherry picked from commit a6f6c3a3d6f2b580f2d87885c9b8a9350ad7bf15) - -Co-authored-by: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> -Co-authored-by: Gregory P. Smith ---- - Lib/encodings/idna.py | 32 +++++++++---------- - Lib/test/test_codecs.py | 6 ++++ - ...2-11-04-09-29-36.gh-issue-98433.l76c5G.rst | 6 ++++ - 3 files changed, 27 insertions(+), 17 deletions(-) - create mode 100644 Misc/NEWS.d/next/Security/2022-11-04-09-29-36.gh-issue-98433.l76c5G.rst - -diff --git a/Lib/encodings/idna.py b/Lib/encodings/idna.py -index ea40585..bf98f51 100644 ---- a/Lib/encodings/idna.py -+++ b/Lib/encodings/idna.py -@@ -39,23 +39,21 @@ def nameprep(label): - - # Check bidi - RandAL = [stringprep.in_table_d1(x) for x in label] -- for c in RandAL: -- if c: -- # There is a RandAL char in the string. Must perform further -- # tests: -- # 1) The characters in section 5.8 MUST be prohibited. -- # This is table C.8, which was already checked -- # 2) If a string contains any RandALCat character, the string -- # MUST NOT contain any LCat character. -- if any(stringprep.in_table_d2(x) for x in label): -- raise UnicodeError("Violation of BIDI requirement 2") -- -- # 3) If a string contains any RandALCat character, a -- # RandALCat character MUST be the first character of the -- # string, and a RandALCat character MUST be the last -- # character of the string. -- if not RandAL[0] or not RandAL[-1]: -- raise UnicodeError("Violation of BIDI requirement 3") -+ if any(RandAL): -+ # There is a RandAL char in the string. Must perform further -+ # tests: -+ # 1) The characters in section 5.8 MUST be prohibited. -+ # This is table C.8, which was already checked -+ # 2) If a string contains any RandALCat character, the string -+ # MUST NOT contain any LCat character. -+ if any(stringprep.in_table_d2(x) for x in label): -+ raise UnicodeError("Violation of BIDI requirement 2") -+ # 3) If a string contains any RandALCat character, a -+ # RandALCat character MUST be the first character of the -+ # string, and a RandALCat character MUST be the last -+ # character of the string. -+ if not RandAL[0] or not RandAL[-1]: -+ raise UnicodeError("Violation of BIDI requirement 3") - - return label - -diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py -index f7310fb..439fab7 100644 ---- a/Lib/test/test_codecs.py -+++ b/Lib/test/test_codecs.py -@@ -1534,6 +1534,12 @@ class IDNACodecTest(unittest.TestCase): - self.assertEqual("pyth\xf6n.org".encode("idna"), b"xn--pythn-mua.org") - self.assertEqual("pyth\xf6n.org.".encode("idna"), b"xn--pythn-mua.org.") - -+ def test_builtin_decode_length_limit(self): -+ with self.assertRaisesRegex(UnicodeError, "too long"): -+ (b"xn--016c"+b"a"*1100).decode("idna") -+ with self.assertRaisesRegex(UnicodeError, "too long"): -+ (b"xn--016c"+b"a"*70).decode("idna") -+ - def test_stream(self): - r = codecs.getreader("idna")(io.BytesIO(b"abc")) - r.read(3) -diff --git a/Misc/NEWS.d/next/Security/2022-11-04-09-29-36.gh-issue-98433.l76c5G.rst b/Misc/NEWS.d/next/Security/2022-11-04-09-29-36.gh-issue-98433.l76c5G.rst -new file mode 100644 -index 0000000..5185fac ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2022-11-04-09-29-36.gh-issue-98433.l76c5G.rst -@@ -0,0 +1,6 @@ -+The IDNA codec decoder used on DNS hostnames by :mod:`socket` or :mod:`asyncio` -+related name resolution functions no longer involves a quadratic algorithm. -+This prevents a potential CPU denial of service if an out-of-spec excessive -+length hostname involving bidirectional characters were decoded. Some protocols -+such as :mod:`urllib` http ``3xx`` redirects potentially allow for an attacker -+to supply such a name. --- -2.27.0 - diff --git a/backport-bpo-46811-Make-test-suite-support-Expat-2.4.5.patch b/backport-bpo-46811-Make-test-suite-support-Expat-2.4.5.patch deleted file mode 100644 index cfa56e6e5bf629987cf7e0ab74fdf0160059a89e..0000000000000000000000000000000000000000 --- a/backport-bpo-46811-Make-test-suite-support-Expat-2.4.5.patch +++ /dev/null @@ -1,104 +0,0 @@ -From 7da97f61816f3cadaa6788804b22a2434b40e8c5 Mon Sep 17 00:00:00 2001 -From: "Miss Islington (bot)" - <31488909+miss-islington@users.noreply.github.com> -Date: Mon, 21 Feb 2022 08:16:09 -0800 -Subject: [PATCH] bpo-46811: Make test suite support Expat >=2.4.5 (GH-31453) - (GH-31472) - -Curly brackets were never allowed in namespace URIs -according to RFC 3986, and so-called namespace-validating -XML parsers have the right to reject them a invalid URIs. - -libexpat >=2.4.5 has become strcter in that regard due to -related security issues; with ET.XML instantiating a -namespace-aware parser under the hood, this test has no -future in CPython. - -References: -- https://datatracker.ietf.org/doc/html/rfc3968 -- https://www.w3.org/TR/xml-names/ - -Also, test_minidom.py: Support Expat >=2.4.5 -(cherry picked from commit 2cae93832f46b245847bdc252456ddf7742ef45e) - -Co-authored-by: Sebastian Pipping ---- - Lib/test/test_minidom.py | 17 +++++++++++++++-- - Lib/test/test_xml_etree.py | 6 ------ - .../Library/2022-02-20-21-03-31.bpo-46811.8BxgdQ.rst | 1 + - 3 files changed, 16 insertions(+), 8 deletions(-) - create mode 100644 Misc/NEWS.d/next/Library/2022-02-20-21-03-31.bpo-46811.8BxgdQ.rst - -diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py -index 1663b1f..9762025 100644 ---- a/Lib/test/test_minidom.py -+++ b/Lib/test/test_minidom.py -@@ -6,10 +6,12 @@ import io - from test import support - import unittest - -+import pyexpat - import xml.dom.minidom - - from xml.dom.minidom import parse, Node, Document, parseString - from xml.dom.minidom import getDOMImplementation -+from xml.parsers.expat import ExpatError - - - tstfile = support.findfile("test.xml", subdir="xmltestdata") -@@ -1147,7 +1149,13 @@ class MinidomTest(unittest.TestCase): - - # Verify that character decoding errors raise exceptions instead - # of crashing -- self.assertRaises(UnicodeDecodeError, parseString, -+ if pyexpat.version_info >= (2, 4, 5): -+ self.assertRaises(ExpatError, parseString, -+ b'') -+ self.assertRaises(ExpatError, parseString, -+ b'Comment \xe7a va ? Tr\xe8s bien ?') -+ else: -+ self.assertRaises(UnicodeDecodeError, parseString, - b'Comment \xe7a va ? Tr\xe8s bien ?') - - doc.unlink() -@@ -1609,7 +1617,12 @@ class MinidomTest(unittest.TestCase): - self.confirm(doc2.namespaceURI == xml.dom.EMPTY_NAMESPACE) - - def testExceptionOnSpacesInXMLNSValue(self): -- with self.assertRaisesRegex(ValueError, 'Unsupported syntax'): -+ if pyexpat.version_info >= (2, 4, 5): -+ context = self.assertRaisesRegex(ExpatError, 'syntax error') -+ else: -+ context = self.assertRaisesRegex(ValueError, 'Unsupported syntax') -+ -+ with context: - parseString('') - - def testDocRemoveChild(self): -diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py -index 285559a..4bd4291 100644 ---- a/Lib/test/test_xml_etree.py -+++ b/Lib/test/test_xml_etree.py -@@ -2183,12 +2183,6 @@ class BugsTest(unittest.TestCase): - b"\n" - b'tãg') - -- def test_issue3151(self): -- e = ET.XML('') -- self.assertEqual(e.tag, '{${stuff}}localname') -- t = ET.ElementTree(e) -- self.assertEqual(ET.tostring(e), b'') -- - def test_issue6565(self): - elem = ET.XML("") - self.assertEqual(summarize_list(elem), ['tag']) -diff --git a/Misc/NEWS.d/next/Library/2022-02-20-21-03-31.bpo-46811.8BxgdQ.rst b/Misc/NEWS.d/next/Library/2022-02-20-21-03-31.bpo-46811.8BxgdQ.rst -new file mode 100644 -index 0000000..6969bd1 ---- /dev/null -+++ b/Misc/NEWS.d/next/Library/2022-02-20-21-03-31.bpo-46811.8BxgdQ.rst -@@ -0,0 +1 @@ -+Make test suite support Expat >=2.4.5 --- -1.8.3.1 - diff --git a/python3.spec b/python3.spec index c72388b3eaf2c6cc54fb22fa2cd9b31301be0b56..5d37dda649c2d8581cdf5193b7ee0b44a6ae152d 100644 --- a/python3.spec +++ b/python3.spec @@ -2,12 +2,12 @@ Name: python3 Summary: Interpreter of the Python3 programming language URL: https://www.python.org/ -Version: 3.10.2 -Release: 12 +Version: 3.11.1 +Release: 1 License: Python-2.0 -%global branchversion 3.10 -%global pyshortver 310 +%global branchversion 3.11 +%global pyshortver 311 %ifarch %{ix86} x86_64 %bcond_with optimizations @@ -87,15 +87,6 @@ Source1: pyconfig.h Patch1: 00001-rpath.patch Patch251: 00251-change-user-install-location.patch -Patch6000: backport-bpo-46811-Make-test-suite-support-Expat-2.4.5.patch -Patch6001: backport-CVE-2015-20107.patch -Patch6002: backport-CVE-2021-28861.patch -Patch6003: backport-0001-CVE-2020-10735.patch -Patch6004: backport-0002-CVE-2020-10735.patch -Patch6005: backport-0003-CVE-2020-10735.patch -Patch6006: backport-CVE-2022-42919.patch -Patch6007: backport-CVE-2022-45061.patch -Patch6008: backport-CVE-2022-37454.patch Patch9000: add-the-sm3-method-for-obtaining-the-salt-value.patch @@ -191,15 +182,6 @@ rm configure pyconfig.h.in %patch1 -p1 %patch251 -p1 -%patch6000 -p1 -%patch6001 -p1 -%patch6002 -p1 -%patch6003 -p1 -%patch6004 -p1 -%patch6005 -p1 -%patch6006 -p1 -%patch6007 -p1 -%patch6008 -p1 %patch9000 -p1 @@ -412,7 +394,8 @@ LD_LIBRARY_PATH=$(pwd)/build/debug $(pwd)/build/debug/python -m test.regrtest \ -x test_bdist_rpm \ -x test_gdb \ -x test_socket \ - -x test_asyncio + -x test_asyncio \ + -i test_freeze_simple_script export OPENSSL_CONF=/non-existing-file LD_LIBRARY_PATH=$(pwd)/build/optimized $(pwd)/build/optimized/python -m test.pythoninfo @@ -424,7 +407,8 @@ LD_LIBRARY_PATH=$(pwd)/build/optimized $(pwd)/build/optimized/python -m test.reg -x test_bdist_rpm \ -x test_gdb \ -x test_socket \ - -x test_asyncio + -x test_asyncio \ + -i test_freeze_simple_script export BEP_WHITELIST="$BEP_WHITELIST_TMP" export BEP_GTDLIST="$BEP_GTDLIST_TMP" @@ -470,6 +454,11 @@ export BEP_GTDLIST="$BEP_GTDLIST_TMP" %exclude %{pylibdir}/ensurepip/_bundled +%dir %{pylibdir}/__phello__/ +%dir %{pylibdir}/__phello__/__pycache__/ +%{pylibdir}/__phello__/*.py +%{pylibdir}/__phello__/__pycache__/*%{bytecode_suffixes} + %dir %{pylibdir}/test/ %dir %{pylibdir}/test/__pycache__/ %dir %{pylibdir}/test/support/ @@ -533,6 +522,7 @@ export BEP_GTDLIST="$BEP_GTDLIST_TMP" %{dynload_dir}/_sqlite3.%{SOABI_optimized}.so %{dynload_dir}/_ssl.%{SOABI_optimized}.so %{dynload_dir}/_struct.%{SOABI_optimized}.so +%{dynload_dir}/_typing.%{SOABI_optimized}.so %{dynload_dir}/array.%{SOABI_optimized}.so %{dynload_dir}/audioop.%{SOABI_optimized}.so %{dynload_dir}/binascii.%{SOABI_optimized}.so @@ -615,6 +605,11 @@ export BEP_GTDLIST="$BEP_GTDLIST_TMP" %dir %{pylibdir}/importlib/metadata/__pycache__/ %{pylibdir}/importlib/metadata/ +%dir %{pylibdir}/importlib/resources/ +%dir %{pylibdir}/importlib/resources/__pycache__/ +%{pylibdir}/importlib/resources/*.py +%{pylibdir}/importlib/resources/__pycache__/*%{bytecode_suffixes} + %dir %{pylibdir}/json/ %dir %{pylibdir}/json/__pycache__/ %{pylibdir}/json/*.py @@ -623,6 +618,16 @@ export BEP_GTDLIST="$BEP_GTDLIST_TMP" %{pylibdir}/logging %{pylibdir}/multiprocessing +%dir %{pylibdir}/re/ +%dir %{pylibdir}/re/__pycache__/ +%{pylibdir}/re/*.py +%{pylibdir}/re/__pycache__/*%{bytecode_suffixes} + +%dir %{pylibdir}/tomllib/ +%dir %{pylibdir}/tomllib/__pycache__/ +%{pylibdir}/tomllib/*.py +%{pylibdir}/tomllib/__pycache__/*%{bytecode_suffixes} + %dir %{pylibdir}/sqlite3/ %dir %{pylibdir}/sqlite3/__pycache__/ %{pylibdir}/sqlite3/*.py @@ -684,7 +689,6 @@ export BEP_GTDLIST="$BEP_GTDLIST_TMP" %{pylibdir}/ctypes/test %{pylibdir}/distutils/tests -%{pylibdir}/sqlite3/test %{pylibdir}/test %exclude %{pylibdir}/test/capath %exclude %{pylibdir}/test/*.pem @@ -762,6 +766,7 @@ export BEP_GTDLIST="$BEP_GTDLIST_TMP" %{dynload_dir}/_sqlite3.%{SOABI_debug}.so %{dynload_dir}/_ssl.%{SOABI_debug}.so %{dynload_dir}/_struct.%{SOABI_debug}.so +%{dynload_dir}/_typing.%{SOABI_debug}.so %{dynload_dir}/array.%{SOABI_debug}.so %{dynload_dir}/audioop.%{SOABI_debug}.so %{dynload_dir}/binascii.%{SOABI_debug}.so @@ -817,6 +822,12 @@ export BEP_GTDLIST="$BEP_GTDLIST_TMP" %{_mandir}/*/* %changelog +* Sat Jan 28 2023 zhuofeng - 3.11.1-1 +- Type:enhancement +- ID:NA +- SUG:NA +- DESC:update version to 3.11.1 + * Mon Nov 28 2022 zhuofeng - 3.10.2-12 - Type:CVE - CVE:CVE-2022-37454