Skip to content

Commit 8a0e92b

Browse files
authored
Merge branch '3.13' into backport-29acc08-3.13
2 parents 8929009 + b4a84bc commit 8a0e92b

File tree

9 files changed

+139
-15
lines changed

9 files changed

+139
-15
lines changed

Doc/library/enum.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,12 @@ Module Contents
153153

154154
Return a list of all power-of-two integers contained in a flag.
155155

156+
:func:`enum.bin`
157+
158+
Like built-in :func:`bin`, except negative values are represented in
159+
two's complement, and the leading bit always indicates sign
160+
(``0`` implies positive, ``1`` implies negative).
161+
156162

157163
.. versionadded:: 3.6 ``Flag``, ``IntFlag``, ``auto``
158164
.. versionadded:: 3.11 ``StrEnum``, ``EnumCheck``, ``ReprEnum``, ``FlagBoundary``, ``property``, ``member``, ``nonmember``, ``global_enum``, ``show_flag_values``
@@ -1034,6 +1040,19 @@ Utilities and Decorators
10341040

10351041
.. versionadded:: 3.11
10361042

1043+
.. function:: bin(num, max_bits=None)
1044+
1045+
Like built-in :func:`bin`, except negative values are represented in
1046+
two's complement, and the leading bit always indicates sign
1047+
(``0`` implies positive, ``1`` implies negative).
1048+
1049+
>>> import enum
1050+
>>> enum.bin(10)
1051+
'0b0 1010'
1052+
>>> enum.bin(~10) # ~10 is -11
1053+
'0b1 0101'
1054+
1055+
.. versionadded:: 3.10
10371056

10381057
---------------
10391058

Doc/library/functions.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ are always available. They are listed here in alphabetical order.
138138
>>> f'{14:#b}', f'{14:b}'
139139
('0b1110', '1110')
140140

141+
See also :func:`enum.bin` to represent negative values as twos-complement.
142+
141143
See also :func:`format` for more information.
142144

143145

Lib/enum.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ def show_flag_values(value):
130130
def bin(num, max_bits=None):
131131
"""
132132
Like built-in bin(), except negative values are represented in
133-
twos-compliment, and the leading bit always indicates sign
133+
twos-complement, and the leading bit always indicates sign
134134
(0=positive, 1=negative).
135135
136136
>>> bin(10)
@@ -139,6 +139,7 @@ def bin(num, max_bits=None):
139139
'0b1 0101'
140140
"""
141141

142+
num = num.__index__()
142143
ceiling = 2 ** (num).bit_length()
143144
if num >= 0:
144145
s = bltns.bin(num + ceiling).replace('1', '0', 1)

Lib/test/_test_multiprocessing.py

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,14 +1484,11 @@ def test_repr_rlock(self):
14841484
for i in range(n):
14851485
self.assertIn(f'<RLock(MainProcess|T{i+1}, {i+1})>', l)
14861486

1487-
1488-
t = threading.Thread(target=self._acquire_release,
1489-
args=(lock, 0.2),
1490-
name=f'T1')
1487+
rlock = self.RLock()
1488+
t = threading.Thread(target=rlock.acquire)
14911489
t.start()
1492-
time.sleep(0.1)
1493-
self.assertEqual('<RLock(SomeOtherThread, nonzero)>', repr(lock))
1494-
time.sleep(0.2)
1490+
t.join()
1491+
self.assertEqual('<RLock(SomeOtherThread, nonzero)>', repr(rlock))
14951492

14961493
pname = 'P1'
14971494
l = multiprocessing.Manager().list()
@@ -1502,14 +1499,11 @@ def test_repr_rlock(self):
15021499
p.join()
15031500
self.assertEqual(f'<RLock({pname}, 1)>', l[0])
15041501

1505-
event = self.Event()
1506-
lock = self.RLock()
1507-
p = self.Process(target=self._acquire_event,
1508-
args=(lock, event))
1502+
rlock = self.RLock()
1503+
p = self.Process(target=self._acquire, args=(rlock,))
15091504
p.start()
1510-
event.wait()
1511-
self.assertEqual('<RLock(SomeOtherProcess, nonzero)>', repr(lock))
15121505
p.join()
1506+
self.assertEqual('<RLock(SomeOtherProcess, nonzero)>', repr(rlock))
15131507

15141508
def test_rlock(self):
15151509
lock = self.RLock()

Lib/test/test_array.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from test.support import import_helper
99
from test.support import os_helper
1010
from test.support import _2G
11+
from test.support import subTests
1112
import weakref
1213
import pickle
1314
import operator
@@ -1680,6 +1681,45 @@ def test_gh_128961(self):
16801681
it.__setstate__(0)
16811682
self.assertRaises(StopIteration, next, it)
16821683

1684+
# Tests for NULL pointer dereference in array.__setitem__
1685+
# when the index conversion mutates the array.
1686+
# See: https://github.com/python/cpython/issues/142555.
1687+
1688+
@subTests("dtype", ["b", "B", "h", "H", "i", "l", "q", "I", "L", "Q"])
1689+
def test_setitem_use_after_clear_with_int_data(self, dtype):
1690+
victim = array.array(dtype, list(range(64)))
1691+
1692+
class Index:
1693+
def __index__(self):
1694+
victim.clear()
1695+
return 0
1696+
1697+
self.assertRaises(IndexError, victim.__setitem__, 1, Index())
1698+
self.assertEqual(len(victim), 0)
1699+
1700+
def test_setitem_use_after_shrink_with_int_data(self):
1701+
victim = array.array('b', [1, 2, 3])
1702+
1703+
class Index:
1704+
def __index__(self):
1705+
victim.pop()
1706+
victim.pop()
1707+
return 0
1708+
1709+
self.assertRaises(IndexError, victim.__setitem__, 1, Index())
1710+
1711+
@subTests("dtype", ["f", "d"])
1712+
def test_setitem_use_after_clear_with_float_data(self, dtype):
1713+
victim = array.array(dtype, [1.0, 2.0, 3.0])
1714+
1715+
class Float:
1716+
def __float__(self):
1717+
victim.clear()
1718+
return 0.0
1719+
1720+
self.assertRaises(IndexError, victim.__setitem__, 1, Float())
1721+
self.assertEqual(len(victim), 0)
1722+
16831723

16841724
if __name__ == "__main__":
16851725
unittest.main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add documentation for :func:`enum.bin`.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:mod:`array`: fix a crash in ``a[i] = v`` when converting *i* to
2+
an index via :meth:`i.__index__ <object.__index__>` or :meth:`i.__float__
3+
<object.__float__>` mutates the array.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a flaky test in ``test_repr_rlock`` that checks the representation of :class:`multiprocessing.RLock`.

Modules/arraymodule.c

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,33 @@ Note that the basic Get and Set functions do NOT check that the index is
202202
in bounds; that's the responsibility of the caller.
203203
****************************************************************************/
204204

205+
/* Macro to check array buffer validity and bounds after calling
206+
user-defined methods (like __index__ or __float__) that might modify
207+
the array during the call.
208+
*/
209+
#define CHECK_ARRAY_BOUNDS(OP, IDX) \
210+
do { \
211+
if ((IDX) >= 0 && ((OP)->ob_item == NULL || \
212+
(IDX) >= Py_SIZE((OP)))) { \
213+
PyErr_SetString(PyExc_IndexError, \
214+
"array assignment index out of range"); \
215+
return -1; \
216+
} \
217+
} while (0)
218+
219+
#define CHECK_ARRAY_BOUNDS_WITH_CLEANUP(OP, IDX, VAL, CLEANUP) \
220+
do { \
221+
if ((IDX) >= 0 && ((OP)->ob_item == NULL || \
222+
(IDX) >= Py_SIZE((OP)))) { \
223+
PyErr_SetString(PyExc_IndexError, \
224+
"array assignment index out of range"); \
225+
if (CLEANUP) { \
226+
Py_DECREF(VAL); \
227+
} \
228+
return -1; \
229+
} \
230+
} while (0)
231+
205232
static PyObject *
206233
b_getitem(arrayobject *ap, Py_ssize_t i)
207234
{
@@ -218,7 +245,10 @@ b_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
218245
the overflow checking */
219246
if (!PyArg_Parse(v, "h;array item must be integer", &x))
220247
return -1;
221-
else if (x < -128) {
248+
249+
CHECK_ARRAY_BOUNDS(ap, i);
250+
251+
if (x < -128) {
222252
PyErr_SetString(PyExc_OverflowError,
223253
"signed char is less than minimum");
224254
return -1;
@@ -247,6 +277,9 @@ BB_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
247277
/* 'B' == unsigned char, maps to PyArg_Parse's 'b' formatter */
248278
if (!PyArg_Parse(v, "b;array item must be integer", &x))
249279
return -1;
280+
281+
CHECK_ARRAY_BOUNDS(ap, i);
282+
250283
if (i >= 0)
251284
((unsigned char *)ap->ob_item)[i] = x;
252285
return 0;
@@ -323,6 +356,9 @@ h_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
323356
/* 'h' == signed short, maps to PyArg_Parse's 'h' formatter */
324357
if (!PyArg_Parse(v, "h;array item must be integer", &x))
325358
return -1;
359+
360+
CHECK_ARRAY_BOUNDS(ap, i);
361+
326362
if (i >= 0)
327363
((short *)ap->ob_item)[i] = x;
328364
return 0;
@@ -352,6 +388,9 @@ HH_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
352388
"unsigned short is greater than maximum");
353389
return -1;
354390
}
391+
392+
CHECK_ARRAY_BOUNDS(ap, i);
393+
355394
if (i >= 0)
356395
((short *)ap->ob_item)[i] = (short)x;
357396
return 0;
@@ -370,6 +409,9 @@ i_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
370409
/* 'i' == signed int, maps to PyArg_Parse's 'i' formatter */
371410
if (!PyArg_Parse(v, "i;array item must be integer", &x))
372411
return -1;
412+
413+
CHECK_ARRAY_BOUNDS(ap, i);
414+
373415
if (i >= 0)
374416
((int *)ap->ob_item)[i] = x;
375417
return 0;
@@ -410,6 +452,9 @@ II_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
410452
}
411453
return -1;
412454
}
455+
456+
CHECK_ARRAY_BOUNDS_WITH_CLEANUP(ap, i, v, do_decref);
457+
413458
if (i >= 0)
414459
((unsigned int *)ap->ob_item)[i] = (unsigned int)x;
415460

@@ -431,6 +476,9 @@ l_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
431476
long x;
432477
if (!PyArg_Parse(v, "l;array item must be integer", &x))
433478
return -1;
479+
480+
CHECK_ARRAY_BOUNDS(ap, i);
481+
434482
if (i >= 0)
435483
((long *)ap->ob_item)[i] = x;
436484
return 0;
@@ -462,6 +510,9 @@ LL_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
462510
}
463511
return -1;
464512
}
513+
514+
CHECK_ARRAY_BOUNDS_WITH_CLEANUP(ap, i, v, do_decref);
515+
465516
if (i >= 0)
466517
((unsigned long *)ap->ob_item)[i] = x;
467518

@@ -483,6 +534,9 @@ q_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
483534
long long x;
484535
if (!PyArg_Parse(v, "L;array item must be integer", &x))
485536
return -1;
537+
538+
CHECK_ARRAY_BOUNDS(ap, i);
539+
486540
if (i >= 0)
487541
((long long *)ap->ob_item)[i] = x;
488542
return 0;
@@ -515,6 +569,9 @@ QQ_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
515569
}
516570
return -1;
517571
}
572+
573+
CHECK_ARRAY_BOUNDS_WITH_CLEANUP(ap, i, v, do_decref);
574+
518575
if (i >= 0)
519576
((unsigned long long *)ap->ob_item)[i] = x;
520577

@@ -536,6 +593,9 @@ f_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
536593
float x;
537594
if (!PyArg_Parse(v, "f;array item must be float", &x))
538595
return -1;
596+
597+
CHECK_ARRAY_BOUNDS(ap, i);
598+
539599
if (i >= 0)
540600
((float *)ap->ob_item)[i] = x;
541601
return 0;
@@ -553,6 +613,9 @@ d_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
553613
double x;
554614
if (!PyArg_Parse(v, "d;array item must be float", &x))
555615
return -1;
616+
617+
CHECK_ARRAY_BOUNDS(ap, i);
618+
556619
if (i >= 0)
557620
((double *)ap->ob_item)[i] = x;
558621
return 0;

0 commit comments

Comments
 (0)