Skip to content

Commit c408c57

Browse files
committed
Check other setitem methods
1 parent ad18d48 commit c408c57

File tree

2 files changed

+63
-2
lines changed

2 files changed

+63
-2
lines changed

Lib/test/test_array.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1710,6 +1710,20 @@ def __index__(self):
17101710
with self.assertRaises(IndexError):
17111711
victim[1] = ShrinkIndex() # Original index 1 should now be out of bounds
17121712

1713+
def test_clear_array_float(victim):
1714+
"""Test array clearing scenario using __float__ method"""
1715+
class EvilFloat:
1716+
def __float__(self):
1717+
# Re-entrant mutation: clear the array while __setitem__
1718+
# still holds a pointer to the pre-clear buffer.
1719+
victim.clear()
1720+
return 0.0
1721+
1722+
with self.assertRaises(IndexError):
1723+
victim[1] = EvilFloat()
1724+
1725+
self.assertEqual(len(victim), 0)
1726+
17131727
# Test various array types
17141728
test_clear_array(array.array('b', [0] * 64))
17151729
test_shrink_array(array.array('b', [1, 2, 3]))
@@ -1719,8 +1733,13 @@ def __index__(self):
17191733
test_clear_array(array.array('i', [1, 2, 3]))
17201734
test_clear_array(array.array('l', [1, 2, 3]))
17211735
test_clear_array(array.array('q', [1, 2, 3]))
1722-
test_clear_array(array.array('f', [1.0, 2.0, 3.0]))
1723-
test_clear_array(array.array('d', [1.0, 2.0, 3.0]))
1736+
test_clear_array(array.array('I', [1, 2, 3]))
1737+
test_clear_array(array.array('L', [1, 2, 3]))
1738+
test_clear_array(array.array('Q', [1, 2, 3]))
1739+
1740+
# Test float arrays with __float__ method
1741+
test_clear_array_float(array.array('f', [1.0, 2.0, 3.0]))
1742+
test_clear_array_float(array.array('d', [1.0, 2.0, 3.0]))
17241743

17251744

17261745
if __name__ == "__main__":

Modules/arraymodule.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,20 @@ II_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
479479
}
480480
return -1;
481481
}
482+
483+
/* Check buffer validity and bounds after potential user code calls
484+
* (_PyNumber_Index and PyLong_AsUnsignedLong may modify the array buffer).
485+
* See gh-142555.
486+
*/
487+
if (i >= 0 && (ap->ob_item == NULL || i >= Py_SIZE(ap))) {
488+
PyErr_SetString(PyExc_IndexError,
489+
"array assignment index out of range");
490+
if (do_decref) {
491+
Py_DECREF(v);
492+
}
493+
return -1;
494+
}
495+
482496
if (i >= 0)
483497
((unsigned int *)ap->ob_item)[i] = (unsigned int)x;
484498

@@ -541,6 +555,20 @@ LL_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
541555
}
542556
return -1;
543557
}
558+
559+
/* Check buffer validity and bounds after potential user code calls
560+
* (_PyNumber_Index and PyLong_AsUnsignedLong may modify the array buffer).
561+
* See gh-142555.
562+
*/
563+
if (i >= 0 && (ap->ob_item == NULL || i >= Py_SIZE(ap))) {
564+
PyErr_SetString(PyExc_IndexError,
565+
"array assignment index out of range");
566+
if (do_decref) {
567+
Py_DECREF(v);
568+
}
569+
return -1;
570+
}
571+
544572
if (i >= 0)
545573
((unsigned long *)ap->ob_item)[i] = x;
546574

@@ -604,6 +632,20 @@ QQ_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
604632
}
605633
return -1;
606634
}
635+
636+
/* Check buffer validity and bounds after potential user code calls
637+
* (_PyNumber_Index and PyLong_AsUnsignedLongLong may modify the array buffer).
638+
* See gh-142555.
639+
*/
640+
if (i >= 0 && (ap->ob_item == NULL || i >= Py_SIZE(ap))) {
641+
PyErr_SetString(PyExc_IndexError,
642+
"array assignment index out of range");
643+
if (do_decref) {
644+
Py_DECREF(v);
645+
}
646+
return -1;
647+
}
648+
607649
if (i >= 0)
608650
((unsigned long long *)ap->ob_item)[i] = x;
609651

0 commit comments

Comments
 (0)