From 3d6b8d45339eb47ca3eb86e19fbbb5cc37bc2747 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 4 Feb 2024 17:32:25 +0200 Subject: [PATCH] [3.11] gh-114388: Fix warnings when assign an unsigned integer member (GH-114391) * Fix a RuntimeWarning emitted when assign an integer-like value that is not an instance of int to an attribute that corresponds to a C struct member of type T_UINT and T_ULONG. * Fix a double RuntimeWarning emitted when assign a negative integer value to an attribute that corresponds to a C struct member of type T_UINT. (cherry picked from commit 3ddc5152550ea62280124c37d0b4339030ff7df4) Co-authored-by: Serhiy Storchaka --- Lib/test/test_capi/test_structmembers.py | 35 ++++++++ ...-01-21-17-29-32.gh-issue-114388.UVGO4K.rst | 5 ++ Python/structmember.c | 85 +++++++++++++------ 3 files changed, 97 insertions(+), 28 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-01-21-17-29-32.gh-issue-114388.UVGO4K.rst diff --git a/Lib/test/test_capi/test_structmembers.py b/Lib/test/test_capi/test_structmembers.py index 07d2f623f7156e..59ed2fb2db8a25 100644 --- a/Lib/test/test_capi/test_structmembers.py +++ b/Lib/test/test_capi/test_structmembers.py @@ -12,6 +12,14 @@ LLONG_MAX, LLONG_MIN, ULLONG_MAX, \ PY_SSIZE_T_MAX, PY_SSIZE_T_MIN + +class Index: + def __init__(self, value): + self.value = value + def __index__(self): + return self.value + + ts=_test_structmembersType(False, # T_BOOL 1, # T_BYTE 2, # T_UBYTE @@ -59,6 +67,10 @@ def test_int(self): self.assertEqual(ts.T_INT, INT_MIN) ts.T_UINT = UINT_MAX self.assertEqual(ts.T_UINT, UINT_MAX) + ts.T_UINT = Index(0) + self.assertEqual(ts.T_UINT, 0) + ts.T_UINT = Index(INT_MAX) + self.assertEqual(ts.T_UINT, INT_MAX) def test_long(self): ts.T_LONG = LONG_MAX @@ -67,6 +79,10 @@ def test_long(self): self.assertEqual(ts.T_LONG, LONG_MIN) ts.T_ULONG = ULONG_MAX self.assertEqual(ts.T_ULONG, ULONG_MAX) + ts.T_ULONG = Index(0) + self.assertEqual(ts.T_ULONG, 0) + ts.T_ULONG = Index(LONG_MAX) + self.assertEqual(ts.T_ULONG, LONG_MAX) def test_py_ssize_t(self): ts.T_PYSSIZET = PY_SSIZE_T_MAX @@ -140,6 +156,25 @@ def test_ushort_max(self): with warnings_helper.check_warnings(('', RuntimeWarning)): ts.T_USHORT = USHRT_MAX+1 + def test_int(self): + if LONG_MIN < INT_MIN: + with self.assertWarns(RuntimeWarning): + ts.T_INT = INT_MIN-1 + if LONG_MAX > INT_MAX: + with self.assertWarns(RuntimeWarning): + ts.T_INT = INT_MAX+1 + + def test_uint(self): + with self.assertWarns(RuntimeWarning): + ts.T_UINT = -1 + if ULONG_MAX > UINT_MAX: + with self.assertWarns(RuntimeWarning): + ts.T_UINT = UINT_MAX+1 + + def test_ulong(self): + with self.assertWarns(RuntimeWarning): + ts.T_ULONG = -1 + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-01-21-17-29-32.gh-issue-114388.UVGO4K.rst b/Misc/NEWS.d/next/Core and Builtins/2024-01-21-17-29-32.gh-issue-114388.UVGO4K.rst new file mode 100644 index 00000000000000..52c2742001d9ca --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-01-21-17-29-32.gh-issue-114388.UVGO4K.rst @@ -0,0 +1,5 @@ +Fix a :exc:`RuntimeWarning` emitted when assign an integer-like value that +is not an instance of :class:`int` to an attribute that corresponds to a C +struct member of :ref:`type ` T_UINT and T_ULONG. Fix a +double :exc:`RuntimeWarning` emitted when assign a negative integer value to +an attribute that corresponds to a C struct member of type T_UINT. diff --git a/Python/structmember.c b/Python/structmember.c index c7e318811d82b8..c790656978734a 100644 --- a/Python/structmember.c +++ b/Python/structmember.c @@ -187,45 +187,74 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v) WARN("Truncation of value to int"); break; } - case T_UINT:{ - unsigned long ulong_val = PyLong_AsUnsignedLong(v); - if ((ulong_val == (unsigned long)-1) && PyErr_Occurred()) { - /* XXX: For compatibility, accept negative int values - as well. */ - PyErr_Clear(); - ulong_val = PyLong_AsLong(v); - if ((ulong_val == (unsigned long)-1) && - PyErr_Occurred()) + case T_UINT: { + /* XXX: For compatibility, accept negative int values + as well. */ + int overflow; + long long_val = PyLong_AsLongAndOverflow(v, &overflow); + if (long_val == -1 && PyErr_Occurred()) { + return -1; + } + if (overflow < 0) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C long"); + return -1; + } + else if (!overflow) { + *(unsigned int *)addr = (unsigned int)(unsigned long)long_val; + if (long_val < 0) { + WARN("Writing negative value into unsigned field"); + } + else if ((unsigned long)long_val > UINT_MAX) { + WARN("Truncation of value to unsigned short"); + } + } + else { + unsigned long ulong_val = PyLong_AsUnsignedLong(v); + if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) { return -1; - *(unsigned int *)addr = (unsigned int)ulong_val; - WARN("Writing negative value into unsigned field"); - } else - *(unsigned int *)addr = (unsigned int)ulong_val; - if (ulong_val > UINT_MAX) - WARN("Truncation of value to unsigned int"); - break; + } + *(unsigned int*)addr = (unsigned int)ulong_val; + if (ulong_val > UINT_MAX) { + WARN("Truncation of value to unsigned int"); + } } + break; + } case T_LONG:{ *(long*)addr = PyLong_AsLong(v); if ((*(long*)addr == -1) && PyErr_Occurred()) return -1; break; } - case T_ULONG:{ - *(unsigned long*)addr = PyLong_AsUnsignedLong(v); - if ((*(unsigned long*)addr == (unsigned long)-1) - && PyErr_Occurred()) { - /* XXX: For compatibility, accept negative int values - as well. */ - PyErr_Clear(); - *(unsigned long*)addr = PyLong_AsLong(v); - if ((*(unsigned long*)addr == (unsigned long)-1) - && PyErr_Occurred()) + case T_ULONG: { + /* XXX: For compatibility, accept negative int values + as well. */ + int overflow; + long long_val = PyLong_AsLongAndOverflow(v, &overflow); + if (long_val == -1 && PyErr_Occurred()) { + return -1; + } + if (overflow < 0) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C long"); + return -1; + } + else if (!overflow) { + *(unsigned long *)addr = (unsigned long)long_val; + if (long_val < 0) { + WARN("Writing negative value into unsigned field"); + } + } + else { + unsigned long ulong_val = PyLong_AsUnsignedLong(v); + if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) { return -1; - WARN("Writing negative value into unsigned field"); + } + *(unsigned long*)addr = ulong_val; } break; - } + } case T_PYSSIZET:{ *(Py_ssize_t*)addr = PyLong_AsSsize_t(v); if ((*(Py_ssize_t*)addr == (Py_ssize_t)-1)