Skip to content

Commit 6d1ab20

Browse files
miss-islingtonsobolevn
authored andcommitted
gh-120384: Fix array-out-of-bounds crash in list_ass_subscript (GH-120442) (#120825)
gh-120384: Fix array-out-of-bounds crash in `list_ass_subscript` (GH-120442) (cherry picked from commit 8334a1b) Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
1 parent 1b0e63c commit 6d1ab20

File tree

4 files changed

+69
-12
lines changed

4 files changed

+69
-12
lines changed

Lib/test/list_tests.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,14 @@ def test_setslice(self):
192192

193193
self.assertRaises(TypeError, a.__setitem__)
194194

195+
def test_slice_assign_iterator(self):
196+
x = self.type2test(range(5))
197+
x[0:3] = reversed(range(3))
198+
self.assertEqual(x, self.type2test([2, 1, 0, 3, 4]))
199+
200+
x[:] = reversed(range(3))
201+
self.assertEqual(x, self.type2test([2, 1, 0]))
202+
195203
def test_delslice(self):
196204
a = self.type2test([0, 1])
197205
del a[1:2]

Lib/test/test_list.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,31 @@ def __eq__(self, other):
229229
list4 = [1]
230230
self.assertFalse(list3 == list4)
231231

232+
def test_lt_operator_modifying_operand(self):
233+
# See gh-120298
234+
class evil:
235+
def __lt__(self, other):
236+
other.clear()
237+
return NotImplemented
238+
239+
a = [[evil()]]
240+
with self.assertRaises(TypeError):
241+
a[0] < a
242+
243+
def test_list_index_modifing_operand(self):
244+
# See gh-120384
245+
class evil:
246+
def __init__(self, lst):
247+
self.lst = lst
248+
def __iter__(self):
249+
yield from self.lst
250+
self.lst.clear()
251+
252+
lst = list(range(5))
253+
operand = evil(lst)
254+
with self.assertRaises(ValueError):
255+
lst[::-1] = operand
256+
232257
@cpython_only
233258
def test_preallocation(self):
234259
iterable = [0] * 10
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix an array out of bounds crash in ``list_ass_subscript``, which could be
2+
invoked via some specificly tailored input: including concurrent modification
3+
of a list object, where one thread assigns a slice and another clears it.

Objects/listobject.c

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2927,6 +2927,23 @@ list_subscript(PyListObject* self, PyObject* item)
29272927
}
29282928
}
29292929

2930+
static Py_ssize_t
2931+
adjust_slice_indexes(PyListObject *lst,
2932+
Py_ssize_t *start, Py_ssize_t *stop,
2933+
Py_ssize_t step)
2934+
{
2935+
Py_ssize_t slicelength = PySlice_AdjustIndices(Py_SIZE(lst), start, stop,
2936+
step);
2937+
2938+
/* Make sure s[5:2] = [..] inserts at the right place:
2939+
before 5, not before 2. */
2940+
if ((step < 0 && *start < *stop) ||
2941+
(step > 0 && *start > *stop))
2942+
*stop = *start;
2943+
2944+
return slicelength;
2945+
}
2946+
29302947
static int
29312948
list_ass_subscript(PyListObject* self, PyObject* item, PyObject* value)
29322949
{
@@ -2939,22 +2956,11 @@ list_ass_subscript(PyListObject* self, PyObject* item, PyObject* value)
29392956
return list_ass_item(self, i, value);
29402957
}
29412958
else if (PySlice_Check(item)) {
2942-
Py_ssize_t start, stop, step, slicelength;
2959+
Py_ssize_t start, stop, step;
29432960

29442961
if (PySlice_Unpack(item, &start, &stop, &step) < 0) {
29452962
return -1;
29462963
}
2947-
slicelength = PySlice_AdjustIndices(Py_SIZE(self), &start, &stop,
2948-
step);
2949-
2950-
if (step == 1)
2951-
return list_ass_slice(self, start, stop, value);
2952-
2953-
/* Make sure s[5:2] = [..] inserts at the right place:
2954-
before 5, not before 2. */
2955-
if ((step < 0 && start < stop) ||
2956-
(step > 0 && start > stop))
2957-
stop = start;
29582964

29592965
if (value == NULL) {
29602966
/* delete slice */
@@ -2963,6 +2969,12 @@ list_ass_subscript(PyListObject* self, PyObject* item, PyObject* value)
29632969
Py_ssize_t i;
29642970
int res;
29652971

2972+
Py_ssize_t slicelength = adjust_slice_indexes(self, &start, &stop,
2973+
step);
2974+
2975+
if (step == 1)
2976+
return list_ass_slice(self, start, stop, value);
2977+
29662978
if (slicelength <= 0)
29672979
return 0;
29682980

@@ -3038,6 +3050,15 @@ list_ass_subscript(PyListObject* self, PyObject* item, PyObject* value)
30383050
if (!seq)
30393051
return -1;
30403052

3053+
Py_ssize_t slicelength = adjust_slice_indexes(self, &start, &stop,
3054+
step);
3055+
3056+
if (step == 1) {
3057+
int res = list_ass_slice(self, start, stop, seq);
3058+
Py_DECREF(seq);
3059+
return res;
3060+
}
3061+
30413062
if (PySequence_Fast_GET_SIZE(seq) != slicelength) {
30423063
PyErr_Format(PyExc_ValueError,
30433064
"attempt to assign sequence of "

0 commit comments

Comments
 (0)