Skip to content

Commit 0244e96

Browse files
[3.11] gh-114388: Fix warnings when assign an unsigned integer member (GH-114391) (GH-115002)
* 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 3ddc515)
1 parent f18341f commit 0244e96

File tree

3 files changed

+97
-28
lines changed

3 files changed

+97
-28
lines changed

Lib/test/test_capi/test_structmembers.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@
1212
LLONG_MAX, LLONG_MIN, ULLONG_MAX, \
1313
PY_SSIZE_T_MAX, PY_SSIZE_T_MIN
1414

15+
16+
class Index:
17+
def __init__(self, value):
18+
self.value = value
19+
def __index__(self):
20+
return self.value
21+
22+
1523
ts=_test_structmembersType(False, # T_BOOL
1624
1, # T_BYTE
1725
2, # T_UBYTE
@@ -59,6 +67,10 @@ def test_int(self):
5967
self.assertEqual(ts.T_INT, INT_MIN)
6068
ts.T_UINT = UINT_MAX
6169
self.assertEqual(ts.T_UINT, UINT_MAX)
70+
ts.T_UINT = Index(0)
71+
self.assertEqual(ts.T_UINT, 0)
72+
ts.T_UINT = Index(INT_MAX)
73+
self.assertEqual(ts.T_UINT, INT_MAX)
6274

6375
def test_long(self):
6476
ts.T_LONG = LONG_MAX
@@ -67,6 +79,10 @@ def test_long(self):
6779
self.assertEqual(ts.T_LONG, LONG_MIN)
6880
ts.T_ULONG = ULONG_MAX
6981
self.assertEqual(ts.T_ULONG, ULONG_MAX)
82+
ts.T_ULONG = Index(0)
83+
self.assertEqual(ts.T_ULONG, 0)
84+
ts.T_ULONG = Index(LONG_MAX)
85+
self.assertEqual(ts.T_ULONG, LONG_MAX)
7086

7187
def test_py_ssize_t(self):
7288
ts.T_PYSSIZET = PY_SSIZE_T_MAX
@@ -140,6 +156,25 @@ def test_ushort_max(self):
140156
with warnings_helper.check_warnings(('', RuntimeWarning)):
141157
ts.T_USHORT = USHRT_MAX+1
142158

159+
def test_int(self):
160+
if LONG_MIN < INT_MIN:
161+
with self.assertWarns(RuntimeWarning):
162+
ts.T_INT = INT_MIN-1
163+
if LONG_MAX > INT_MAX:
164+
with self.assertWarns(RuntimeWarning):
165+
ts.T_INT = INT_MAX+1
166+
167+
def test_uint(self):
168+
with self.assertWarns(RuntimeWarning):
169+
ts.T_UINT = -1
170+
if ULONG_MAX > UINT_MAX:
171+
with self.assertWarns(RuntimeWarning):
172+
ts.T_UINT = UINT_MAX+1
173+
174+
def test_ulong(self):
175+
with self.assertWarns(RuntimeWarning):
176+
ts.T_ULONG = -1
177+
143178

144179
if __name__ == "__main__":
145180
unittest.main()
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fix a :exc:`RuntimeWarning` emitted when assign an integer-like value that
2+
is not an instance of :class:`int` to an attribute that corresponds to a C
3+
struct member of type T_UINT and T_ULONG. Fix a
4+
double :exc:`RuntimeWarning` emitted when assign a negative integer value to
5+
an attribute that corresponds to a C struct member of type T_UINT.

Python/structmember.c

Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -187,45 +187,74 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
187187
WARN("Truncation of value to int");
188188
break;
189189
}
190-
case T_UINT:{
191-
unsigned long ulong_val = PyLong_AsUnsignedLong(v);
192-
if ((ulong_val == (unsigned long)-1) && PyErr_Occurred()) {
193-
/* XXX: For compatibility, accept negative int values
194-
as well. */
195-
PyErr_Clear();
196-
ulong_val = PyLong_AsLong(v);
197-
if ((ulong_val == (unsigned long)-1) &&
198-
PyErr_Occurred())
190+
case T_UINT: {
191+
/* XXX: For compatibility, accept negative int values
192+
as well. */
193+
int overflow;
194+
long long_val = PyLong_AsLongAndOverflow(v, &overflow);
195+
if (long_val == -1 && PyErr_Occurred()) {
196+
return -1;
197+
}
198+
if (overflow < 0) {
199+
PyErr_SetString(PyExc_OverflowError,
200+
"Python int too large to convert to C long");
201+
return -1;
202+
}
203+
else if (!overflow) {
204+
*(unsigned int *)addr = (unsigned int)(unsigned long)long_val;
205+
if (long_val < 0) {
206+
WARN("Writing negative value into unsigned field");
207+
}
208+
else if ((unsigned long)long_val > UINT_MAX) {
209+
WARN("Truncation of value to unsigned short");
210+
}
211+
}
212+
else {
213+
unsigned long ulong_val = PyLong_AsUnsignedLong(v);
214+
if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) {
199215
return -1;
200-
*(unsigned int *)addr = (unsigned int)ulong_val;
201-
WARN("Writing negative value into unsigned field");
202-
} else
203-
*(unsigned int *)addr = (unsigned int)ulong_val;
204-
if (ulong_val > UINT_MAX)
205-
WARN("Truncation of value to unsigned int");
206-
break;
216+
}
217+
*(unsigned int*)addr = (unsigned int)ulong_val;
218+
if (ulong_val > UINT_MAX) {
219+
WARN("Truncation of value to unsigned int");
220+
}
207221
}
222+
break;
223+
}
208224
case T_LONG:{
209225
*(long*)addr = PyLong_AsLong(v);
210226
if ((*(long*)addr == -1) && PyErr_Occurred())
211227
return -1;
212228
break;
213229
}
214-
case T_ULONG:{
215-
*(unsigned long*)addr = PyLong_AsUnsignedLong(v);
216-
if ((*(unsigned long*)addr == (unsigned long)-1)
217-
&& PyErr_Occurred()) {
218-
/* XXX: For compatibility, accept negative int values
219-
as well. */
220-
PyErr_Clear();
221-
*(unsigned long*)addr = PyLong_AsLong(v);
222-
if ((*(unsigned long*)addr == (unsigned long)-1)
223-
&& PyErr_Occurred())
230+
case T_ULONG: {
231+
/* XXX: For compatibility, accept negative int values
232+
as well. */
233+
int overflow;
234+
long long_val = PyLong_AsLongAndOverflow(v, &overflow);
235+
if (long_val == -1 && PyErr_Occurred()) {
236+
return -1;
237+
}
238+
if (overflow < 0) {
239+
PyErr_SetString(PyExc_OverflowError,
240+
"Python int too large to convert to C long");
241+
return -1;
242+
}
243+
else if (!overflow) {
244+
*(unsigned long *)addr = (unsigned long)long_val;
245+
if (long_val < 0) {
246+
WARN("Writing negative value into unsigned field");
247+
}
248+
}
249+
else {
250+
unsigned long ulong_val = PyLong_AsUnsignedLong(v);
251+
if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) {
224252
return -1;
225-
WARN("Writing negative value into unsigned field");
253+
}
254+
*(unsigned long*)addr = ulong_val;
226255
}
227256
break;
228-
}
257+
}
229258
case T_PYSSIZET:{
230259
*(Py_ssize_t*)addr = PyLong_AsSsize_t(v);
231260
if ((*(Py_ssize_t*)addr == (Py_ssize_t)-1)

0 commit comments

Comments
 (0)