From e18829a8adb3a64ffffffbd7dcada3c3611522b0 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 13 Jul 2025 12:44:54 +0300 Subject: [PATCH] gh-132629: Deprecate accepting out-of-range values for unsigned integers in PyArg_Parse (GH-132630) For unsigned integer formats in the PyArg_Parse* functions, accepting Python integers with value that is larger than the maximal value the corresponding C type or less than the minimal value for the corresponding signed integer type is now deprecated. --- Doc/c-api/arg.rst | 27 +++-- Doc/whatsnew/3.15.rst | 6 +- Lib/test/clinic.test.c | 95 ++++++++++++--- Lib/test/test_capi/test_getargs.py | 70 ++++++++--- Lib/test/test_clinic.py | 55 ++++++--- ...-04-17-12-37-27.gh-issue-132629.01ArwX.rst | 4 + Modules/clinic/_cursesmodule.c.h | 36 +++++- Modules/clinic/_testclinic.c.h | 87 +++++++++++--- Modules/clinic/_zoneinfo.c.h | 17 ++- Modules/clinic/binascii.c.h | 38 ++++-- Modules/clinic/fcntlmodule.c.h | 19 ++- Modules/clinic/posixmodule.c.h | 54 ++++++++- Modules/clinic/selectmodule.c.h | 38 ++++-- Modules/clinic/signalmodule.c.h | 19 ++- Modules/clinic/zlibmodule.c.h | 110 +++++++++++++++--- Modules/fcntlmodule.c | 4 +- PC/clinic/msvcrtmodule.c.h | 20 +++- Python/getargs.c | 83 +++++++++---- Tools/clinic/libclinic/converters.py | 103 +++++++--------- 19 files changed, 674 insertions(+), 211 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2025-04-17-12-37-27.gh-issue-132629.01ArwX.rst diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index ab9f9c4539a..803572afc39 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -241,9 +241,11 @@ the Python object to the required type. For signed integer formats, :exc:`OverflowError` is raised if the value is out of range for the C type. -For unsigned integer formats, no range checking is done --- the +For unsigned integer formats, the most significant bits are silently truncated when the receiving field is too -small to receive the value. +small to receive the value, and :exc:`DeprecationWarning` is emitted when +the value is larger than the maximal value for the C type or less than +the minimal value for the corresponding signed integer type of the same size. ``b`` (:class:`int`) [unsigned char] Convert a nonnegative Python integer to an unsigned tiny integer, stored in a C @@ -252,27 +254,25 @@ small to receive the value. ``B`` (:class:`int`) [unsigned char] Convert a Python integer to a tiny integer without overflow checking, stored in a C :c:expr:`unsigned char`. + Convert a Python integer to a C :c:expr:`unsigned char`. ``h`` (:class:`int`) [short int] Convert a Python integer to a C :c:expr:`short int`. ``H`` (:class:`int`) [unsigned short int] - Convert a Python integer to a C :c:expr:`unsigned short int`, without overflow - checking. + Convert a Python integer to a C :c:expr:`unsigned short int`. ``i`` (:class:`int`) [int] Convert a Python integer to a plain C :c:expr:`int`. ``I`` (:class:`int`) [unsigned int] - Convert a Python integer to a C :c:expr:`unsigned int`, without overflow - checking. + Convert a Python integer to a C :c:expr:`unsigned int`. ``l`` (:class:`int`) [long int] Convert a Python integer to a C :c:expr:`long int`. ``k`` (:class:`int`) [unsigned long] - Convert a Python integer to a C :c:expr:`unsigned long` without - overflow checking. + Convert a Python integer to a C :c:expr:`unsigned long`. .. versionchanged:: 3.14 Use :meth:`~object.__index__` if available. @@ -281,8 +281,7 @@ small to receive the value. Convert a Python integer to a C :c:expr:`long long`. ``K`` (:class:`int`) [unsigned long long] - Convert a Python integer to a C :c:expr:`unsigned long long` - without overflow checking. + Convert a Python integer to a C :c:expr:`unsigned long long`. .. versionchanged:: 3.14 Use :meth:`~object.__index__` if available. @@ -310,6 +309,14 @@ small to receive the value. ``D`` (:class:`complex`) [Py_complex] Convert a Python complex number to a C :c:type:`Py_complex` structure. +.. deprecated:: next + + For unsigned integer formats ``B``, ``H``, ``I``, ``k`` and ``K``, + :exc:`DeprecationWarning` is emitted when the value is larger than + the maximal value for the C type or less than the minimal value for + the corresponding signed integer type of the same size. + + Other objects ------------- diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 010abb7d9b9..2f713fbb888 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -462,7 +462,11 @@ Porting to Python 3.15 Deprecated C APIs ----------------- -* TODO +* For unsigned integer formats in :c:func:`PyArg_ParseTuple`, + accepting Python integers with value that is larger than the maximal value + for the C type or less than the minimal value for the corresponding + signed integer type of the same size is now deprecated. + (Contributed by Serhiy Storchaka in :gh:`132629`.) .. Add C API deprecations above alphabetically, not here at the end. diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c index 4a67fcd2c3e..dc5b4b27a07 100644 --- a/Lib/test/clinic.test.c +++ b/Lib/test/clinic.test.c @@ -1020,12 +1020,19 @@ test_unsigned_char_converter(PyObject *module, PyObject *const *args, Py_ssize_t goto skip_optional; } { - unsigned long ival = PyLong_AsUnsignedLongMask(args[2]); - if (ival == (unsigned long)-1 && PyErr_Occurred()) { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[2], &c, sizeof(unsigned char), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { goto exit; } - else { - c = (unsigned char) ival; + if ((size_t)_bytes > sizeof(unsigned char)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } } } skip_optional: @@ -1038,7 +1045,7 @@ exit: static PyObject * test_unsigned_char_converter_impl(PyObject *module, unsigned char a, unsigned char b, unsigned char c) -/*[clinic end generated code: output=45920dbedc22eb55 input=021414060993e289]*/ +/*[clinic end generated code: output=49eda9faaf53372a input=021414060993e289]*/ /*[clinic input] @@ -1151,9 +1158,21 @@ test_unsigned_short_converter(PyObject *module, PyObject *const *args, Py_ssize_ if (nargs < 3) { goto skip_optional; } - c = (unsigned short)PyLong_AsUnsignedLongMask(args[2]); - if (c == (unsigned short)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[2], &c, sizeof(unsigned short), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned short)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } skip_optional: return_value = test_unsigned_short_converter_impl(module, a, b, c); @@ -1165,7 +1184,7 @@ exit: static PyObject * test_unsigned_short_converter_impl(PyObject *module, unsigned short a, unsigned short b, unsigned short c) -/*[clinic end generated code: output=e6e990df729114fc input=cdfd8eff3d9176b4]*/ +/*[clinic end generated code: output=f591c7797e150f49 input=cdfd8eff3d9176b4]*/ /*[clinic input] @@ -1298,9 +1317,21 @@ test_unsigned_int_converter(PyObject *module, PyObject *const *args, Py_ssize_t if (nargs < 3) { goto skip_optional; } - c = (unsigned int)PyLong_AsUnsignedLongMask(args[2]); - if (c == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[2], &c, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } skip_optional: return_value = test_unsigned_int_converter_impl(module, a, b, c); @@ -1312,7 +1343,7 @@ exit: static PyObject * test_unsigned_int_converter_impl(PyObject *module, unsigned int a, unsigned int b, unsigned int c) -/*[clinic end generated code: output=f9cdbe410ccc98a3 input=5533534828b62fc0]*/ +/*[clinic end generated code: output=50a413f1cc82dc11 input=5533534828b62fc0]*/ /*[clinic input] @@ -1414,7 +1445,22 @@ test_unsigned_long_converter(PyObject *module, PyObject *const *args, Py_ssize_t _PyArg_BadArgument("test_unsigned_long_converter", "argument 3", "int", args[2]); goto exit; } - c = PyLong_AsUnsignedLongMask(args[2]); + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[2], &c, sizeof(unsigned long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } skip_optional: return_value = test_unsigned_long_converter_impl(module, a, b, c); @@ -1425,7 +1471,7 @@ exit: static PyObject * test_unsigned_long_converter_impl(PyObject *module, unsigned long a, unsigned long b, unsigned long c) -/*[clinic end generated code: output=d74eed227d77a31b input=f450d94cae1ef73b]*/ +/*[clinic end generated code: output=1bbf5620093cc914 input=f450d94cae1ef73b]*/ /*[clinic input] @@ -1529,7 +1575,22 @@ test_unsigned_long_long_converter(PyObject *module, PyObject *const *args, Py_ss _PyArg_BadArgument("test_unsigned_long_long_converter", "argument 3", "int", args[2]); goto exit; } - c = PyLong_AsUnsignedLongLongMask(args[2]); + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[2], &c, sizeof(unsigned long long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned long long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } skip_optional: return_value = test_unsigned_long_long_converter_impl(module, a, b, c); @@ -1542,7 +1603,7 @@ test_unsigned_long_long_converter_impl(PyObject *module, unsigned long long a, unsigned long long b, unsigned long long c) -/*[clinic end generated code: output=5ca4e4dfb3db644b input=a15115dc41866ff4]*/ +/*[clinic end generated code: output=582a6623dc845824 input=a15115dc41866ff4]*/ /*[clinic input] diff --git a/Lib/test/test_capi/test_getargs.py b/Lib/test/test_capi/test_getargs.py index 67a8da75995..dc200008968 100644 --- a/Lib/test/test_capi/test_getargs.py +++ b/Lib/test/test_capi/test_getargs.py @@ -48,8 +48,8 @@ except ImportError: LARGE = 0x7FFFFFFF VERY_LARGE = 0xFF0000121212121212121242 -from _testcapi import UCHAR_MAX, USHRT_MAX, UINT_MAX, ULONG_MAX, INT_MAX, \ - INT_MIN, LONG_MIN, LONG_MAX, PY_SSIZE_T_MIN, PY_SSIZE_T_MAX, \ +from _testcapi import UCHAR_MAX, USHRT_MAX, UINT_MAX, ULONG_MAX, ULLONG_MAX, INT_MAX, \ + INT_MIN, LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX, PY_SSIZE_T_MIN, PY_SSIZE_T_MAX, \ SHRT_MIN, SHRT_MAX, FLT_MIN, FLT_MAX, DBL_MIN, DBL_MAX DBL_MAX_EXP = sys.float_info.max_exp @@ -57,9 +57,8 @@ INF = float('inf') NAN = float('nan') # fake, they are not defined in Python's header files -LLONG_MAX = 2**63-1 -LLONG_MIN = -2**63 -ULLONG_MAX = 2**64-1 +SCHAR_MAX = UCHAR_MAX // 2 +SCHAR_MIN = SCHAR_MAX - UCHAR_MAX NULL = None @@ -209,10 +208,23 @@ class Unsigned_TestCase(unittest.TestCase): self.assertEqual(UCHAR_MAX, getargs_B(-1)) self.assertEqual(0, getargs_B(0)) self.assertEqual(UCHAR_MAX, getargs_B(UCHAR_MAX)) - self.assertEqual(0, getargs_B(UCHAR_MAX+1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(0, getargs_B(UCHAR_MAX+1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(1, getargs_B(-UCHAR_MAX)) + self.assertEqual(SCHAR_MAX+1, getargs_B(SCHAR_MIN)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(SCHAR_MAX, getargs_B(SCHAR_MIN-1)) + + self.assertEqual(128, getargs_B(-2**7)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(127, getargs_B(-2**7-1)) self.assertEqual(42, getargs_B(42)) - self.assertEqual(UCHAR_MAX & VERY_LARGE, getargs_B(VERY_LARGE)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(UCHAR_MAX & VERY_LARGE, getargs_B(VERY_LARGE)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(UCHAR_MAX & -VERY_LARGE, getargs_B(-VERY_LARGE)) def test_H(self): from _testcapi import getargs_H @@ -233,11 +245,18 @@ class Unsigned_TestCase(unittest.TestCase): self.assertEqual(USHRT_MAX, getargs_H(-1)) self.assertEqual(0, getargs_H(0)) self.assertEqual(USHRT_MAX, getargs_H(USHRT_MAX)) - self.assertEqual(0, getargs_H(USHRT_MAX+1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(0, getargs_H(USHRT_MAX+1)) + self.assertEqual(SHRT_MAX+1, getargs_H(SHRT_MIN)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(SHRT_MAX, getargs_H(SHRT_MIN-1)) self.assertEqual(42, getargs_H(42)) - self.assertEqual(VERY_LARGE & USHRT_MAX, getargs_H(VERY_LARGE)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(USHRT_MAX & VERY_LARGE, getargs_H(VERY_LARGE)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(USHRT_MAX & -VERY_LARGE, getargs_H(-VERY_LARGE)) def test_I(self): from _testcapi import getargs_I @@ -258,11 +277,18 @@ class Unsigned_TestCase(unittest.TestCase): self.assertEqual(UINT_MAX, getargs_I(-1)) self.assertEqual(0, getargs_I(0)) self.assertEqual(UINT_MAX, getargs_I(UINT_MAX)) - self.assertEqual(0, getargs_I(UINT_MAX+1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(0, getargs_I(UINT_MAX+1)) + self.assertEqual(INT_MAX+1, getargs_I(INT_MIN)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(INT_MAX, getargs_I(INT_MIN-1)) self.assertEqual(42, getargs_I(42)) - self.assertEqual(VERY_LARGE & UINT_MAX, getargs_I(VERY_LARGE)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(UINT_MAX & VERY_LARGE, getargs_I(VERY_LARGE)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(UINT_MAX & -VERY_LARGE, getargs_I(-VERY_LARGE)) def test_k(self): from _testcapi import getargs_k @@ -283,11 +309,18 @@ class Unsigned_TestCase(unittest.TestCase): self.assertEqual(ULONG_MAX, getargs_k(-1)) self.assertEqual(0, getargs_k(0)) self.assertEqual(ULONG_MAX, getargs_k(ULONG_MAX)) - self.assertEqual(0, getargs_k(ULONG_MAX+1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(0, getargs_k(ULONG_MAX+1)) + self.assertEqual(LONG_MAX+1, getargs_k(LONG_MIN)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(LONG_MAX, getargs_k(LONG_MIN-1)) self.assertEqual(42, getargs_k(42)) - self.assertEqual(VERY_LARGE & ULONG_MAX, getargs_k(VERY_LARGE)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ULONG_MAX & VERY_LARGE, getargs_k(VERY_LARGE)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ULONG_MAX & -VERY_LARGE, getargs_k(-VERY_LARGE)) class Signed_TestCase(unittest.TestCase): def test_h(self): @@ -434,11 +467,18 @@ class LongLong_TestCase(unittest.TestCase): self.assertEqual(ULLONG_MAX, getargs_K(ULLONG_MAX)) self.assertEqual(0, getargs_K(0)) self.assertEqual(ULLONG_MAX, getargs_K(ULLONG_MAX)) - self.assertEqual(0, getargs_K(ULLONG_MAX+1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(0, getargs_K(ULLONG_MAX+1)) + self.assertEqual(LLONG_MAX+1, getargs_K(LLONG_MIN)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(LLONG_MAX, getargs_K(LLONG_MIN-1)) self.assertEqual(42, getargs_K(42)) - self.assertEqual(VERY_LARGE & ULLONG_MAX, getargs_K(VERY_LARGE)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ULLONG_MAX & VERY_LARGE, getargs_K(VERY_LARGE)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ULLONG_MAX & -VERY_LARGE, getargs_K(-VERY_LARGE)) class Float_TestCase(unittest.TestCase, FloatsAreIdenticalMixin): diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 580d54e0eb0..a6ed8879992 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -3048,6 +3048,8 @@ class ClinicFunctionalTest(unittest.TestCase): def test_unsigned_char_converter(self): from _testcapi import UCHAR_MAX + SCHAR_MAX = UCHAR_MAX // 2 + SCHAR_MIN = SCHAR_MAX - UCHAR_MAX with self.assertRaises(OverflowError): ac_tester.unsigned_char_converter(-1) with self.assertRaises(OverflowError): @@ -3057,8 +3059,13 @@ class ClinicFunctionalTest(unittest.TestCase): with self.assertRaises(TypeError): ac_tester.unsigned_char_converter([]) self.assertEqual(ac_tester.unsigned_char_converter(), (12, 34, 56)) - self.assertEqual(ac_tester.unsigned_char_converter(0, 0, UCHAR_MAX + 1), (0, 0, 0)) - self.assertEqual(ac_tester.unsigned_char_converter(0, 0, (UCHAR_MAX + 1) * 3 + 123), (0, 0, 123)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_char_converter(0, 0, UCHAR_MAX + 1), (0, 0, 0)) + self.assertEqual(ac_tester.unsigned_char_converter(0, 0, SCHAR_MIN), (0, 0, SCHAR_MAX + 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_char_converter(0, 0, SCHAR_MIN - 1), (0, 0, SCHAR_MAX)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_char_converter(0, 0, (UCHAR_MAX + 1) * 3 + 123), (0, 0, 123)) def test_short_converter(self): from _testcapi import SHRT_MIN, SHRT_MAX @@ -3072,7 +3079,7 @@ class ClinicFunctionalTest(unittest.TestCase): self.assertEqual(ac_tester.short_converter(4321), (4321,)) def test_unsigned_short_converter(self): - from _testcapi import USHRT_MAX + from _testcapi import SHRT_MIN, SHRT_MAX, USHRT_MAX with self.assertRaises(ValueError): ac_tester.unsigned_short_converter(-1) with self.assertRaises(OverflowError): @@ -3082,8 +3089,13 @@ class ClinicFunctionalTest(unittest.TestCase): with self.assertRaises(TypeError): ac_tester.unsigned_short_converter([]) self.assertEqual(ac_tester.unsigned_short_converter(), (12, 34, 56)) - self.assertEqual(ac_tester.unsigned_short_converter(0, 0, USHRT_MAX + 1), (0, 0, 0)) - self.assertEqual(ac_tester.unsigned_short_converter(0, 0, (USHRT_MAX + 1) * 3 + 123), (0, 0, 123)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_short_converter(0, 0, USHRT_MAX + 1), (0, 0, 0)) + self.assertEqual(ac_tester.unsigned_short_converter(0, 0, SHRT_MIN), (0, 0, SHRT_MAX + 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_short_converter(0, 0, SHRT_MIN - 1), (0, 0, SHRT_MAX)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_short_converter(0, 0, (USHRT_MAX + 1) * 3 + 123), (0, 0, 123)) def test_int_converter(self): from _testcapi import INT_MIN, INT_MAX @@ -3099,7 +3111,7 @@ class ClinicFunctionalTest(unittest.TestCase): self.assertEqual(ac_tester.int_converter(1, 2, '3'), (1, 2, ord('3'))) def test_unsigned_int_converter(self): - from _testcapi import UINT_MAX + from _testcapi import INT_MIN, INT_MAX, UINT_MAX with self.assertRaises(ValueError): ac_tester.unsigned_int_converter(-1) with self.assertRaises(OverflowError): @@ -3109,8 +3121,13 @@ class ClinicFunctionalTest(unittest.TestCase): with self.assertRaises(TypeError): ac_tester.unsigned_int_converter([]) self.assertEqual(ac_tester.unsigned_int_converter(), (12, 34, 56)) - self.assertEqual(ac_tester.unsigned_int_converter(0, 0, UINT_MAX + 1), (0, 0, 0)) - self.assertEqual(ac_tester.unsigned_int_converter(0, 0, (UINT_MAX + 1) * 3 + 123), (0, 0, 123)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_int_converter(0, 0, UINT_MAX + 1), (0, 0, 0)) + self.assertEqual(ac_tester.unsigned_int_converter(0, 0, INT_MIN), (0, 0, INT_MAX + 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_int_converter(0, 0, INT_MIN - 1), (0, 0, INT_MAX)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_int_converter(0, 0, (UINT_MAX + 1) * 3 + 123), (0, 0, 123)) def test_long_converter(self): from _testcapi import LONG_MIN, LONG_MAX @@ -3124,7 +3141,7 @@ class ClinicFunctionalTest(unittest.TestCase): self.assertEqual(ac_tester.long_converter(-1234), (-1234,)) def test_unsigned_long_converter(self): - from _testcapi import ULONG_MAX + from _testcapi import LONG_MIN, LONG_MAX, ULONG_MAX with self.assertRaises(ValueError): ac_tester.unsigned_long_converter(-1) with self.assertRaises(OverflowError): @@ -3134,8 +3151,13 @@ class ClinicFunctionalTest(unittest.TestCase): with self.assertRaises(TypeError): ac_tester.unsigned_long_converter([]) self.assertEqual(ac_tester.unsigned_long_converter(), (12, 34, 56)) - self.assertEqual(ac_tester.unsigned_long_converter(0, 0, ULONG_MAX + 1), (0, 0, 0)) - self.assertEqual(ac_tester.unsigned_long_converter(0, 0, (ULONG_MAX + 1) * 3 + 123), (0, 0, 123)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_long_converter(0, 0, ULONG_MAX + 1), (0, 0, 0)) + self.assertEqual(ac_tester.unsigned_long_converter(0, 0, LONG_MIN), (0, 0, LONG_MAX + 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_long_converter(0, 0, LONG_MIN - 1), (0, 0, LONG_MAX)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_long_converter(0, 0, (ULONG_MAX + 1) * 3 + 123), (0, 0, 123)) def test_long_long_converter(self): from _testcapi import LLONG_MIN, LLONG_MAX @@ -3149,7 +3171,7 @@ class ClinicFunctionalTest(unittest.TestCase): self.assertEqual(ac_tester.long_long_converter(-1234), (-1234,)) def test_unsigned_long_long_converter(self): - from _testcapi import ULLONG_MAX + from _testcapi import LLONG_MIN, LLONG_MAX, ULLONG_MAX with self.assertRaises(ValueError): ac_tester.unsigned_long_long_converter(-1) with self.assertRaises(OverflowError): @@ -3159,8 +3181,13 @@ class ClinicFunctionalTest(unittest.TestCase): with self.assertRaises(TypeError): ac_tester.unsigned_long_long_converter([]) self.assertEqual(ac_tester.unsigned_long_long_converter(), (12, 34, 56)) - self.assertEqual(ac_tester.unsigned_long_long_converter(0, 0, ULLONG_MAX + 1), (0, 0, 0)) - self.assertEqual(ac_tester.unsigned_long_long_converter(0, 0, (ULLONG_MAX + 1) * 3 + 123), (0, 0, 123)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_long_long_converter(0, 0, ULLONG_MAX + 1), (0, 0, 0)) + self.assertEqual(ac_tester.unsigned_long_long_converter(0, 0, LLONG_MIN), (0, 0, LLONG_MAX + 1)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_long_long_converter(0, 0, LLONG_MIN - 1), (0, 0, LLONG_MAX)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(ac_tester.unsigned_long_long_converter(0, 0, (ULLONG_MAX + 1) * 3 + 123), (0, 0, 123)) def test_py_ssize_t_converter(self): from _testcapi import PY_SSIZE_T_MIN, PY_SSIZE_T_MAX diff --git a/Misc/NEWS.d/next/C_API/2025-04-17-12-37-27.gh-issue-132629.01ArwX.rst b/Misc/NEWS.d/next/C_API/2025-04-17-12-37-27.gh-issue-132629.01ArwX.rst new file mode 100644 index 00000000000..38b7a0a493e --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2025-04-17-12-37-27.gh-issue-132629.01ArwX.rst @@ -0,0 +1,4 @@ +For unsigned integer formats in :c:func:`PyArg_ParseTuple`, accepting Python +integers with value that is larger than the maximal value for the C type or +less than the minimal value for the corresponding signed integer type +of the same size is now deprecated. diff --git a/Modules/clinic/_cursesmodule.c.h b/Modules/clinic/_cursesmodule.c.h index 49c864318c8..a8c32d65106 100644 --- a/Modules/clinic/_cursesmodule.c.h +++ b/Modules/clinic/_cursesmodule.c.h @@ -2372,7 +2372,22 @@ _curses_ungetmouse(PyObject *module, PyObject *const *args, Py_ssize_t nargs) _PyArg_BadArgument("ungetmouse", "argument 5", "int", args[4]); goto exit; } - bstate = PyLong_AsUnsignedLongMask(args[4]); + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[4], &bstate, sizeof(unsigned long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } return_value = _curses_ungetmouse_impl(module, id, x, y, z, bstate); exit: @@ -3138,7 +3153,22 @@ _curses_mousemask(PyObject *module, PyObject *arg) _PyArg_BadArgument("mousemask", "argument", "int", arg); goto exit; } - newmask = PyLong_AsUnsignedLongMask(arg); + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(arg, &newmask, sizeof(unsigned long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } return_value = _curses_mousemask_impl(module, newmask); exit: @@ -4420,4 +4450,4 @@ _curses_has_extended_color_support(PyObject *module, PyObject *Py_UNUSED(ignored #ifndef _CURSES_ASSUME_DEFAULT_COLORS_METHODDEF #define _CURSES_ASSUME_DEFAULT_COLORS_METHODDEF #endif /* !defined(_CURSES_ASSUME_DEFAULT_COLORS_METHODDEF) */ -/*[clinic end generated code: output=a083473003179b30 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=79ddaae4da3b80df input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_testclinic.c.h b/Modules/clinic/_testclinic.c.h index 970528ce9ea..68c92a86226 100644 --- a/Modules/clinic/_testclinic.c.h +++ b/Modules/clinic/_testclinic.c.h @@ -746,12 +746,19 @@ unsigned_char_converter(PyObject *module, PyObject *const *args, Py_ssize_t narg goto skip_optional; } { - unsigned long ival = PyLong_AsUnsignedLongMask(args[2]); - if (ival == (unsigned long)-1 && PyErr_Occurred()) { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[2], &c, sizeof(unsigned char), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { goto exit; } - else { - c = (unsigned char) ival; + if ((size_t)_bytes > sizeof(unsigned char)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } } } skip_optional: @@ -848,9 +855,21 @@ unsigned_short_converter(PyObject *module, PyObject *const *args, Py_ssize_t nar if (nargs < 3) { goto skip_optional; } - c = (unsigned short)PyLong_AsUnsignedLongMask(args[2]); - if (c == (unsigned short)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[2], &c, sizeof(unsigned short), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned short)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } skip_optional: return_value = unsigned_short_converter_impl(module, a, b, c); @@ -955,9 +974,21 @@ unsigned_int_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs if (nargs < 3) { goto skip_optional; } - c = (unsigned int)PyLong_AsUnsignedLongMask(args[2]); - if (c == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[2], &c, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } skip_optional: return_value = unsigned_int_converter_impl(module, a, b, c); @@ -1042,7 +1073,22 @@ unsigned_long_converter(PyObject *module, PyObject *const *args, Py_ssize_t narg _PyArg_BadArgument("unsigned_long_converter", "argument 3", "int", args[2]); goto exit; } - c = PyLong_AsUnsignedLongMask(args[2]); + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[2], &c, sizeof(unsigned long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } skip_optional: return_value = unsigned_long_converter_impl(module, a, b, c); @@ -1126,7 +1172,22 @@ unsigned_long_long_converter(PyObject *module, PyObject *const *args, Py_ssize_t _PyArg_BadArgument("unsigned_long_long_converter", "argument 3", "int", args[2]); goto exit; } - c = PyLong_AsUnsignedLongLongMask(args[2]); + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[2], &c, sizeof(unsigned long long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned long long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } skip_optional: return_value = unsigned_long_long_converter_impl(module, a, b, c); @@ -4481,4 +4542,4 @@ _testclinic_TestClass_posonly_poskw_varpos_array_no_fastcall(PyObject *type, PyO exit: return return_value; } -/*[clinic end generated code: output=84ffc31f27215baa input=a9049054013a1b77]*/ +/*[clinic end generated code: output=6b04671afdafbecf input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_zoneinfo.c.h b/Modules/clinic/_zoneinfo.c.h index 09ac157cbfd..19564a6c13f 100644 --- a/Modules/clinic/_zoneinfo.c.h +++ b/Modules/clinic/_zoneinfo.c.h @@ -434,12 +434,19 @@ zoneinfo_ZoneInfo__unpickle(PyObject *type, PyTypeObject *cls, PyObject *const * } key = args[0]; { - unsigned long ival = PyLong_AsUnsignedLongMask(args[1]); - if (ival == (unsigned long)-1 && PyErr_Occurred()) { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &from_cache, sizeof(unsigned char), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { goto exit; } - else { - from_cache = (unsigned char) ival; + if ((size_t)_bytes > sizeof(unsigned char)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } } } return_value = zoneinfo_ZoneInfo__unpickle_impl((PyTypeObject *)type, cls, key, from_cache); @@ -447,4 +454,4 @@ zoneinfo_ZoneInfo__unpickle(PyObject *type, PyTypeObject *cls, PyObject *const * exit: return return_value; } -/*[clinic end generated code: output=8e9e204f390261b9 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c6df04d7b400bd7f input=a9049054013a1b77]*/ diff --git a/Modules/clinic/binascii.c.h b/Modules/clinic/binascii.c.h index 602e42a4c1a..ce29e0d11a4 100644 --- a/Modules/clinic/binascii.c.h +++ b/Modules/clinic/binascii.c.h @@ -292,9 +292,21 @@ binascii_crc_hqx(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (PyObject_GetBuffer(args[0], &data, PyBUF_SIMPLE) != 0) { goto exit; } - crc = (unsigned int)PyLong_AsUnsignedLongMask(args[1]); - if (crc == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &crc, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } return_value = binascii_crc_hqx_impl(module, &data, crc); @@ -336,9 +348,21 @@ binascii_crc32(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (nargs < 2) { goto skip_optional; } - crc = (unsigned int)PyLong_AsUnsignedLongMask(args[1]); - if (crc == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &crc, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } skip_optional: _return_value = binascii_crc32_impl(module, &data, crc); @@ -788,4 +812,4 @@ exit: return return_value; } -/*[clinic end generated code: output=adb855a2797c3cad input=a9049054013a1b77]*/ +/*[clinic end generated code: output=fba6a71e0d7d092f input=a9049054013a1b77]*/ diff --git a/Modules/clinic/fcntlmodule.c.h b/Modules/clinic/fcntlmodule.c.h index 00a929064ba..005e9b9e12a 100644 --- a/Modules/clinic/fcntlmodule.c.h +++ b/Modules/clinic/fcntlmodule.c.h @@ -124,7 +124,22 @@ fcntl_ioctl(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyErr_Format(PyExc_TypeError, "ioctl() argument 2 must be int, not %T", args[1]); goto exit; } - code = PyLong_AsUnsignedLongMask(args[1]); + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &code, sizeof(unsigned long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } if (nargs < 3) { goto skip_optional; } @@ -264,4 +279,4 @@ skip_optional: exit: return return_value; } -/*[clinic end generated code: output=65a16bc64c7b4de4 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=bf84289b741e7cf6 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 3621a062541..22f426c5192 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -844,7 +844,22 @@ os_chflags(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject * _PyArg_BadArgument("chflags", "argument 'flags'", "int", args[1]); goto exit; } - flags = PyLong_AsUnsignedLongMask(args[1]); + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &flags, sizeof(unsigned long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } if (!noptargs) { goto skip_optional_pos; } @@ -928,7 +943,22 @@ os_lchflags(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject _PyArg_BadArgument("lchflags", "argument 'flags'", "int", args[1]); goto exit; } - flags = PyLong_AsUnsignedLongMask(args[1]); + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &flags, sizeof(unsigned long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } return_value = os_lchflags_impl(module, &path, flags); exit: @@ -11373,9 +11403,21 @@ os_memfd_create(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj if (!noptargs) { goto skip_optional_pos; } - flags = (unsigned int)PyLong_AsUnsignedLongMask(args[1]); - if (flags == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &flags, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } skip_optional_pos: return_value = os_memfd_create_impl(module, name, flags); @@ -13398,4 +13440,4 @@ os__emscripten_debugger(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef OS__EMSCRIPTEN_DEBUGGER_METHODDEF #define OS__EMSCRIPTEN_DEBUGGER_METHODDEF #endif /* !defined(OS__EMSCRIPTEN_DEBUGGER_METHODDEF) */ -/*[clinic end generated code: output=ae64df0389746258 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=5341daae6581a62b input=a9049054013a1b77]*/ diff --git a/Modules/clinic/selectmodule.c.h b/Modules/clinic/selectmodule.c.h index 253ad8c9e78..c0a5f678ad0 100644 --- a/Modules/clinic/selectmodule.c.h +++ b/Modules/clinic/selectmodule.c.h @@ -783,9 +783,21 @@ select_epoll_register(PyObject *self, PyObject *const *args, Py_ssize_t nargs, P if (!noptargs) { goto skip_optional_pos; } - eventmask = (unsigned int)PyLong_AsUnsignedLongMask(args[1]); - if (eventmask == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &eventmask, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } skip_optional_pos: return_value = select_epoll_register_impl((pyEpoll_Object *)self, fd, eventmask); @@ -860,9 +872,21 @@ select_epoll_modify(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyO if (fd < 0) { goto exit; } - eventmask = (unsigned int)PyLong_AsUnsignedLongMask(args[1]); - if (eventmask == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &eventmask, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } return_value = select_epoll_modify_impl((pyEpoll_Object *)self, fd, eventmask); @@ -1375,4 +1399,4 @@ exit: #ifndef SELECT_KQUEUE_CONTROL_METHODDEF #define SELECT_KQUEUE_CONTROL_METHODDEF #endif /* !defined(SELECT_KQUEUE_CONTROL_METHODDEF) */ -/*[clinic end generated code: output=6fc20d78802511d1 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2a66dd831f22c696 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/signalmodule.c.h b/Modules/clinic/signalmodule.c.h index 955861b4da3..b0cd9e2e561 100644 --- a/Modules/clinic/signalmodule.c.h +++ b/Modules/clinic/signalmodule.c.h @@ -660,7 +660,22 @@ signal_pthread_kill(PyObject *module, PyObject *const *args, Py_ssize_t nargs) _PyArg_BadArgument("pthread_kill", "argument 1", "int", args[0]); goto exit; } - thread_id = PyLong_AsUnsignedLongMask(args[0]); + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[0], &thread_id, sizeof(unsigned long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } signalnum = PyLong_AsInt(args[1]); if (signalnum == -1 && PyErr_Occurred()) { goto exit; @@ -779,4 +794,4 @@ exit: #ifndef SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF #define SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF #endif /* !defined(SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF) */ -/*[clinic end generated code: output=48bfaffeb25df5d2 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=37ae8ebeae4178fa input=a9049054013a1b77]*/ diff --git a/Modules/clinic/zlibmodule.c.h b/Modules/clinic/zlibmodule.c.h index 146a7e25001..016af258d63 100644 --- a/Modules/clinic/zlibmodule.c.h +++ b/Modules/clinic/zlibmodule.c.h @@ -1028,9 +1028,21 @@ zlib_adler32(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (nargs < 2) { goto skip_optional; } - value = (unsigned int)PyLong_AsUnsignedLongMask(args[1]); - if (value == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &value, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } skip_optional: return_value = zlib_adler32_impl(module, &data, value); @@ -1080,13 +1092,37 @@ zlib_adler32_combine(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (!_PyArg_CheckPositional("adler32_combine", nargs, 3, 3)) { goto exit; } - adler1 = (unsigned int)PyLong_AsUnsignedLongMask(args[0]); - if (adler1 == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[0], &adler1, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } - adler2 = (unsigned int)PyLong_AsUnsignedLongMask(args[1]); - if (adler2 == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &adler2, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } if (!PyLong_Check(args[2])) { _PyArg_BadArgument("adler32_combine", "argument 3", "int", args[2]); @@ -1137,9 +1173,21 @@ zlib_crc32(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (nargs < 2) { goto skip_optional; } - value = (unsigned int)PyLong_AsUnsignedLongMask(args[1]); - if (value == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &value, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } skip_optional: _return_value = zlib_crc32_impl(module, &data, value); @@ -1193,13 +1241,37 @@ zlib_crc32_combine(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (!_PyArg_CheckPositional("crc32_combine", nargs, 3, 3)) { goto exit; } - crc1 = (unsigned int)PyLong_AsUnsignedLongMask(args[0]); - if (crc1 == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[0], &crc1, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } - crc2 = (unsigned int)PyLong_AsUnsignedLongMask(args[1]); - if (crc2 == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &crc2, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } if (!PyLong_Check(args[2])) { _PyArg_BadArgument("crc32_combine", "argument 3", "int", args[2]); @@ -1239,4 +1311,4 @@ exit: #ifndef ZLIB_DECOMPRESS___DEEPCOPY___METHODDEF #define ZLIB_DECOMPRESS___DEEPCOPY___METHODDEF #endif /* !defined(ZLIB_DECOMPRESS___DEEPCOPY___METHODDEF) */ -/*[clinic end generated code: output=3f7692eb3b5d5a0c input=a9049054013a1b77]*/ +/*[clinic end generated code: output=3054c8894aa44568 input=a9049054013a1b77]*/ diff --git a/Modules/fcntlmodule.c b/Modules/fcntlmodule.c index 90363b9dca3..524eb54b984 100644 --- a/Modules/fcntlmodule.c +++ b/Modules/fcntlmodule.c @@ -1,9 +1,9 @@ /* fcntl module */ -// Need limited C API version 3.13 for PyLong_AsInt() +// Need limited C API version 3.14 for PyLong_AsNativeBytes() in AC code #include "pyconfig.h" // Py_GIL_DISABLED #ifndef Py_GIL_DISABLED -# define Py_LIMITED_API 0x030d0000 +# define Py_LIMITED_API 0x030e0000 #endif #include "Python.h" diff --git a/PC/clinic/msvcrtmodule.c.h b/PC/clinic/msvcrtmodule.c.h index a77d0855af2..647aadfa46b 100644 --- a/PC/clinic/msvcrtmodule.c.h +++ b/PC/clinic/msvcrtmodule.c.h @@ -690,9 +690,21 @@ msvcrt_SetErrorMode(PyObject *module, PyObject *arg) PyObject *return_value = NULL; unsigned int mode; - mode = (unsigned int)PyLong_AsUnsignedLongMask(arg); - if (mode == (unsigned int)-1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(arg, &mode, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } } return_value = msvcrt_SetErrorMode_impl(module, mode); @@ -731,4 +743,4 @@ exit: #ifndef MSVCRT_GETERRORMODE_METHODDEF #define MSVCRT_GETERRORMODE_METHODDEF #endif /* !defined(MSVCRT_GETERRORMODE_METHODDEF) */ -/*[clinic end generated code: output=692c6f52bb9193ce input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f67eaf745685429d input=a9049054013a1b77]*/ diff --git a/Python/getargs.c b/Python/getargs.c index 0cf596285cc..02f5c0e37ee 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -734,11 +734,20 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, values allowed */ unsigned char *p = va_arg(*p_va, unsigned char *); HANDLE_NULLABLE; - unsigned long ival = PyLong_AsUnsignedLongMask(arg); - if (ival == (unsigned long)-1 && PyErr_Occurred()) + Py_ssize_t bytes = PyLong_AsNativeBytes(arg, p, sizeof(unsigned char), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (bytes < 0) { RETURN_ERR_OCCURRED; - else - *p = (unsigned char) ival; + } + if ((size_t)bytes > sizeof(unsigned char)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + RETURN_ERR_OCCURRED; + } + } break; } @@ -767,11 +776,20 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, unsigned allowed */ unsigned short *p = va_arg(*p_va, unsigned short *); HANDLE_NULLABLE; - unsigned long ival = PyLong_AsUnsignedLongMask(arg); - if (ival == (unsigned long)-1 && PyErr_Occurred()) + Py_ssize_t bytes = PyLong_AsNativeBytes(arg, p, sizeof(unsigned short), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (bytes < 0) { RETURN_ERR_OCCURRED; - else - *p = (unsigned short) ival; + } + if ((size_t)bytes > sizeof(unsigned short)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + RETURN_ERR_OCCURRED; + } + } break; } @@ -800,11 +818,20 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, unsigned allowed */ unsigned int *p = va_arg(*p_va, unsigned int *); HANDLE_NULLABLE; - unsigned long ival = PyLong_AsUnsignedLongMask(arg); - if (ival == (unsigned long)-1 && PyErr_Occurred()) + Py_ssize_t bytes = PyLong_AsNativeBytes(arg, p, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (bytes < 0) { RETURN_ERR_OCCURRED; - else - *p = (unsigned int) ival; + } + if ((size_t)bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + RETURN_ERR_OCCURRED; + } + } break; } @@ -838,15 +865,23 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, case 'k': { /* long sized bitfield */ unsigned long *p = va_arg(*p_va, unsigned long *); HANDLE_NULLABLE; - unsigned long ival; if (!PyIndex_Check(arg)) { return converterr(nullable, "int", arg, msgbuf, bufsize); } - ival = PyLong_AsUnsignedLongMask(arg); - if (ival == (unsigned long)(long)-1 && PyErr_Occurred()) { + Py_ssize_t bytes = PyLong_AsNativeBytes(arg, p, sizeof(unsigned long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (bytes < 0) { RETURN_ERR_OCCURRED; } - *p = ival; + if ((size_t)bytes > sizeof(unsigned long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + RETURN_ERR_OCCURRED; + } + } break; } @@ -864,15 +899,23 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, case 'K': { /* long long sized bitfield */ unsigned long long *p = va_arg(*p_va, unsigned long long *); HANDLE_NULLABLE; - unsigned long long ival; if (!PyIndex_Check(arg)) { return converterr(nullable, "int", arg, msgbuf, bufsize); } - ival = PyLong_AsUnsignedLongLongMask(arg); - if (ival == (unsigned long long)(long long)-1 && PyErr_Occurred()) { + Py_ssize_t bytes = PyLong_AsNativeBytes(arg, p, sizeof(unsigned long long), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (bytes < 0) { RETURN_ERR_OCCURRED; } - *p = ival; + if ((size_t)bytes > sizeof(unsigned long long)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + RETURN_ERR_OCCURRED; + } + } break; } diff --git a/Tools/clinic/libclinic/converters.py b/Tools/clinic/libclinic/converters.py index 39d0ac557a6..6e89e8de7cc 100644 --- a/Tools/clinic/libclinic/converters.py +++ b/Tools/clinic/libclinic/converters.py @@ -18,6 +18,7 @@ TypeSet = set[bltns.type[object]] class BaseUnsignedIntConverter(CConverter): + bitwise = False def use_converter(self) -> None: if self.converter: @@ -25,6 +26,38 @@ class BaseUnsignedIntConverter(CConverter): f'{self.converter}()') def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: + if self.bitwise: + result = self.format_code(""" + {{{{ + Py_ssize_t _bytes = PyLong_AsNativeBytes({argname}, &{paramname}, sizeof({type}), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) {{{{ + goto exit; + }}}} + if ((size_t)_bytes > sizeof({type})) {{{{ + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + {{{{ + goto exit; + }}}} + }}}} + }}}} + """, + argname=argname, + type=self.type, + bad_argument=self.bad_argument(displayname, 'int', limited_capi=limited_capi)) + if self.format_unit in ('k', 'K'): + result = self.format_code(""" + if (!PyIndex_Check({argname})) {{{{ + {bad_argument} + goto exit; + }}}}""", + argname=argname, + bad_argument=self.bad_argument(displayname, 'int', limited_capi=limited_capi)) + result + return result + if not limited_capi: return super().parse_arg(argname, displayname, limited_capi=limited_capi) return self.format_code(""" @@ -172,13 +205,14 @@ class char_converter(CConverter): @add_legacy_c_converter('B', bitwise=True) -class unsigned_char_converter(CConverter): +class unsigned_char_converter(BaseUnsignedIntConverter): type = 'unsigned char' default_type = int format_unit = 'b' c_ignored_default = "'\0'" def converter_init(self, *, bitwise: bool = False) -> None: + self.bitwise = bitwise if bitwise: self.format_unit = 'B' @@ -206,19 +240,6 @@ class unsigned_char_converter(CConverter): }}}} """, argname=argname) - elif self.format_unit == 'B': - return self.format_code(""" - {{{{ - unsigned long ival = PyLong_AsUnsignedLongMask({argname}); - if (ival == (unsigned long)-1 && PyErr_Occurred()) {{{{ - goto exit; - }}}} - else {{{{ - {paramname} = (unsigned char) ival; - }}}} - }}}} - """, - argname=argname) return super().parse_arg(argname, displayname, limited_capi=limited_capi) @@ -265,22 +286,12 @@ class unsigned_short_converter(BaseUnsignedIntConverter): c_ignored_default = "0" def converter_init(self, *, bitwise: bool = False) -> None: + self.bitwise = bitwise if bitwise: self.format_unit = 'H' else: self.converter = '_PyLong_UnsignedShort_Converter' - def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: - if self.format_unit == 'H': - return self.format_code(""" - {paramname} = (unsigned short)PyLong_AsUnsignedLongMask({argname}); - if ({paramname} == (unsigned short)-1 && PyErr_Occurred()) {{{{ - goto exit; - }}}} - """, - argname=argname) - return super().parse_arg(argname, displayname, limited_capi=limited_capi) - @add_legacy_c_converter('C', accept={str}) class int_converter(CConverter): @@ -336,22 +347,12 @@ class unsigned_int_converter(BaseUnsignedIntConverter): c_ignored_default = "0" def converter_init(self, *, bitwise: bool = False) -> None: + self.bitwise = bitwise if bitwise: self.format_unit = 'I' else: self.converter = '_PyLong_UnsignedInt_Converter' - def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: - if self.format_unit == 'I': - return self.format_code(""" - {paramname} = (unsigned int)PyLong_AsUnsignedLongMask({argname}); - if ({paramname} == (unsigned int)-1 && PyErr_Occurred()) {{{{ - goto exit; - }}}} - """, - argname=argname) - return super().parse_arg(argname, displayname, limited_capi=limited_capi) - class long_converter(CConverter): type = 'long' @@ -377,25 +378,12 @@ class unsigned_long_converter(BaseUnsignedIntConverter): c_ignored_default = "0" def converter_init(self, *, bitwise: bool = False) -> None: + self.bitwise = bitwise if bitwise: self.format_unit = 'k' else: self.converter = '_PyLong_UnsignedLong_Converter' - def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: - if self.format_unit == 'k': - return self.format_code(""" - if (!PyIndex_Check({argname})) {{{{ - {bad_argument} - goto exit; - }}}} - {paramname} = PyLong_AsUnsignedLongMask({argname}); - """, - argname=argname, - bad_argument=self.bad_argument(displayname, 'int', limited_capi=limited_capi), - ) - return super().parse_arg(argname, displayname, limited_capi=limited_capi) - class long_long_converter(CConverter): type = 'long long' @@ -421,25 +409,12 @@ class unsigned_long_long_converter(BaseUnsignedIntConverter): c_ignored_default = "0" def converter_init(self, *, bitwise: bool = False) -> None: + self.bitwise = bitwise if bitwise: self.format_unit = 'K' else: self.converter = '_PyLong_UnsignedLongLong_Converter' - def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: - if self.format_unit == 'K': - return self.format_code(""" - if (!PyIndex_Check({argname})) {{{{ - {bad_argument} - goto exit; - }}}} - {paramname} = PyLong_AsUnsignedLongLongMask({argname}); - """, - argname=argname, - bad_argument=self.bad_argument(displayname, 'int', limited_capi=limited_capi), - ) - return super().parse_arg(argname, displayname, limited_capi=limited_capi) - class Py_ssize_t_converter(CConverter): type = 'Py_ssize_t'