diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst index c4a342ee811ca9..9e461eca3cb12d 100644 --- a/Doc/c-api/init_config.rst +++ b/Doc/c-api/init_config.rst @@ -828,6 +828,18 @@ PyConfig Default: ``0``. + .. c:member:: int32_t int_max_str_digits + + If greater than 0, enable int conversion digit limitations. ``-1`` means + that :data:`sys.int_info.default_max_str_digits` will be used. + + Configured by the :option:`-X int_max_str_digits <-X>` command line + flag or the :envvar:`PYTHONINTMAXSTRDIGITS` environment varable. + + Default: ``-1``. + + .. versionadded:: 3.12 + .. c:member:: int isolated If greater than ``0``, enable isolated mode: diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index b9cf02e87eb614..ccb691dd9f009f 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -910,7 +910,7 @@ are always available. They are listed here in alphabetical order. .. versionchanged:: 3.11 The delegation to :meth:`__trunc__` is deprecated. - .. versionchanged:: 3.12 + .. versionchanged:: 3.11 :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 diff --git a/Doc/library/json.rst b/Doc/library/json.rst index d05d62e78cc71e..de02be850544e2 100644 --- a/Doc/library/json.rst +++ b/Doc/library/json.rst @@ -258,7 +258,7 @@ Basic Usage be used to use another datatype or parser for JSON integers (e.g. :class:`float`). - .. versionchanged:: 3.12 + .. versionchanged:: 3.11 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 diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 01c9b9a5cf4106..a5ff413ec059de 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -5523,6 +5523,22 @@ Verification: ... '571186405732').to_bytes(53, 'big') ... +.. versionadded:: 3.11 + +.. audit-event:: int/digits/to_decimal value + + When a conversion from an :class:`int` to decimal is estimated to be larger + than :data:`sys.int_info.str_digits_check_threshold ` digits + an :ref:`auditing event ` with the :class:`int` ``value`` as the + argument is raised. + +.. audit-event:: int/digits/from_base digits base + + When a conversion to an :class:`int` from a base other than 2, 4, 8, 16, or + 32 has more than :data:`sys.int_info.str_digits_check_threshold + ` digits an :ref:`auditing event ` with the number + of ``digits`` and ``base`` is raised. + .. versionadded:: 3.12 Affected APIs @@ -5578,7 +5594,7 @@ Information about the default and minimum can be found in :attr:`sys.int_info`: * :data:`sys.int_info.str_digits_check_threshold ` is the lowest accepted value for the limit (other than 0 which disables it). -.. versionadded:: 3.12 +.. versionadded:: 3.11 .. caution:: diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index c6be12c14b550a..aab3f6aa83fced 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -544,7 +544,7 @@ always available. .. versionchanged:: 3.11 Added the ``safe_path`` attribute for :option:`-P` option. - .. versionchanged:: 3.12 + .. versionchanged:: 3.11 Added the ``int_max_str_digits`` attribute. @@ -732,7 +732,7 @@ always available. Returns the current value for the :ref:`integer string conversion length limitation `. See also :func:`set_int_max_str_digits`. - .. versionadded:: 3.12 + .. versionadded:: 3.11 .. function:: getrefcount(object) @@ -1029,7 +1029,7 @@ always available. .. versionadded:: 3.1 - .. versionchanged:: 3.12 + .. versionchanged:: 3.11 Added ``default_max_str_digits`` and ``str_digits_check_threshold``. @@ -1337,7 +1337,7 @@ always available. ` used by this interpreter. See also :func:`get_int_max_str_digits`. - .. versionadded:: 3.12 + .. versionadded:: 3.11 .. function:: setprofile(profilefunc) diff --git a/Doc/library/test.rst b/Doc/library/test.rst index eff375132318a9..53bcd7c178f947 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -1018,7 +1018,7 @@ The :mod:`test.support` module defines the following functions: 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.12 + .. versionadded:: 3.11 The :mod:`test.support` module defines the following classes: diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index 6a33d98a059a09..02c9f3095b4090 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -585,7 +585,7 @@ Miscellaneous options .. versionadded:: 3.11 The ``-X frozen_modules`` option. - .. versionadded:: 3.12 + .. versionadded:: 3.11 The ``-X int_max_str_digits`` option. .. versionadded:: 3.12 @@ -775,7 +775,7 @@ conflict. interpreter's global :ref:`integer string conversion length limitation `. - .. versionadded:: 3.12 + .. versionadded:: 3.11 .. envvar:: PYTHONIOENCODING diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 90355a73f08004..936cc1524e41df 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -84,15 +84,11 @@ Other Language Changes (Contributed by Serhiy Storchaka in :gh:`87995`.) * 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. + (binary), 4, 8 (octal), 16 (hexadecimal), or 32 such as base 10 (decimal) now + generates an :ref:`auditing event ` whenever the estimated number + of digits is above :data:`sys.int_info.str_digits_check_threshold + `. See the :ref:`integer string conversion length limitation + ` documentation. * :class:`memoryview` now supports the half-float type (the "e" format code). (Contributed by Dong-hee Na and Antoine Pitrou in :gh:`90751`.) diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h index c6057a4c3ed945..2143074c5c2fd3 100644 --- a/Include/cpython/initconfig.h +++ b/Include/cpython/initconfig.h @@ -178,6 +178,7 @@ typedef struct PyConfig { wchar_t *check_hash_pycs_mode; int use_frozen_modules; int safe_path; + int32_t int_max_str_digits; /* --- Path configuration inputs ------------ */ int pathconfig_warnings; diff --git a/Include/internal/pycore_initconfig.h b/Include/internal/pycore_initconfig.h index 6e491261d55ca6..1b2ed861dc9c80 100644 --- a/Include/internal/pycore_initconfig.h +++ b/Include/internal/pycore_initconfig.h @@ -79,6 +79,9 @@ PyAPI_FUNC(PyStatus) _PyArgv_AsWstrList(const _PyArgv *args, PyAPI_FUNC(int) _Py_str_to_int( const char *str, int *result); +PyAPI_FUNC(int) _Py_str_to_int32( + const char *str, + int32_t *result); PyAPI_FUNC(const wchar_t*) _Py_get_xoption( const PyWideStringList *xoptions, const wchar_t *name); @@ -170,8 +173,6 @@ extern void _Py_DumpPathConfig(PyThreadState *tstate); PyAPI_FUNC(PyObject*) _Py_Get_Getpath_CodeObject(void); -extern int _Py_global_config_int_max_str_digits; // TODO(gpshead): move this into PyConfig in 3.12 after the backports ship. - /* --- Function used for testing ---------------------------------- */ diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index e7f914ec2fe521..f806c495e7463a 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -175,7 +175,7 @@ struct _is { struct types_state types; struct callable_cache callable_cache; - int int_max_str_digits; + int32_t int_max_str_digits; /* The following fields are here to avoid allocation during init. The data is exposed through PyInterpreterState pointer fields. diff --git a/Lib/test/audit-tests.py b/Lib/test/audit-tests.py index 66c08f7f2ff8d5..56c17303d4ec2b 100644 --- a/Lib/test/audit-tests.py +++ b/Lib/test/audit-tests.py @@ -7,6 +7,7 @@ import contextlib import os +from test import support import sys @@ -409,8 +410,6 @@ def hook(event, *args): def test_sys_getframe(): - import sys - def hook(event, args): if event.startswith("sys."): print(event, args[0].f_code.co_name) @@ -430,10 +429,24 @@ def hook(event, args): _wmi.exec_query("SELECT * FROM Win32_OperatingSystem") -if __name__ == "__main__": - from test.support import suppress_msvcrt_asserts +def test_int_digits(): + def hook(event, args): + if event.startswith("int/digits/"): + print(event, *(hex(arg) for arg in args)) + + sys.addaudithook(hook) + + threshold = sys.int_info.str_digits_check_threshold + with support.adjust_int_max_str_digits(2*threshold): + fives = int('5' * (threshold+20)) + int('1' * (threshold*3//2), base=36) + for base in (2, 4, 8, 16, 32): # linear unaudited bases + int('1' * (threshold+1), base) + str(fives) - suppress_msvcrt_asserts() + +if __name__ == "__main__": + support.suppress_msvcrt_asserts() test = sys.argv[1] globals()[test]() diff --git a/Lib/test/test_audit.py b/Lib/test/test_audit.py index 09b3333afe184f..91bcb9ff3b4c75 100644 --- a/Lib/test/test_audit.py +++ b/Lib/test/test_audit.py @@ -200,5 +200,32 @@ def test_wmi_exec_query(self): self.assertEqual(actual, expected) + def test_int_digits(self): + returncode, events, stderr = self.run_python("test_int_digits") + if returncode: + self.fail(stderr) + + if support.verbose: + print(*events, sep='\n') + actual = [(ev[0], ev[2].split(' ')) for ev in events] + + threshold = sys.int_info.str_digits_check_threshold + self.assertEqual( + actual.pop(0), + ("int/digits/from_base", [hex(threshold+20), hex(10)]), + ) + self.assertEqual( + actual.pop(0), + ("int/digits/from_base", [hex(threshold*3//2), hex(36)]), + ) + + event = actual.pop(0) + self.assertEqual(event[0], "int/digits/to_decimal") + self.assertEqual(len(event[1]), 1) + self.assertGreater(len(event[1][0]), threshold//2) + + self.assertEqual(actual, []) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 70d7367ea9e64f..18ce517c474f44 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -434,6 +434,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): 'install_signal_handlers': 1, 'use_hash_seed': 0, 'hash_seed': 0, + 'int_max_str_digits': -1, 'faulthandler': 0, 'tracemalloc': 0, 'perf_profiling': 0, diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-09-15-16-11-38.gh-issue-96512.VrlORK.rst b/Misc/NEWS.d/next/Core and Builtins/2022-09-15-16-11-38.gh-issue-96512.VrlORK.rst new file mode 100644 index 00000000000000..8c9c0e4cca9b64 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-09-15-16-11-38.gh-issue-96512.VrlORK.rst @@ -0,0 +1,4 @@ +Added ``int/digits/to_decimal`` and ``int/digits/from_base`` :ref:`auditing +events ` on :class:`int` non-linear algorithm base conversions +with more than `sys.int_info.str_digits_check_threshold ` +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 deleted file mode 100644 index 8eb8a34884dced..00000000000000 --- a/Misc/NEWS.d/next/Security/2022-08-07-16-53-38.gh-issue-95778.ch010gps.rst +++ /dev/null @@ -1,14 +0,0 @@ -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, Ned Deily, and Mark Dickinson. diff --git a/Objects/longobject.c b/Objects/longobject.c index cb52f82687af28..c6c6e511e97705 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -1766,8 +1766,11 @@ long_to_decimal_string_internal(PyObject *aa, */ if (size_a >= 10 * _PY_LONG_MAX_STR_DIGITS_THRESHOLD / (3 * PyLong_SHIFT) + 2) { + if (PySys_Audit("int/digits/to_decimal", "O", aa) < 0) { + return -1; + } PyInterpreterState *interp = _PyInterpreterState_GET(); - int max_str_digits = interp->int_max_str_digits; + int32_t 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, @@ -1837,7 +1840,7 @@ long_to_decimal_string_internal(PyObject *aa, } if (strlen > _PY_LONG_MAX_STR_DIGITS_THRESHOLD) { PyInterpreterState *interp = _PyInterpreterState_GET(); - int max_str_digits = interp->int_max_str_digits; + int32_t 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); @@ -2513,8 +2516,11 @@ digit beyond the first. /* Limit the size to avoid excessive computation attacks. */ if (digits > _PY_LONG_MAX_STR_DIGITS_THRESHOLD) { + if (PySys_Audit("int/digits/from_base", "ni", digits, base) < 0) { + return NULL; + } PyInterpreterState *interp = _PyInterpreterState_GET(); - int max_str_digits = interp->int_max_str_digits; + int32_t 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_TO_INT, max_str_digits, digits); @@ -6196,7 +6202,7 @@ _PyLong_InitTypes(PyInterpreterState *interp) return _PyStatus_ERR("can't init int info type"); } } - interp->int_max_str_digits = _Py_global_config_int_max_str_digits; + interp->int_max_str_digits = _PyInterpreterState_GetConfig(interp)->int_max_str_digits; if (interp->int_max_str_digits == -1) { interp->int_max_str_digits = _PY_LONG_DEFAULT_MAX_STR_DIGITS; } diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index 6864b8b0e03b2f..a789211df9bd2f 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -773,7 +773,7 @@ PyDoc_STRVAR(sys_set_int_max_str_digits__doc__, {"set_int_max_str_digits", _PyCFunction_CAST(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); +sys_set_int_max_str_digits_impl(PyObject *module, long maxdigits); static PyObject * sys_set_int_max_str_digits(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -805,13 +805,13 @@ sys_set_int_max_str_digits(PyObject *module, PyObject *const *args, Py_ssize_t n }; #undef KWTUPLE PyObject *argsbuf[1]; - int maxdigits; + long maxdigits; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); if (!args) { goto exit; } - maxdigits = _PyLong_AsInt(args[0]); + maxdigits = PyLong_AsLong(args[0]); if (maxdigits == -1 && PyErr_Occurred()) { goto exit; } @@ -1343,4 +1343,4 @@ sys_is_stack_trampoline_active(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF #define SYS_GETANDROIDAPILEVEL_METHODDEF #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */ -/*[clinic end generated code: output=15318cdd96b62b06 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b351d7f0c6fea5be input=a9049054013a1b77]*/ diff --git a/Python/initconfig.c b/Python/initconfig.c index f18ec4068bc443..459225d844f3b3 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -789,14 +789,12 @@ _PyConfig_InitCompatConfig(PyConfig *config) config->use_frozen_modules = 1; #endif config->safe_path = 0; + /* config_init_int_max_str_digits() sets default limit */ + config->int_max_str_digits = -1; config->_is_python_build = 0; config->code_debug_ranges = 1; } -/* 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) @@ -1021,6 +1019,7 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2) COPY_ATTR(safe_path); COPY_WSTRLIST(orig_argv); COPY_ATTR(_is_python_build); + COPY_ATTR(int_max_str_digits); #undef COPY_ATTR #undef COPY_WSTR_ATTR @@ -1128,6 +1127,7 @@ _PyConfig_AsDict(const PyConfig *config) SET_ITEM_INT(use_frozen_modules); SET_ITEM_INT(safe_path); SET_ITEM_INT(_is_python_build); + SET_ITEM_INT(int_max_str_digits); return dict; @@ -1630,6 +1630,28 @@ config_wstr_to_int(const wchar_t *wstr, int *result) } +static int +config_wstr_to_int32(const wchar_t *wstr, int32_t *result) +{ +#if INT32_MAX == INT_MAX && INT32_MIN == INT_MIN + return config_wstr_to_int(wstr, result); +#else + const wchar_t *endptr = wstr; + errno = 0; + long value = wcstol(wstr, (wchar_t **)&endptr, 10); + if (*endptr != '\0' || errno == ERANGE) { + return -1; + } + if (value < INT32_MIN || value > INT32_MAX) { + return -1; + } + + *result = (int32_t)value; + return 0; +#endif +} + + static PyStatus config_read_env_vars(PyConfig *config) { @@ -1778,12 +1800,15 @@ config_init_tracemalloc(PyConfig *config) static PyStatus config_init_int_max_str_digits(PyConfig *config) { - int maxdigits; - int valid = 0; + int32_t maxdigits; + bool valid = 0; + + /* default to unconfigured, _PyLong_InitTypes() does the rest */ + config->int_max_str_digits = -1; const char *env = config_get_env(config, "PYTHONINTMAXSTRDIGITS"); if (env) { - if (!_Py_str_to_int(env, &maxdigits)) { + if (!_Py_str_to_int32(env, &maxdigits)) { valid = ((maxdigits == 0) || (maxdigits >= _PY_LONG_MAX_STR_DIGITS_THRESHOLD)); } if (!valid) { @@ -1794,14 +1819,14 @@ config_init_int_max_str_digits(PyConfig *config) STRINGIFY(_PY_LONG_MAX_STR_DIGITS_THRESHOLD) " or 0 for unlimited."); } - _Py_global_config_int_max_str_digits = maxdigits; + 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)) { + if (!config_wstr_to_int32(sep + 1, &maxdigits)) { valid = ((maxdigits == 0) || (maxdigits >= _PY_LONG_MAX_STR_DIGITS_THRESHOLD)); } } @@ -1813,7 +1838,7 @@ config_init_int_max_str_digits(PyConfig *config) #undef _STRINGIFY #undef STRINGIFY } - _Py_global_config_int_max_str_digits = maxdigits; + config->int_max_str_digits = maxdigits; } return _PyStatus_OK(); } @@ -1881,7 +1906,7 @@ config_read_complex_options(PyConfig *config) } } - if (_Py_global_config_int_max_str_digits < 0) { + if (config->int_max_str_digits < 0) { status = config_init_int_max_str_digits(config); if (_PyStatus_EXCEPTION(status)) { return status; diff --git a/Python/preconfig.c b/Python/preconfig.c index 77a86d651eb0f4..c7fdd004889775 100644 --- a/Python/preconfig.c +++ b/Python/preconfig.c @@ -559,6 +559,28 @@ _Py_str_to_int(const char *str, int *result) } +int +_Py_str_to_int32(const char *str, int32_t *result) +{ +#if INT32_MAX == INT_MAX && INT32_MIN == INT_MIN + return _Py_str_to_int(str, result); +#else + const char *endptr = str; + errno = 0; + long value = strtol(str, (char **)&endptr, 10); + if (*endptr != '\0' || errno == ERANGE) { + return -1; + } + if (value < INT32_MIN || value > INT32_MAX) { + return -1; + } + + *result = (int32_t)value; + return 0; +#endif +} + + void _Py_get_env_flag(int use_environment, int *flag, const char *name) { diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 653b5a55e885e5..4d3a0931162030 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1717,23 +1717,27 @@ 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); + return PyLong_FromLong(interp->int_max_str_digits); } /*[clinic input] sys.set_int_max_str_digits - maxdigits: int + maxdigits: long 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]*/ +sys_set_int_max_str_digits_impl(PyObject *module, long maxdigits) +/*[clinic end generated code: output=40ea5d33a82c5c44 input=657c61a1822f6bcf]*/ { PyThreadState *tstate = _PyThreadState_GET(); if ((!maxdigits) || (maxdigits >= _PY_LONG_MAX_STR_DIGITS_THRESHOLD)) { + if (maxdigits > INT32_MAX) { + /* Silently cap to our range; already effectively unlimited. */ + maxdigits = INT32_MAX; + } tstate->interp->int_max_str_digits = maxdigits; Py_RETURN_NONE; } else { @@ -2810,7 +2814,7 @@ set_flags_from_config(PyInterpreterState *interp, PyObject *flags) SetFlag(preconfig->utf8_mode); SetFlag(config->warn_default_encoding); SetFlagObj(PyBool_FromLong(config->safe_path)); - SetFlag(_Py_global_config_int_max_str_digits); + SetFlag(config->int_max_str_digits); #undef SetFlagObj #undef SetFlag return 0;