Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
19b28fc
CVE-2020-10735: Prevent DoS by very large int()
tiran May 5, 2020
0a96b20
Default to disable, improve tests and docs
tiran Jan 19, 2022
88f6d5d
fix typo
tiran Jan 19, 2022
70c195e
More docs (WIP)
tiran Jan 19, 2022
e17e93b
Basic documentation for sys functions
tiran Jan 19, 2022
fbd14b7
Use ValueError, ignore underscore, scale limit
tiran Jan 20, 2022
dd74d70
Fix CI
tiran Jan 20, 2022
0e01461
Address Greg's review
tiran Aug 1, 2022
0b21e5f
Fix sys.flags len and docs
tiran Aug 1, 2022
3b38abe
Keep the warning, but remove advice about limiting input length in th…
gpshead Aug 2, 2022
37193ed
Renamed the APIs & too many other refactorings.
gpshead Aug 5, 2022
c90b79f
Improve the configuring docs.
gpshead Aug 7, 2022
fea25ea
Stop tying to base10, just use string digits.
gpshead Aug 7, 2022
ac9f22f
Remove the added now-unneeded helper log tbl fn.
gpshead Aug 7, 2022
da72dd1
prevent intdostimeit from emitting errors in test_tools.
gpshead Aug 7, 2022
d7e4d7b
Remove a leftover base 10 reference. clarify.
gpshead Aug 7, 2022
5c7e6d5
versionadded/changed to 3.12
gpshead Aug 7, 2022
61a5bc9
Link to the CVE from the main doc.
gpshead Aug 7, 2022
c15adde
Add a What's New entry.
gpshead Aug 7, 2022
76ae1c2
Add a Misc/NEWS.d entry.
gpshead Aug 7, 2022
1ad88f5
Undo addition to PyConfig to ease backporting.
gpshead Aug 8, 2022
0c83111
Remove the Tools/scripts/ example and timing code.
gpshead Aug 8, 2022
5d39ab6
un-add the <math.h> include (not needed for PR anymore)
gpshead Aug 8, 2022
5b77b3e
Remove added unused imports.
gpshead Aug 8, 2022
de00cdc
Tabs -> Spaces
gpshead Aug 8, 2022
3cc8553
make html and make doctest in Doc pass.
gpshead Aug 8, 2022
da97e65
Raise the default limit and the threshold.
gpshead Aug 10, 2022
ef03a16
Remove xmlrpc.client changes, test-only.
gpshead Aug 12, 2022
e916845
Rearrange the new stdtypes docs, w/limits + caution.
gpshead Aug 13, 2022
101502e
Make a huge int a SyntaxError with lineno when parsing.
gpshead Aug 16, 2022
fa8a58a
Mention the chosen default in the NEWS entry.
gpshead Aug 16, 2022
313ab6d
Properly clear & free the prior exception.
gpshead Aug 16, 2022
614cd02
Add a note to the float.as_integer_ratio() docs.
gpshead Aug 17, 2022
16ad090
Clarify the documentation wording and error msg.
gpshead Aug 17, 2022
4eb72e6
Fix test_idle, it used a long int on a line.
gpshead Aug 17, 2022
da36550
Rename the test.support context manager and document it.
gpshead Aug 19, 2022
f4372cc
Documentation cleanup.
gpshead Aug 19, 2022
c421853
Update attribution in Misc/NEWS.d
gpshead Aug 25, 2022
9f2168a
Regen global strings
tiran Sep 1, 2022
3c8504b
Make the doctest actually run & fix it.
gpshead Sep 1, 2022
1586419
Fix the docs build.
gpshead Sep 2, 2022
94bd3ee
Rename the news file to appease the Bedevere bot.
gpshead Sep 2, 2022
0b91f65
Regen argument clinic after the rebase merge.
gpshead Sep 2, 2022
02776f9
Hexi hexa
tiran Sep 2, 2022
173fa4e
Hexi hexa 2
tiran Sep 2, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Default to disable, improve tests and docs
  • Loading branch information
tiran authored and gpshead committed Sep 2, 2022
commit 0a96b20a136f42f136ced5cbb7ab9aaf0cda55cb
9 changes: 9 additions & 0 deletions Doc/c-api/init_config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,15 @@ PyConfig

Default: ``0``.

.. c:member:: int intmaxdigits

If greater than 0, enable int digit limitation.

Configured by :option:`-X intmaxdigits <-X>` command line option or
:envvar:`PYTHONINTMAXDIGITS` env var.

Default: ``-1``.

.. c:member:: int isolated

If greater than ``0``, enable isolated mode:
Expand Down
32 changes: 22 additions & 10 deletions Doc/library/sys.rst
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,7 @@ always available.
:const:`dev_mode` :option:`-X dev <-X>` (:ref:`Python Development Mode <devmode>`)
:const:`utf8_mode` :option:`-X utf8 <-X>`
:const:`safe_path` :option:`-P`
:const:`intmaxdigits` `:option:``-X intmaxdigits <-X>` (default: *-1*)
============================= ================================================================

.. versionchanged:: 3.2
Expand All @@ -543,6 +544,9 @@ always available.
.. versionchanged:: 3.11
Added the ``safe_path`` attribute for :option:`-P` option.

.. versionchanged:: 3.12
Added ``intmaxdigits`` attribute


.. data:: float_info

Expand Down Expand Up @@ -1002,19 +1006,27 @@ 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_digits` | default value for :func:`sys.getintmaxdigits` |
+-------------------------------------+-----------------------------------------------+
| :const:`max_digits_check_threshold` | minimum value value for |
| | :func:`sys.setintmaxdigits` |
+-------------------------------------+-----------------------------------------------+

.. versionadded:: 3.1

.. versionchanged:: 3.11
Added ``default_max_digits`` and ``max_digits_check_threshold``.


.. data:: __interactivehook__

Expand Down
9 changes: 8 additions & 1 deletion Doc/using/cmdline.rst
Original file line number Diff line number Diff line change
Expand Up @@ -505,11 +505,13 @@ 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 intmaxdigits`` to enable or disable int conversion limit.
See also :envvar:`PYTHONPROFILEIMPORTTIME`.
* ``-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
application. Typical usage is ``python3 -X importtime -c 'import
asyncio'``. See also :envvar:`PYTHONPROFILEIMPORTTIME`.
asyncio'``. See also :envvar:`PYTHONINTMAXDIGITS`.
* ``-X dev``: enable :ref:`Python Development Mode <devmode>`, introducing
additional runtime checks that are too expensive to be enabled by
default.
Expand Down Expand Up @@ -763,6 +765,11 @@ conflict.

.. versionadded:: 3.2.3

.. envvar:: PYTHONINTMAXDIGITS

TODO

.. versionadded:: 3.11

.. envvar:: PYTHONIOENCODING

Expand Down
9 changes: 7 additions & 2 deletions Include/longobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,13 @@ PyAPI_FUNC(unsigned long long) PyLong_AsUnsignedLongLongMask(PyObject *);
PyAPI_FUNC(long long) PyLong_AsLongLongAndOverflow(PyObject *, int *);

/* Default limitation */
#define _PY_LONG_DEFAULT_MAX_DIGITS 5000
/* Don't check unless input / output is larger than threshold */
#ifndef _PY_LONG_DEFAULT_MAX_DIGITS
# define _PY_LONG_DEFAULT_MAX_DIGITS 0
#endif
/* Threshold for max digits check. For performance reasons int() and
int.__str__ don't checks values that are smaller than the
threshold. For common cases it avoids a lookup of the interpreter
state in a hot path */
#define _PY_LONG_MAX_DIGITS_TRESHOLD 1024

PyAPI_FUNC(PyObject *) PyLong_FromString(const char *, char **, int);
Expand Down
12 changes: 12 additions & 0 deletions Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2341,3 +2341,15 @@ def sleeping_retry(timeout, err_msg=None, /,

time.sleep(delay)
delay = min(delay * 2, max_delay)


@contextlib.contextmanager
def setintmaxdigits(maxdigits):
"""Set integer max digits limit
"""
current = sys.getintmaxdigits()
try:
sys.setintmaxdigits(maxdigits)
yield
finally:
sys.setintmaxdigits(current)
33 changes: 33 additions & 0 deletions Lib/test/test_cmd_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# See test_cmd_line_script.py for testing of script execution

import os
import re
import subprocess
import sys
import tempfile
Expand Down Expand Up @@ -865,6 +866,38 @@ def test_parsing_error(self):
self.assertTrue(proc.stderr.startswith(err_msg), proc.stderr)
self.assertNotEqual(proc.returncode, 0)

def test_intmaxdigits(self):
code = "import sys; print(sys.flags.intmaxdigits, sys.getintmaxdigits())"

assert_python_failure('-X', 'intmaxdigits', '-c', code)
assert_python_failure('-X', 'intmaxdigits=foo', '-c', code)
assert_python_failure('-X', 'intmaxdigits=100', '-c', code)

assert_python_failure('-c', code, PYTHONINTMAXDIGITS='foo')
assert_python_failure('-c', code, PYTHONINTMAXDIGITS='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.getintmaxdigits()))
res = assert_python_ok('-X', 'intmaxdigits=0', '-c', code)
self.assertEqual(res2int(res), (0, 0))
res = assert_python_ok('-X', 'intmaxdigits=4000', '-c', code)
self.assertEqual(res2int(res), (4000, 4000))
res = assert_python_ok('-X', 'intmaxdigits=100000', '-c', code)
self.assertEqual(res2int(res), (100000, 100000))

res = assert_python_ok('-c', code, PYTHONINTMAXDIGITS='0')
self.assertEqual(res2int(res), (0, 0))
res = assert_python_ok('-c', code, PYTHONINTMAXDIGITS='4000')
self.assertEqual(res2int(res), (4000, 4000))
res = assert_python_ok(
'-X', 'intmaxdigits=6000', '-c', code, PYTHONINTMAXDIGITS='4000'
)
self.assertEqual(res2int(res), (6000, 6000))


@unittest.skipIf(interpreter_requires_environment(),
'Cannot run -I tests when PYTHON env vars are required.')
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'install_signal_handlers': 1,
'use_hash_seed': 0,
'hash_seed': 0,
'intmaxdigits': -1,
'faulthandler': 0,
'tracemalloc': 0,
'perf_profiling': 0,
Expand Down
32 changes: 11 additions & 21 deletions Lib/test/test_int.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import contextlib
import sys

import unittest
Expand Down Expand Up @@ -28,16 +27,6 @@
]


@contextlib.contextmanager
def setintmaxdigits(maxdigits):
current = sys.getintmaxdigits()
try:
sys.setintmaxdigits(maxdigits)
yield
finally:
sys.setintmaxdigits(current)


class IntSubclass(int):
pass

Expand Down Expand Up @@ -590,16 +579,17 @@ def test_issue31619(self):

def _test_maxdigits(self, c):
maxdigits = sys.getintmaxdigits()
# edge cases
c('1' * maxdigits)
c(' ' + '1' * maxdigits)
c('+' + '1' * maxdigits)
self.assertEqual(len(str(10 ** (maxdigits - 1))), maxdigits)
if maxdigits != 0:
# edge cases
c('1' * maxdigits)
c(' ' + '1' * maxdigits)
c('+' + '1' * maxdigits)
self.assertEqual(len(str(10 ** (maxdigits - 1))), maxdigits)

# disable limitation
with setintmaxdigits(0):
c('1' * (maxdigits + 1))
c('1' * (maxdigits + 1))
with support.setintmaxdigits(0):
i = c('1' * 100_000)
str(i)

# OverflowError
def check(i, base=None):
Expand All @@ -609,8 +599,8 @@ def check(i, base=None):
else:
c(i, base)

with setintmaxdigits(1024):
maxdigits = 1024
maxdigits = 1024
with support.setintmaxdigits(maxdigits):
check('1' * (maxdigits + 1))
check('+' + '1' * (maxdigits + 1))
check('1' * (maxdigits + 1))
Expand Down
10 changes: 6 additions & 4 deletions Lib/test/test_json/test_decode.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from io import StringIO
from collections import OrderedDict
from test.test_json import PyTest, CTest
from test import support
import sys


Expand Down Expand Up @@ -97,10 +98,11 @@ def test_negative_index(self):
self.assertRaises(ValueError, d.raw_decode, 'a'*42, -50000)

def test_limit_int(self):
maxdigits = sys.getintmaxdigits()
self.loads('1' * maxdigits)
with self.assertRaises(OverflowError):
self.loads('1' * (maxdigits + 1))
maxdigits = 5000
with support.setintmaxdigits(maxdigits):
self.loads('1' * maxdigits)
with self.assertRaises(OverflowError):
self.loads('1' * (maxdigits + 1))


class TestPyDecode(TestDecode, PyTest): pass
Expand Down
8 changes: 6 additions & 2 deletions Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -550,11 +550,15 @@ def test_attributes(self):
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_digits, 0)
self.assertGreaterEqual(sys.int_info.max_digits_check_threshold, 0)
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_digits, int)
self.assertIsInstance(sys.int_info.max_digits_check_threshold, int)
self.assertIsInstance(sys.hexversion, int)

self.assertEqual(len(sys.hash_info), 9)
Expand Down Expand Up @@ -677,7 +681,7 @@ def test_sys_flags(self):
"dont_write_bytecode", "no_user_site", "no_site",
"ignore_environment", "verbose", "bytes_warning", "quiet",
"hash_randomization", "isolated", "dev_mode", "utf8_mode",
"warn_default_encoding", "safe_path")
"warn_default_encoding", "safe_path", "intmaxdigits")
for attr in attrs:
self.assertTrue(hasattr(sys.flags, attr), attr)
attr_type = bool if attr in ("dev_mode", "safe_path") else int
Expand Down
6 changes: 4 additions & 2 deletions Lib/test/test_xmlrpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,10 @@ def test_limit_int(self):
with self.assertRaises(OverflowError):
check('<int>123456780123456789</int>', None)
with self.assertRaises(OverflowError):
s = '1' * (sys.getintmaxdigits() + 1)
check(f'<biginteger>{s}</biginteger>', None)
maxdigits = 5000
with support.setintmaxdigits(maxdigits):
s = '1' * (maxdigits + 1)
check(f'<biginteger>{s}</biginteger>', None)

def test_get_host_info(self):
# see bug #3613, this raised a TypeError
Expand Down
5 changes: 4 additions & 1 deletion Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -6114,7 +6114,7 @@ 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_digits", "maximum digits limitation"},
{"max_digits_threshold", "minimum threshold to check for max digits"},
{"max_digits_check_threshold", "minimum threshold to check for max digits"},
{NULL, NULL}
};

Expand Down Expand Up @@ -6169,6 +6169,9 @@ _PyLong_InitTypes(PyInterpreterState *interp)
}
}
interp->intmaxdigits = _PyInterpreterState_GetConfig(interp)->intmaxdigits;
if (interp->intmaxdigits == -1) {
interp->intmaxdigits = _PY_LONG_DEFAULT_MAX_DIGITS;
}

return _PyStatus_OK();
}
Expand Down
4 changes: 2 additions & 2 deletions Python/initconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -1775,8 +1775,8 @@ config_init_intmaxdigits(PyConfig *config)
int maxdigits;
int valid = 0;

/* set default limitation */
config->intmaxdigits = _PY_LONG_DEFAULT_MAX_DIGITS;
/* default to unconfigured, _PyLong_InitTypes() does the rest */
config->intmaxdigits = -1;

const char *env = config_get_env(config, "PYTHONINTMAXDIGITS");
if (env) {
Expand Down