Skip to content

Commit 71fc725

Browse files
committed
Fix null pointer dereference in array.__setitem__ via re-entrant __index__
1 parent af7cca3 commit 71fc725

File tree

3 files changed

+31
-1
lines changed

3 files changed

+31
-1
lines changed

Lib/test/test_array.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1680,6 +1680,23 @@ def test_gh_128961(self):
16801680
it.__setstate__(0)
16811681
self.assertRaises(StopIteration, next, it)
16821682

1683+
def test_gh_142555(self):
1684+
# Test for null pointer dereference in array.__setitem__
1685+
# via re-entrant __index__.
1686+
victim = array.array('b', [0] * 64)
1687+
1688+
class EvilIndex:
1689+
def __index__(self):
1690+
# Re-entrant mutation: shrink the array while __setitem__
1691+
# still holds a pointer to the pre-clear buffer.
1692+
victim.clear()
1693+
return 0
1694+
1695+
with self.assertRaises(IndexError):
1696+
victim[1] = EvilIndex()
1697+
1698+
self.assertEqual(len(victim), 0)
1699+
16831700

16841701
if __name__ == "__main__":
16851702
unittest.main()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix null pointer dereference in :class:`array.array.__setitem__` via
2+
an user-defined ``__index__`` method which modify the array during index
3+
conversion.

Modules/arraymodule.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,17 @@ b_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
221221
the overflow checking */
222222
if (!PyArg_Parse(v, "h;array item must be integer", &x))
223223
return -1;
224-
else if (x < -128) {
224+
225+
/* Check buffer validity after PyArg_Parse which may call user-defined
226+
* __index__ on v, which might modify the array buffer. See gh-142555.
227+
*/
228+
if (i >= 0 && ap->ob_item == NULL) {
229+
PyErr_SetString(PyExc_IndexError,
230+
"array assignment index out of range");
231+
return -1;
232+
}
233+
234+
if (x < -128) {
225235
PyErr_SetString(PyExc_OverflowError,
226236
"signed char is less than minimum");
227237
return -1;

0 commit comments

Comments
 (0)