]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.11] gh-114388: Fix warnings when assign an unsigned integer member (GH-114391...
authorSerhiy Storchaka <storchaka@gmail.com>
Sun, 4 Feb 2024 17:54:26 +0000 (19:54 +0200)
committerGitHub <noreply@github.com>
Sun, 4 Feb 2024 17:54:26 +0000 (17:54 +0000)
* 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)

Lib/test/test_capi/test_structmembers.py
Misc/NEWS.d/next/Core and Builtins/2024-01-21-17-29-32.gh-issue-114388.UVGO4K.rst [new file with mode: 0644]
Python/structmember.c

index 07d2f623f7156ea729856a29eca0f24d5b1349b2..59ed2fb2db8a25cb98e0d432abcbdc2366b5b7a0 100644 (file)
@@ -12,6 +12,14 @@ from _testcapi import _test_structmembersType, \
     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 @@ class ReadWriteTests(unittest.TestCase):
         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 @@ class ReadWriteTests(unittest.TestCase):
         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 @@ class TestWarnings(unittest.TestCase):
         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 (file)
index 0000000..97e2d48
--- /dev/null
@@ -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 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.
index c7e318811d82b8895d40e5fffa01535055ef4f08..c790656978734ac23bde5cfe85140fa13d3c6233 100644 (file)
@@ -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)