Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Lib/test/test_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ def __eq__(self, other):

list1 = [X()]
list2 = [Y()]
self.assertTrue(list1 == list2)
self.assertFalse(list1 == list2)

list3 = [Z()]
list4 = [1]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fix a race condition in ``list.__lt__``, ``list.__le__``, ``list.__gt__``,
and ``list.__ge__``. Previously, under concurrent list mutation, the items
being compared could be replaced with other objects before the final comparison,
causing the final comparison result to be incorrect.
57 changes: 30 additions & 27 deletions Objects/listobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -3432,7 +3432,10 @@ list_richcompare_impl(PyObject *v, PyObject *w, int op)
Py_RETURN_TRUE;
}

/* Search for the first index where items are different */
/* Search for the first index where items are different.
* We incref vitem/witem before calling PyObject_RichCompareBool, which may
* release the GIL and allow the list to be mutated in the meantime.
*/
for (i = 0; i < Py_SIZE(vl) && i < Py_SIZE(wl); i++) {
PyObject *vitem = vl->ob_item[i];
PyObject *witem = wl->ob_item[i];
Expand All @@ -3443,36 +3446,36 @@ list_richcompare_impl(PyObject *v, PyObject *w, int op)
Py_INCREF(vitem);
Py_INCREF(witem);
int k = PyObject_RichCompareBool(vitem, witem, Py_EQ);
Py_DECREF(vitem);
Py_DECREF(witem);
if (k < 0)
if (k < 0) {
Py_DECREF(vitem);
Py_DECREF(witem);
return NULL;
if (!k)
break;
}

if (i >= Py_SIZE(vl) || i >= Py_SIZE(wl)) {
/* No more items to compare -- compare sizes */
Py_RETURN_RICHCOMPARE(Py_SIZE(vl), Py_SIZE(wl), op);
}
}
if (!k) {
/* We have a differing item -- shortcuts for EQ/NE */
if (op == Py_EQ) {
Py_DECREF(vitem);
Py_DECREF(witem);
Py_RETURN_FALSE;
}
if (op == Py_NE) {
Py_DECREF(vitem);
Py_DECREF(witem);
Py_RETURN_TRUE;
}
/* Compare the differing items using the proper operator */
PyObject *result = PyObject_RichCompare(vitem, witem, op);
Py_DECREF(vitem);
Py_DECREF(witem);
return result;
}

/* We have an item that differs -- shortcuts for EQ/NE */
if (op == Py_EQ) {
Py_RETURN_FALSE;
}
if (op == Py_NE) {
Py_RETURN_TRUE;
Py_DECREF(vitem);
Py_DECREF(witem);
}

/* Compare the final item again using the proper operator */
PyObject *vitem = vl->ob_item[i];
PyObject *witem = wl->ob_item[i];
Py_INCREF(vitem);
Py_INCREF(witem);
PyObject *result = PyObject_RichCompare(vl->ob_item[i], wl->ob_item[i], op);
Py_DECREF(vitem);
Py_DECREF(witem);
return result;
/* All compared elements were equal -- compare sizes */
Py_RETURN_RICHCOMPARE(Py_SIZE(vl), Py_SIZE(wl), op);
}

static PyObject *
Expand Down
Loading