From ff4cfc8e48e7e2cf7e60b016db8c6f9f70be6305 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 28 Jun 2024 10:53:04 +0300 Subject: [PATCH 1/6] gh-74020: Raise ValueError for negative values converted to unsigned Converting negative Python integer to a C unsigned integer type now raises ValueError, not OverflowError. Affected C API: PyLong_AsUnsignedLong(), PyLong_AsUnsignedLongLong(), PyLong_AsSize_t() and the "b" format unit in PyArg_Parse(). --- Doc/c-api/arg.rst | 4 ++ Doc/c-api/long.rst | 21 ++++++--- Doc/whatsnew/3.14.rst | 4 ++ Lib/test/test_array.py | 5 +- Lib/test/test_capi/test_getargs.py | 2 +- Lib/test/test_capi/test_long.py | 6 +-- Lib/test/test_long.py | 4 +- Lib/test/test_lzma.py | 4 +- Lib/test/test_socket.py | 13 ++++-- Lib/test/test_ssl.py | 2 +- ...4-06-28-10-51-56.gh-issue-74020.FcEAzj.rst | 2 + Modules/_struct.c | 30 +++++++++--- Modules/_testlimitedcapi/testcapi_long.h | 4 +- Modules/arraymodule.c | 43 +++++++++-------- Modules/clinic/socketmodule.c.h | 17 ++++--- Modules/mathmodule.c | 14 +++--- Modules/pwdmodule.c | 5 +- Modules/socketmodule.c | 28 ++++------- Objects/longobject.c | 46 ++++--------------- Python/getargs.c | 31 +++++++------ Python/initconfig.c | 4 +- 21 files changed, 152 insertions(+), 137 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-06-28-10-51-56.gh-issue-74020.FcEAzj.rst diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index 834aae9372fe3b..2c774ec2b5f143 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -233,6 +233,10 @@ Numbers Convert a nonnegative Python integer to an unsigned tiny int, stored in a C :c:expr:`unsigned char`. + .. versionchanged:: 3.14 + A negative Python integer now raises :exc:`ValueError`, + not :exc:`OverflowError`. + ``B`` (:class:`int`) [unsigned char] Convert a Python integer to a tiny int without overflow checking, stored in a C :c:expr:`unsigned char`. diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst index a0e111af5996d7..230986b8d46093 100644 --- a/Doc/c-api/long.rst +++ b/Doc/c-api/long.rst @@ -257,12 +257,15 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. Return a C :c:expr:`unsigned long` representation of *pylong*. *pylong* must be an instance of :c:type:`PyLongObject`. - Raise :exc:`OverflowError` if the value of *pylong* is out of range for a - :c:expr:`unsigned long`. + Raise :exc:`ValueError` if the value of *pylong* is negative and + :exc:`OverflowError` if it is out of range for a :c:expr:`unsigned long`. Returns ``(unsigned long)-1`` on error. Use :c:func:`PyErr_Occurred` to disambiguate. + .. versionchanged:: 3.14 + A negative *pylong* now raises :exc:`ValueError`, not :exc:`OverflowError`. + .. c:function:: size_t PyLong_AsSize_t(PyObject *pylong) @@ -273,12 +276,15 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. Return a C :c:type:`size_t` representation of *pylong*. *pylong* must be an instance of :c:type:`PyLongObject`. - Raise :exc:`OverflowError` if the value of *pylong* is out of range for a - :c:type:`size_t`. + Raise :exc:`ValueError` if the value of *pylong* is negative and + :exc:`OverflowError` if it is out of range for a :c:type:`size_t`. Returns ``(size_t)-1`` on error. Use :c:func:`PyErr_Occurred` to disambiguate. + .. versionchanged:: 3.14 + A negative *pylong* now raises :exc:`ValueError`, not :exc:`OverflowError`. + .. c:function:: unsigned long long PyLong_AsUnsignedLongLong(PyObject *pylong) @@ -288,8 +294,8 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. Return a C :c:expr:`unsigned long long` representation of *pylong*. *pylong* must be an instance of :c:type:`PyLongObject`. - Raise :exc:`OverflowError` if the value of *pylong* is out of range for an - :c:expr:`unsigned long long`. + Raise :exc:`ValueError` if the value of *pylong* is negative and + :exc:`OverflowError` if it is out of range for a :c:expr:`unsigned long long`. Returns ``(unsigned long long)-1`` on error. Use :c:func:`PyErr_Occurred` to disambiguate. @@ -297,6 +303,9 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. .. versionchanged:: 3.1 A negative *pylong* now raises :exc:`OverflowError`, not :exc:`TypeError`. + .. versionchanged:: 3.14 + A negative *pylong* now raises :exc:`ValueError`, not :exc:`OverflowError`. + .. c:function:: unsigned long PyLong_AsUnsignedLongMask(PyObject *obj) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 9662044915b8ca..84e2f509ed77a5 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -285,6 +285,10 @@ Others integer must implement either :meth:`~object.__int__` or :meth:`~object.__index__`. (Contributed by Mark Dickinson in :gh:`119743`.) +* Converting negative Python integer to a C unsigned integer type now raises + :exc:`ValueError`, not :exc:`OverflowError`. + (Contributed by Serhiy Storchaka in :gh:`74020`.) + Porting to Python 3.14 ====================== diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index 95383be9659eb9..ba77a5d1df8a32 100755 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -1326,8 +1326,9 @@ def check_overflow(self, lower, upper): a = array.array(self.typecode, [lower]) a[0] = lower # should overflow assigning less than lower limit - self.assertRaises(OverflowError, array.array, self.typecode, [lower-1]) - self.assertRaises(OverflowError, a.__setitem__, 0, lower-1) + exc = ValueError if int(lower) == 0 else OverflowError + self.assertRaises(exc, array.array, self.typecode, [lower-1]) + self.assertRaises(exc, a.__setitem__, 0, lower-1) # should not overflow assigning upper limit a = array.array(self.typecode, [upper]) a[0] = upper diff --git a/Lib/test/test_capi/test_getargs.py b/Lib/test/test_capi/test_getargs.py index 232aa2a80025dc..5a55dc939aeeaf 100644 --- a/Lib/test/test_capi/test_getargs.py +++ b/Lib/test/test_capi/test_getargs.py @@ -179,7 +179,7 @@ def test_b(self): self.assertRaises(TypeError, getargs_b, BadInt2()) self.assertEqual(0, getargs_b(BadInt3())) - self.assertRaises(OverflowError, getargs_b, -1) + self.assertRaises(ValueError, getargs_b, -1) self.assertEqual(0, getargs_b(0)) self.assertEqual(UCHAR_MAX, getargs_b(UCHAR_MAX)) self.assertRaises(OverflowError, getargs_b, UCHAR_MAX + 1) diff --git a/Lib/test/test_capi/test_long.py b/Lib/test/test_capi/test_long.py index 06a29b5a0505b4..763fff1654cff6 100644 --- a/Lib/test/test_capi/test_long.py +++ b/Lib/test/test_capi/test_long.py @@ -236,7 +236,7 @@ def test_long_asunsignedlong(self): self.assertRaises(TypeError, asunsignedlong, Index(42)) self.assertRaises(TypeError, asunsignedlong, MyIndexAndInt()) - self.assertRaises(OverflowError, asunsignedlong, -1) + self.assertRaises(ValueError, asunsignedlong, -1) self.assertRaises(OverflowError, asunsignedlong, ULONG_MAX + 1) self.assertRaises(TypeError, asunsignedlong, 1.0) self.assertRaises(TypeError, asunsignedlong, b'2') @@ -314,7 +314,7 @@ def test_long_asunsignedlonglong(self): self.assertRaises(TypeError, asunsignedlonglong, Index(42)) self.assertRaises(TypeError, asunsignedlonglong, MyIndexAndInt()) - self.assertRaises(OverflowError, asunsignedlonglong, -1) + self.assertRaises(ValueError, asunsignedlonglong, -1) self.assertRaises(OverflowError, asunsignedlonglong, ULLONG_MAX + 1) self.assertRaises(TypeError, asunsignedlonglong, 1.0) self.assertRaises(TypeError, asunsignedlonglong, b'2') @@ -374,7 +374,7 @@ def test_long_as_size_t(self): self.assertRaises(TypeError, as_size_t, Index(42)) self.assertRaises(TypeError, as_size_t, MyIndexAndInt()) - self.assertRaises(OverflowError, as_size_t, -1) + self.assertRaises(ValueError, as_size_t, -1) self.assertRaises(OverflowError, as_size_t, SIZE_MAX + 1) self.assertRaises(TypeError, as_size_t, 1.0) self.assertRaises(TypeError, as_size_t, b'2') diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py index 3b2e7c4e71d10d..a8105bfa65e8dd 100644 --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -1316,8 +1316,8 @@ def equivalent_python(n, length, byteorder, signed=False): self.assertRaises(OverflowError, (256).to_bytes, 1, 'big', signed=True) self.assertRaises(OverflowError, (256).to_bytes, 1, 'little', signed=False) self.assertRaises(OverflowError, (256).to_bytes, 1, 'little', signed=True) - self.assertRaises(OverflowError, (-1).to_bytes, 2, 'big', signed=False) - self.assertRaises(OverflowError, (-1).to_bytes, 2, 'little', signed=False) + self.assertRaises(ValueError, (-1).to_bytes, 2, 'big', signed=False) + self.assertRaises(ValueError, (-1).to_bytes, 2, 'little', signed=False) self.assertEqual((0).to_bytes(0, 'big'), b'') self.assertEqual((1).to_bytes(5, 'big'), b'\x00\x00\x00\x00\x01') self.assertEqual((0).to_bytes(5, 'big'), b'\x00\x00\x00\x00\x00') diff --git a/Lib/test/test_lzma.py b/Lib/test/test_lzma.py index 22478c14fb4a65..c1ec5766dd63aa 100644 --- a/Lib/test/test_lzma.py +++ b/Lib/test/test_lzma.py @@ -647,9 +647,9 @@ def test_init_bad_preset(self): LZMAFile(BytesIO(), "w", preset=10) with self.assertRaises(LZMAError): LZMAFile(BytesIO(), "w", preset=23) - with self.assertRaises(OverflowError): + with self.assertRaises(ValueError): LZMAFile(BytesIO(), "w", preset=-1) - with self.assertRaises(OverflowError): + with self.assertRaises(ValueError): LZMAFile(BytesIO(), "w", preset=-7) with self.assertRaises(TypeError): LZMAFile(BytesIO(), "w", preset="foo") diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index ce0f64b43ed49f..92a881f5615629 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1121,7 +1121,7 @@ def testInterfaceNameIndex(self): 'socket.if_indextoname() not available.') def testInvalidInterfaceIndexToName(self): self.assertRaises(OSError, socket.if_indextoname, 0) - self.assertRaises(OverflowError, socket.if_indextoname, -1) + self.assertRaises(ValueError, socket.if_indextoname, -1) self.assertRaises(OverflowError, socket.if_indextoname, 2**1000) self.assertRaises(TypeError, socket.if_indextoname, '_DEADBEEF') if hasattr(socket, 'if_nameindex'): @@ -1182,11 +1182,11 @@ def testNtoHErrors(self): import _testcapi s_good_values = [0, 1, 2, 0xffff] l_good_values = s_good_values + [0xffffffff] - l_bad_values = [-1, -2, 1<<32, 1<<1000] + neg_values = [-1, -2, -(1<<15)-1, -(1<<31)-1, -(1<<63)-1, -1<<1000] + l_bad_values = [1<<32, 1<<1000] s_bad_values = ( l_bad_values + - [_testcapi.INT_MIN-1, _testcapi.INT_MAX+1] + - [1 << 16, _testcapi.INT_MAX] + [1 << 16, _testcapi.INT_MAX, _testcapi.INT_MAX+1] ) for k in s_good_values: socket.ntohs(k) @@ -1194,6 +1194,11 @@ def testNtoHErrors(self): for k in l_good_values: socket.ntohl(k) socket.htonl(k) + for k in neg_values: + self.assertRaises(ValueError, socket.ntohs, k) + self.assertRaises(ValueError, socket.htons, k) + self.assertRaises(ValueError, socket.ntohl, k) + self.assertRaises(ValueError, socket.htonl, k) for k in s_bad_values: self.assertRaises(OverflowError, socket.ntohs, k) self.assertRaises(OverflowError, socket.htons, k) diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 6ec010d13f9e7e..b38af28e634136 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -997,7 +997,7 @@ def test_options(self): self.assertEqual(0, ctx.options & ~ssl.OP_NO_SSLv3) # invalid options - with self.assertRaises(OverflowError): + with self.assertRaises(ValueError): ctx.options = -1 with self.assertRaises(OverflowError): ctx.options = 2 ** 100 diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-06-28-10-51-56.gh-issue-74020.FcEAzj.rst b/Misc/NEWS.d/next/Core and Builtins/2024-06-28-10-51-56.gh-issue-74020.FcEAzj.rst new file mode 100644 index 00000000000000..c635861718173e --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-06-28-10-51-56.gh-issue-74020.FcEAzj.rst @@ -0,0 +1,2 @@ +Converting negative Python integer to a C unsigned integer type now raises +:exc:`ValueError`, not :exc:`OverflowError`. diff --git a/Modules/_struct.c b/Modules/_struct.c index 6a68478dd45d36..a6dc5149b181fb 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -636,7 +636,9 @@ np_uint(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) unsigned long x; unsigned int y; if (get_ulong(state, v, &x) < 0) { - if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + if (PyErr_ExceptionMatches(PyExc_OverflowError) || + PyErr_ExceptionMatches(PyExc_ValueError)) + { RANGE_ERROR(state, f, 1); } return -1; @@ -669,7 +671,9 @@ np_ulong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) { unsigned long x; if (get_ulong(state, v, &x) < 0) { - if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + if (PyErr_ExceptionMatches(PyExc_OverflowError) || + PyErr_ExceptionMatches(PyExc_ValueError)) + { RANGE_ERROR(state, f, 1); } return -1; @@ -697,7 +701,9 @@ np_size_t(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) { size_t x; if (get_size_t(state, v, &x) < 0) { - if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + if (PyErr_ExceptionMatches(PyExc_OverflowError) || + PyErr_ExceptionMatches(PyExc_ValueError)) + { RANGE_ERROR(state, f, 1); } return -1; @@ -729,7 +735,9 @@ np_ulonglong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f { unsigned long long x; if (get_ulonglong(state, v, &x) < 0) { - if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + if (PyErr_ExceptionMatches(PyExc_OverflowError) || + PyErr_ExceptionMatches(PyExc_ValueError)) + { PyErr_Format(state->StructError, "'%c' format requires 0 <= number <= %llu", f->format, @@ -970,7 +978,9 @@ bp_uint(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) Py_ssize_t i; unsigned char *q = (unsigned char *)p; if (get_ulong(state, v, &x) < 0) { - if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + if (PyErr_ExceptionMatches(PyExc_OverflowError) || + PyErr_ExceptionMatches(PyExc_ValueError)) + { RANGE_ERROR(state, f, 1); } return -1; @@ -1232,7 +1242,9 @@ lp_uint(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) Py_ssize_t i; unsigned char *q = (unsigned char *)p; if (get_ulong(state, v, &x) < 0) { - if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + if (PyErr_ExceptionMatches(PyExc_OverflowError) || + PyErr_ExceptionMatches(PyExc_ValueError)) + { RANGE_ERROR(state, f, 1); } return -1; @@ -1991,9 +2003,13 @@ s_pack_internal(PyStructObject *soself, PyObject *const *args, int offset, *res = Py_SAFE_DOWNCAST(n, Py_ssize_t, unsigned char); } else { if (e->pack(state, res, v, e) < 0) { - if (PyLong_Check(v) && PyErr_ExceptionMatches(PyExc_OverflowError)) + if (PyLong_Check(v) && + (PyErr_ExceptionMatches(PyExc_OverflowError) || + PyErr_ExceptionMatches(PyExc_ValueError))) + { PyErr_SetString(state->StructError, "int too large to convert"); + } return -1; } } diff --git a/Modules/_testlimitedcapi/testcapi_long.h b/Modules/_testlimitedcapi/testcapi_long.h index 143258140b4bb4..dd139fc4554a45 100644 --- a/Modules/_testlimitedcapi/testcapi_long.h +++ b/Modules/_testlimitedcapi/testcapi_long.h @@ -96,10 +96,10 @@ TESTNAME(PyObject *error(const char*)) if (uout != (unsigned TYPENAME)-1 || !PyErr_Occurred()) return error( "PyLong_AsUnsignedXXX(-1) didn't complain"); - if (!PyErr_ExceptionMatches(PyExc_OverflowError)) + if (!PyErr_ExceptionMatches(PyExc_ValueError)) return error( "PyLong_AsUnsignedXXX(-1) raised " - "something other than OverflowError"); + "something other than ValueError"); PyErr_Clear(); UNBIND(x); diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index e6c84d588be98b..0173aec84e7e94 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -211,20 +211,23 @@ b_getitem(arrayobject *ap, Py_ssize_t i) static int b_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) { - short x; - /* PyArg_Parse's 'b' formatter is for an unsigned char, therefore - must use the next size up that is signed ('h') and manually do - the overflow checking */ - if (!PyArg_Parse(v, "h;array item must be integer", &x)) + int overflow; + long x = PyLong_AsLongAndOverflow(v, &overflow); + if (x == -1 && PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + PyErr_SetString(PyExc_TypeError, + "array item must be integer"); + } return -1; - else if (x < -128) { + } + if (overflow > 0 || x > 127) { PyErr_SetString(PyExc_OverflowError, - "signed char is less than minimum"); + "signed char is greater than maximum"); return -1; } - else if (x > 127) { + if (overflow < 0 || x < -128) { PyErr_SetString(PyExc_OverflowError, - "signed char is greater than maximum"); + "signed char is less than minimum"); return -1; } if (i >= 0) @@ -352,21 +355,25 @@ HH_getitem(arrayobject *ap, Py_ssize_t i) static int HH_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) { - int x; - /* PyArg_Parse's 'h' formatter is for a signed short, therefore - must use the next size up and manually do the overflow checking */ - if (!PyArg_Parse(v, "i;array item must be integer", &x)) - return -1; - else if (x < 0) { - PyErr_SetString(PyExc_OverflowError, - "unsigned short is less than minimum"); + int overflow; + long x = PyLong_AsLongAndOverflow(v, &overflow); + if (x == -1 && PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + PyErr_SetString(PyExc_TypeError, + "array item must be integer"); + } return -1; } - else if (x > USHRT_MAX) { + if (overflow > 0 || x > USHRT_MAX) { PyErr_SetString(PyExc_OverflowError, "unsigned short is greater than maximum"); return -1; } + if (overflow < 0 || x < 0) { + PyErr_SetString(PyExc_ValueError, + "unsigned short is less than minimum"); + return -1; + } if (i >= 0) ((short *)ap->ob_item)[i] = (short)x; return 0; diff --git a/Modules/clinic/socketmodule.c.h b/Modules/clinic/socketmodule.c.h index 3f4056efff2fec..8523b4b43e9699 100644 --- a/Modules/clinic/socketmodule.c.h +++ b/Modules/clinic/socketmodule.c.h @@ -6,6 +6,7 @@ preserve # include "pycore_gc.h" // PyGC_Head # include "pycore_runtime.h" // _Py_ID() #endif +#include "pycore_long.h" // _PyLong_UnsignedShort_Converter() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() static int @@ -102,16 +103,15 @@ PyDoc_STRVAR(_socket_socket_ntohs__doc__, {"ntohs", (PyCFunction)_socket_socket_ntohs, METH_O, _socket_socket_ntohs__doc__}, static PyObject * -_socket_socket_ntohs_impl(PySocketSockObject *self, int x); +_socket_socket_ntohs_impl(PySocketSockObject *self, unsigned short x); static PyObject * _socket_socket_ntohs(PySocketSockObject *self, PyObject *arg) { PyObject *return_value = NULL; - int x; + unsigned short x; - x = PyLong_AsInt(arg); - if (x == -1 && PyErr_Occurred()) { + if (!_PyLong_UnsignedShort_Converter(arg, &x)) { goto exit; } return_value = _socket_socket_ntohs_impl(self, x); @@ -130,16 +130,15 @@ PyDoc_STRVAR(_socket_socket_htons__doc__, {"htons", (PyCFunction)_socket_socket_htons, METH_O, _socket_socket_htons__doc__}, static PyObject * -_socket_socket_htons_impl(PySocketSockObject *self, int x); +_socket_socket_htons_impl(PySocketSockObject *self, unsigned short x); static PyObject * _socket_socket_htons(PySocketSockObject *self, PyObject *arg) { PyObject *return_value = NULL; - int x; + unsigned short x; - x = PyLong_AsInt(arg); - if (x == -1 && PyErr_Occurred()) { + if (!_PyLong_UnsignedShort_Converter(arg, &x)) { goto exit; } return_value = _socket_socket_htons_impl(self, x); @@ -259,4 +258,4 @@ _socket_socket_if_nametoindex(PySocketSockObject *self, PyObject *arg) #ifndef _SOCKET_SOCKET_IF_NAMETOINDEX_METHODDEF #define _SOCKET_SOCKET_IF_NAMETOINDEX_METHODDEF #endif /* !defined(_SOCKET_SOCKET_IF_NAMETOINDEX_METHODDEF) */ -/*[clinic end generated code: output=eb37b5d88a1e4661 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=23dc8d4d0982eada input=a9049054013a1b77]*/ diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 6defa973da0952..cc0edc20f72851 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -3942,13 +3942,6 @@ math_nextafter_impl(PyObject *module, double x, double y, PyObject *steps) return NULL; } assert(PyLong_CheckExact(steps)); - if (_PyLong_IsNegative((PyLongObject *)steps)) { - PyErr_SetString(PyExc_ValueError, - "steps must be a non-negative integer"); - Py_DECREF(steps); - return NULL; - } - unsigned long long usteps_ull = PyLong_AsUnsignedLongLong(steps); // Conveniently, uint64_t and double have the same number of bits // on all the platforms we care about. @@ -3960,7 +3953,12 @@ math_nextafter_impl(PyObject *module, double x, double y, PyObject *steps) // usteps_ull can be strictly larger than UINT64_MAX on a machine // where unsigned long long has width > 64 bits. if (PyErr_Occurred()) { - if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + if (PyErr_ExceptionMatches(PyExc_ValueError)) { + PyErr_SetString(PyExc_ValueError, + "steps must be a non-negative integer"); + return NULL; + } + else if (PyErr_ExceptionMatches(PyExc_OverflowError)) { PyErr_Clear(); } else { diff --git a/Modules/pwdmodule.c b/Modules/pwdmodule.c index 2240e2078b2d98..d11051352f74d2 100644 --- a/Modules/pwdmodule.c +++ b/Modules/pwdmodule.c @@ -141,9 +141,12 @@ pwd_getpwuid(PyObject *module, PyObject *uidobj) char *buf = NULL, *buf2 = NULL; if (!_Py_Uid_Converter(uidobj, &uid)) { - if (PyErr_ExceptionMatches(PyExc_OverflowError)) + if (PyErr_ExceptionMatches(PyExc_OverflowError) || + PyErr_ExceptionMatches(PyExc_ValueError)) + { PyErr_Format(PyExc_KeyError, "getpwuid(): uid not found"); + } return NULL; } #ifdef HAVE_GETPWUID_R diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 6d161478be2d9d..8af0033debba7c 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -6332,28 +6332,24 @@ AF_UNIX if defined on the platform; otherwise, the default is AF_INET."); /*[clinic input] _socket.socket.ntohs - x: int + x: unsigned_short(bitwise=False) / Convert a 16-bit unsigned integer from network to host byte order. [clinic start generated code]*/ static PyObject * -_socket_socket_ntohs_impl(PySocketSockObject *self, int x) -/*[clinic end generated code: output=a828a61a9fb205b2 input=9a79cb3a71652147]*/ +_socket_socket_ntohs_impl(PySocketSockObject *self, unsigned short x) +/*[clinic end generated code: output=fea6a7f0ed6b5319 input=a64696ca0e7e61ab]*/ { - if (x < 0) { - PyErr_SetString(PyExc_OverflowError, - "ntohs: can't convert negative Python int to C " - "16-bit unsigned integer"); - return NULL; - } +#if SIZEOF_SHORT > 2 if (x > 0xffff) { PyErr_SetString(PyExc_OverflowError, "ntohs: Python int too large to convert to C " "16-bit unsigned integer"); return NULL; } +#endif return PyLong_FromUnsignedLong(ntohs((unsigned short)x)); } @@ -6394,28 +6390,24 @@ Convert a 32-bit integer from network to host byte order."); /*[clinic input] _socket.socket.htons - x: int + x: unsigned_short(bitwise=False) / Convert a 16-bit unsigned integer from host to network byte order. [clinic start generated code]*/ static PyObject * -_socket_socket_htons_impl(PySocketSockObject *self, int x) -/*[clinic end generated code: output=d785ee692312da47 input=053252d8416f4337]*/ +_socket_socket_htons_impl(PySocketSockObject *self, unsigned short x) +/*[clinic end generated code: output=051e2c88a1e143fe input=45f8ada592cbbc8c]*/ { - if (x < 0) { - PyErr_SetString(PyExc_OverflowError, - "htons: can't convert negative Python int to C " - "16-bit unsigned integer"); - return NULL; - } +#if SIZEOF_SHORT > 2 if (x > 0xffff) { PyErr_SetString(PyExc_OverflowError, "htons: Python int too large to convert to C " "16-bit unsigned integer"); return NULL; } +#endif return PyLong_FromUnsignedLong(htons((unsigned short)x)); } diff --git a/Objects/longobject.c b/Objects/longobject.c index 86afec9a414134..c67075818a01f0 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -643,7 +643,7 @@ PyLong_AsUnsignedLong(PyObject *vv) #endif } if (_PyLong_IsNegative(v)) { - PyErr_SetString(PyExc_OverflowError, + PyErr_SetString(PyExc_ValueError, "can't convert negative value to unsigned int"); return (unsigned long) -1; } @@ -688,7 +688,7 @@ PyLong_AsSize_t(PyObject *vv) return _PyLong_CompactValue(v); } if (_PyLong_IsNegative(v)) { - PyErr_SetString(PyExc_OverflowError, + PyErr_SetString(PyExc_ValueError, "can't convert negative value to size_t"); return (size_t) -1; } @@ -959,7 +959,7 @@ _PyLong_AsByteArray(PyLongObject* v, if (_PyLong_IsNegative(v)) { if (!is_signed) { if (with_exceptions) { - PyErr_SetString(PyExc_OverflowError, + PyErr_SetString(PyExc_ValueError, "can't convert negative int to unsigned"); } return -1; @@ -1678,13 +1678,7 @@ PyLong_AsLongLongAndOverflow(PyObject *vv, int *overflow) int _PyLong_UnsignedShort_Converter(PyObject *obj, void *ptr) { - unsigned long uval; - - if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) { - PyErr_SetString(PyExc_ValueError, "value must be positive"); - return 0; - } - uval = PyLong_AsUnsignedLong(obj); + unsigned long uval = PyLong_AsUnsignedLong(obj); if (uval == (unsigned long)-1 && PyErr_Occurred()) return 0; if (uval > USHRT_MAX) { @@ -1700,13 +1694,7 @@ _PyLong_UnsignedShort_Converter(PyObject *obj, void *ptr) int _PyLong_UnsignedInt_Converter(PyObject *obj, void *ptr) { - unsigned long uval; - - if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) { - PyErr_SetString(PyExc_ValueError, "value must be positive"); - return 0; - } - uval = PyLong_AsUnsignedLong(obj); + unsigned long uval = PyLong_AsUnsignedLong(obj); if (uval == (unsigned long)-1 && PyErr_Occurred()) return 0; if (uval > UINT_MAX) { @@ -1722,13 +1710,7 @@ _PyLong_UnsignedInt_Converter(PyObject *obj, void *ptr) int _PyLong_UnsignedLong_Converter(PyObject *obj, void *ptr) { - unsigned long uval; - - if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) { - PyErr_SetString(PyExc_ValueError, "value must be positive"); - return 0; - } - uval = PyLong_AsUnsignedLong(obj); + unsigned long uval = PyLong_AsUnsignedLong(obj); if (uval == (unsigned long)-1 && PyErr_Occurred()) return 0; @@ -1739,13 +1721,7 @@ _PyLong_UnsignedLong_Converter(PyObject *obj, void *ptr) int _PyLong_UnsignedLongLong_Converter(PyObject *obj, void *ptr) { - unsigned long long uval; - - if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) { - PyErr_SetString(PyExc_ValueError, "value must be positive"); - return 0; - } - uval = PyLong_AsUnsignedLongLong(obj); + unsigned long long uval = PyLong_AsUnsignedLongLong(obj); if (uval == (unsigned long long)-1 && PyErr_Occurred()) return 0; @@ -1756,13 +1732,7 @@ _PyLong_UnsignedLongLong_Converter(PyObject *obj, void *ptr) int _PyLong_Size_t_Converter(PyObject *obj, void *ptr) { - size_t uval; - - if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) { - PyErr_SetString(PyExc_ValueError, "value must be positive"); - return 0; - } - uval = PyLong_AsSize_t(obj); + size_t uval = PyLong_AsSize_t(obj); if (uval == (size_t)-1 && PyErr_Occurred()) return 0; diff --git a/Python/getargs.c b/Python/getargs.c index b96ce3a22dae7c..ae6b6615c1d9c3 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -625,17 +625,18 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, case 'b': { /* unsigned byte -- very short int */ unsigned char *p = va_arg(*p_va, unsigned char *); - long ival = PyLong_AsLong(arg); + int overflow; + long ival = PyLong_AsLongAndOverflow(arg, &overflow); if (ival == -1 && PyErr_Occurred()) RETURN_ERR_OCCURRED; - else if (ival < 0) { + else if (overflow > 0 || ival > UCHAR_MAX) { PyErr_SetString(PyExc_OverflowError, - "unsigned byte integer is less than minimum"); + "unsigned byte integer is greater than maximum"); RETURN_ERR_OCCURRED; } - else if (ival > UCHAR_MAX) { - PyErr_SetString(PyExc_OverflowError, - "unsigned byte integer is greater than maximum"); + else if (overflow < 0 || ival < 0) { + PyErr_SetString(PyExc_ValueError, + "unsigned byte integer is less than minimum"); RETURN_ERR_OCCURRED; } else @@ -656,17 +657,18 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, case 'h': {/* signed short int */ short *p = va_arg(*p_va, short *); - long ival = PyLong_AsLong(arg); + int overflow; + long ival = PyLong_AsLongAndOverflow(arg, &overflow); if (ival == -1 && PyErr_Occurred()) RETURN_ERR_OCCURRED; - else if (ival < SHRT_MIN) { + else if (overflow > 0 || ival > SHRT_MAX) { PyErr_SetString(PyExc_OverflowError, - "signed short integer is less than minimum"); + "signed short integer is greater than maximum"); RETURN_ERR_OCCURRED; } - else if (ival > SHRT_MAX) { + else if (overflow < 0 || ival < SHRT_MIN) { PyErr_SetString(PyExc_OverflowError, - "signed short integer is greater than maximum"); + "signed short integer is less than minimum"); RETURN_ERR_OCCURRED; } else @@ -687,15 +689,16 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, case 'i': {/* signed int */ int *p = va_arg(*p_va, int *); - long ival = PyLong_AsLong(arg); + int overflow; + long ival = PyLong_AsLongAndOverflow(arg, &overflow); if (ival == -1 && PyErr_Occurred()) RETURN_ERR_OCCURRED; - else if (ival > INT_MAX) { + else if (overflow > 0 || ival > INT_MAX) { PyErr_SetString(PyExc_OverflowError, "signed integer is greater than maximum"); RETURN_ERR_OCCURRED; } - else if (ival < INT_MIN) { + else if (overflow < 0 || ival < INT_MIN) { PyErr_SetString(PyExc_OverflowError, "signed integer is less than minimum"); RETURN_ERR_OCCURRED; diff --git a/Python/initconfig.c b/Python/initconfig.c index 51897a2d0aef66..424f182bdc453c 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -1161,7 +1161,9 @@ config_dict_get_ulong(PyObject *dict, const char *name, unsigned long *result) if (PyErr_ExceptionMatches(PyExc_TypeError)) { config_dict_invalid_type(name); } - else if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + else if (PyErr_ExceptionMatches(PyExc_OverflowError) || + PyErr_ExceptionMatches(PyExc_ValueError)) + { config_dict_invalid_value(name); } return -1; From 1af78976305010cec45c1c58205ead568f1c9d9c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 28 Jun 2024 11:51:30 +0300 Subject: [PATCH 2/6] Fix some tests on Windows. --- Lib/test/test_os.py | 2 +- Lib/test/test_winreg.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index f93937fb587386..961f2cf537c82a 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -3445,7 +3445,7 @@ def test_waitstatus_to_exitcode_windows(self): # invalid values with self.assertRaises(ValueError): os.waitstatus_to_exitcode((max_exitcode + 1) << 8) - with self.assertRaises(OverflowError): + with self.assertRaises(ValueError): os.waitstatus_to_exitcode(-1) # Skip the test on Windows diff --git a/Lib/test/test_winreg.py b/Lib/test/test_winreg.py index 924a962781a75b..5a27b129d47426 100644 --- a/Lib/test/test_winreg.py +++ b/Lib/test/test_winreg.py @@ -359,8 +359,9 @@ def test_setvalueex_negative_one_check(self): # the value set was not -1. try: with CreateKey(HKEY_CURRENT_USER, test_key_name) as ck: - with self.assertRaises(OverflowError): + with self.assertRaises(ValueError): SetValueEx(ck, "test_name_dword", None, REG_DWORD, -1) + with self.assertRaises(ValueError): SetValueEx(ck, "test_name_qword", None, REG_QWORD, -1) self.assertRaises(FileNotFoundError, QueryValueEx, ck, "test_name_dword") self.assertRaises(FileNotFoundError, QueryValueEx, ck, "test_name_qword") From 8701b4a19aef0e70e3ba5e91e76b62e76335f57b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 29 Apr 2025 00:06:30 +0300 Subject: [PATCH 3/6] Use "versionchanged:: next". --- Doc/c-api/arg.rst | 2 +- Doc/c-api/long.rst | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index 4a76041672aa47..bebfcd42ca44cc 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -249,7 +249,7 @@ small to receive the value. Convert a nonnegative Python integer to an unsigned tiny integer, stored in a C :c:expr:`unsigned char`. - .. versionchanged:: 3.14 + .. versionchanged:: next A negative Python integer now raises :exc:`ValueError`, not :exc:`OverflowError`. diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst index 41cacf2850909f..6c911d284bbdbd 100644 --- a/Doc/c-api/long.rst +++ b/Doc/c-api/long.rst @@ -292,7 +292,7 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. Returns ``(unsigned long)-1`` on error. Use :c:func:`PyErr_Occurred` to disambiguate. - .. versionchanged:: 3.14 + .. versionchanged:: next A negative *pylong* now raises :exc:`ValueError`, not :exc:`OverflowError`. @@ -311,7 +311,7 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. Returns ``(size_t)-1`` on error. Use :c:func:`PyErr_Occurred` to disambiguate. - .. versionchanged:: 3.14 + .. versionchanged:: next A negative *pylong* now raises :exc:`ValueError`, not :exc:`OverflowError`. @@ -332,7 +332,7 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. .. versionchanged:: 3.1 A negative *pylong* now raises :exc:`OverflowError`, not :exc:`TypeError`. - .. versionchanged:: 3.14 + .. versionchanged:: next A negative *pylong* now raises :exc:`ValueError`, not :exc:`OverflowError`. From 7685781bb2a4368aeae4b684336a4c63e175d336 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 29 Apr 2025 10:38:17 +0300 Subject: [PATCH 4/6] Update docs for int.to_bytes(). --- Doc/library/stdtypes.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 39aaa5da0786f8..78e529fffa144d 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -534,7 +534,7 @@ class`. In addition, it provides a few more methods: The *signed* argument determines whether two's complement is used to represent the integer. If *signed* is ``False`` and a negative integer is - given, an :exc:`OverflowError` is raised. The default value for *signed* + given, a :exc:`ValueError` is raised. The default value for *signed* is ``False``. The default values can be used to conveniently turn an integer into a @@ -559,8 +559,14 @@ class`. In addition, it provides a few more methods: return bytes((n >> i*8) & 0xff for i in order) .. versionadded:: 3.2 + .. versionchanged:: 3.11 - Added default argument values for ``length`` and ``byteorder``. + Added default argument values for *length* and *byteorder*. + + .. versionchanged:: next + Raise :exc:`ValueError` instead of :exc:`OverflowError` for + negative integer if *signed* is false. + .. classmethod:: int.from_bytes(bytes, byteorder='big', *, signed=False) From 0300d31fe41525f5de3f05468c99c81db7e29fb3 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 29 Apr 2025 10:55:57 +0300 Subject: [PATCH 5/6] Copy tests from socket-unsigned-int-index. --- Lib/test/test_socket.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 1075b82ced6b36..ace97ce0cbe422 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1225,18 +1225,12 @@ def testNtoH(self): self.assertEqual(swapped & mask, mask) self.assertRaises(OverflowError, func, 1<<34) - @support.cpython_only - @unittest.skipIf(_testcapi is None, "requires _testcapi") def testNtoHErrors(self): - import _testcapi s_good_values = [0, 1, 2, 0xffff] l_good_values = s_good_values + [0xffffffff] neg_values = [-1, -2, -(1<<15)-1, -(1<<31)-1, -(1<<63)-1, -1<<1000] l_bad_values = [1<<32, 1<<1000] - s_bad_values = ( - l_bad_values + - [1 << 16, _testcapi.INT_MAX, _testcapi.INT_MAX+1] - ) + s_bad_values = l_bad_values + [1 << 16, (1<<31)-1, 1<<31] for k in s_good_values: socket.ntohs(k) socket.htons(k) From 8526ac6c309cca0dda21e2db7dfddf3d0bf5262d Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 29 Apr 2025 11:02:57 +0300 Subject: [PATCH 6/6] Remove unneeded bitwise=False. --- Modules/socketmodule.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 401691a6ca8ee4..ccef39a45f6384 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -6576,7 +6576,7 @@ AF_UNIX if defined on the platform; otherwise, the default is AF_INET."); /*[clinic input] _socket.socket.ntohs - x: unsigned_short(bitwise=False) + x: unsigned_short / Convert a 16-bit unsigned integer from network to host byte order. @@ -6584,7 +6584,7 @@ Convert a 16-bit unsigned integer from network to host byte order. static PyObject * _socket_socket_ntohs_impl(PySocketSockObject *self, unsigned short x) -/*[clinic end generated code: output=fea6a7f0ed6b5319 input=a64696ca0e7e61ab]*/ +/*[clinic end generated code: output=fea6a7f0ed6b5319 input=6a7ac232db21dd62]*/ { #if SIZEOF_SHORT > 2 if (x > 0xffff) { @@ -6634,7 +6634,7 @@ Convert a 32-bit integer from network to host byte order."); /*[clinic input] _socket.socket.htons - x: unsigned_short(bitwise=False) + x: unsigned_short / Convert a 16-bit unsigned integer from host to network byte order. @@ -6642,7 +6642,7 @@ Convert a 16-bit unsigned integer from host to network byte order. static PyObject * _socket_socket_htons_impl(PySocketSockObject *self, unsigned short x) -/*[clinic end generated code: output=051e2c88a1e143fe input=45f8ada592cbbc8c]*/ +/*[clinic end generated code: output=051e2c88a1e143fe input=2da6f8e015ed3d93]*/ { #if SIZEOF_SHORT > 2 if (x > 0xffff) {