gh-148442: Eliminate race condition in list rich comparison (GH-148531)
diff --git a/Lib/test/test_list.py b/Lib/test/test_list.py
index 44ecd62..1b0724c 100644
--- a/Lib/test/test_list.py
+++ b/Lib/test/test_list.py
@@ -244,7 +244,7 @@ def __eq__(self, other):
 
         list1 = [X()]
         list2 = [Y()]
-        self.assertTrue(list1 == list2)
+        self.assertFalse(list1 == list2)
 
         list3 = [Z()]
         list4 = [1]
@@ -261,6 +261,54 @@ def __lt__(self, other):
         with self.assertRaises(TypeError):
             a[0] < a
 
+    def test_richcompare_stale_element_list_vitem(self):
+        # gh-148442: list_richcompare_impl must use the captured vitem for
+        # the final ordering comparison, not re-read list1's slot after __eq__
+        # may have mutated it.
+        #
+        # x.__eq__(0) puts AlwaysLT() into list1[0] and returns False.
+        class AlwaysLT:
+            def __eq__(self, other: object) -> bool:
+                return False
+
+            def __gt__(self, other: object) -> bool:
+                return False
+
+        class Mutating:
+            def __eq__(self, other: object) -> bool:
+                list1[0] = AlwaysLT()
+                return False
+
+            def __gt__(self, other: object) -> bool:
+                return True
+
+        list1 = [Mutating(), 0]
+        list2 = [0, 0]
+        self.assertTrue(list1 > list2)
+
+    def test_richcompare_stale_element_list_witem(self):
+        # gh-148442: list_richcompare_impl must use the captured witem for
+        # the final ordering comparison, not re-read list2's slot after __eq__
+        # may have mutated it.
+        #
+        # x.__eq__(0) puts AlwaysGT() into list2[0] and returns False.
+        class AlwaysGT:
+            pass
+
+        class Mutating:
+            def __eq__(self, other: object) -> bool:
+                list2[0] = AlwaysGT()
+                return False
+
+            def __gt__(self, other: object) -> bool:
+                if isinstance(other, AlwaysGT):
+                    return False  # pretend AlwaysGT beats us
+                return True       # beat everything else (including 0)
+
+        list1 = [Mutating(), 0]
+        list2 = [0, 0]
+        self.assertTrue(list1 > list2)
+
     def test_list_index_modifing_operand(self):
         # See gh-120384
         class evil:
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-04-13-00-00-00.gh-issue-148442.aBcDeF.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-13-00-00-00.gh-issue-148442.aBcDeF.rst
new file mode 100644
index 0000000..e30a3de
--- /dev/null
+++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-13-00-00-00.gh-issue-148442.aBcDeF.rst
@@ -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.
diff --git a/Objects/listobject.c b/Objects/listobject.c
index 8a9c9bd..81eb3e1 100644
--- a/Objects/listobject.c
+++ b/Objects/listobject.c
@@ -3454,7 +3454,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];
@@ -3465,36 +3468,36 @@ list_richcompare_impl(PyObject *v, PyObject *w, int op)
         Py_INCREF(vitem);
         Py_INCREF(witem);
         int k = PyObject_RichCompareBool(vitem, witem, Py_EQ);
+        if (k < 0) {
+            Py_DECREF(vitem);
+            Py_DECREF(witem);
+            return NULL;
+        }
+        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;
+        }
+
         Py_DECREF(vitem);
         Py_DECREF(witem);
-        if (k < 0)
-            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);
-    }
-
-    /* We have an item that differs -- shortcuts for EQ/NE */
-    if (op == Py_EQ) {
-        Py_RETURN_FALSE;
-    }
-    if (op == Py_NE) {
-        Py_RETURN_TRUE;
-    }
-
-    /* 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 *