Skip to content

Commit d9e561d

Browse files
ZackerySpytzpablogsal
authored andcommitted
bpo-38610: Fix possible crashes in several list methods (GH-17022)
Hold strong references to list elements while calling PyObject_RichCompareBool().
1 parent 09c482f commit d9e561d

File tree

3 files changed

+40
-3
lines changed

3 files changed

+40
-3
lines changed

Lib/test/test_list.py

+26
Original file line numberDiff line numberDiff line change
@@ -171,5 +171,31 @@ def test_preallocation(self):
171171
self.assertEqual(iter_size, sys.getsizeof(list([0] * 10)))
172172
self.assertEqual(iter_size, sys.getsizeof(list(range(10))))
173173

174+
def test_count_index_remove_crashes(self):
175+
# bpo-38610: The count(), index(), and remove() methods were not
176+
# holding strong references to list elements while calling
177+
# PyObject_RichCompareBool().
178+
class X:
179+
def __eq__(self, other):
180+
lst.clear()
181+
return NotImplemented
182+
183+
lst = [X()]
184+
with self.assertRaises(ValueError):
185+
lst.index(lst)
186+
187+
class L(list):
188+
def __eq__(self, other):
189+
str(other)
190+
return NotImplemented
191+
192+
lst = L([X()])
193+
lst.count(lst)
194+
195+
lst = L([X()])
196+
with self.assertRaises(ValueError):
197+
lst.remove(lst)
198+
199+
174200
if __name__ == "__main__":
175201
unittest.main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix possible crashes in several list methods by holding strong references to
2+
list elements when calling :c:func:`PyObject_RichCompareBool`.

Objects/listobject.c

+12-3
Original file line numberDiff line numberDiff line change
@@ -2553,7 +2553,10 @@ list_index_impl(PyListObject *self, PyObject *value, Py_ssize_t start,
25532553
stop = 0;
25542554
}
25552555
for (i = start; i < stop && i < Py_SIZE(self); i++) {
2556-
int cmp = PyObject_RichCompareBool(self->ob_item[i], value, Py_EQ);
2556+
PyObject *obj = self->ob_item[i];
2557+
Py_INCREF(obj);
2558+
int cmp = PyObject_RichCompareBool(obj, value, Py_EQ);
2559+
Py_DECREF(obj);
25572560
if (cmp > 0)
25582561
return PyLong_FromSsize_t(i);
25592562
else if (cmp < 0)
@@ -2580,7 +2583,10 @@ list_count(PyListObject *self, PyObject *value)
25802583
Py_ssize_t i;
25812584

25822585
for (i = 0; i < Py_SIZE(self); i++) {
2583-
int cmp = PyObject_RichCompareBool(self->ob_item[i], value, Py_EQ);
2586+
PyObject *obj = self->ob_item[i];
2587+
Py_INCREF(obj);
2588+
int cmp = PyObject_RichCompareBool(obj, value, Py_EQ);
2589+
Py_DECREF(obj);
25842590
if (cmp > 0)
25852591
count++;
25862592
else if (cmp < 0)
@@ -2607,7 +2613,10 @@ list_remove(PyListObject *self, PyObject *value)
26072613
Py_ssize_t i;
26082614

26092615
for (i = 0; i < Py_SIZE(self); i++) {
2610-
int cmp = PyObject_RichCompareBool(self->ob_item[i], value, Py_EQ);
2616+
PyObject *obj = self->ob_item[i];
2617+
Py_INCREF(obj);
2618+
int cmp = PyObject_RichCompareBool(obj, value, Py_EQ);
2619+
Py_DECREF(obj);
26112620
if (cmp > 0) {
26122621
if (list_ass_slice(self, i, i+1,
26132622
(PyObject *)NULL) == 0)

0 commit comments

Comments
 (0)