]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-132713: Fix repr(list) race condition (#132801)
authorVictor Stinner <vstinner@python.org>
Tue, 22 Apr 2025 20:09:35 +0000 (22:09 +0200)
committerGitHub <noreply@github.com>
Tue, 22 Apr 2025 20:09:35 +0000 (22:09 +0200)
Hold a strong reference to the item while calling repr(item).

Lib/test/test_list.py
Misc/NEWS.d/next/Core_and_Builtins/2025-04-22-16-38-43.gh-issue-132713.mBWTSZ.rst [new file with mode: 0644]
Objects/listobject.c

index ec65ed49281527a35e63b0232b0ae3c579bbf69b..6894fba2ad1b00c317fbf5a5cb53df47177baa7f 100644 (file)
@@ -118,6 +118,19 @@ class ListTest(list_tests.CommonTest):
         with self.assertRaises((MemoryError, OverflowError)):
             lst *= size
 
+    def test_repr_mutate(self):
+        class Obj:
+            @staticmethod
+            def __repr__():
+                try:
+                    mylist.pop()
+                except IndexError:
+                    pass
+                return 'obj'
+
+        mylist = [Obj() for _ in range(5)]
+        self.assertEqual(repr(mylist), '[obj, obj, obj]')
+
     def test_repr_large(self):
         # Check the repr of large list objects
         def check(n):
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-04-22-16-38-43.gh-issue-132713.mBWTSZ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-22-16-38-43.gh-issue-132713.mBWTSZ.rst
new file mode 100644 (file)
index 0000000..877b423
--- /dev/null
@@ -0,0 +1,2 @@
+Fix ``repr(list)`` race condition: hold a strong reference to the item while
+calling ``repr(item)``. Patch by Victor Stinner.
index 12d5b33414a92af29c20ee3a64a0327ae049e996..3f665a4929f14e33df536905bb70e725d5824b71 100644 (file)
@@ -583,6 +583,7 @@ list_repr_impl(PyListObject *v)
     /* "[" + "1" + ", 2" * (len - 1) + "]" */
     Py_ssize_t prealloc = 1 + 1 + (2 + 1) * (Py_SIZE(v) - 1) + 1;
     PyUnicodeWriter *writer = PyUnicodeWriter_Create(prealloc);
+    PyObject *item = NULL;
     if (writer == NULL) {
         goto error;
     }
@@ -594,6 +595,13 @@ list_repr_impl(PyListObject *v)
     /* Do repr() on each element.  Note that this may mutate the list,
        so must refetch the list size on each iteration. */
     for (Py_ssize_t i = 0; i < Py_SIZE(v); ++i) {
+        item = list_get_item_ref(v, i);
+        if (item == NULL) {
+            // List truncated while iterating on it
+            PyErr_Clear();
+            break;
+        }
+
         if (i > 0) {
             if (PyUnicodeWriter_WriteChar(writer, ',') < 0) {
                 goto error;
@@ -603,9 +611,10 @@ list_repr_impl(PyListObject *v)
             }
         }
 
-        if (PyUnicodeWriter_WriteRepr(writer, v->ob_item[i]) < 0) {
+        if (PyUnicodeWriter_WriteRepr(writer, item) < 0) {
             goto error;
         }
+        Py_CLEAR(item);
     }
 
     if (PyUnicodeWriter_WriteChar(writer, ']') < 0) {
@@ -616,6 +625,7 @@ list_repr_impl(PyListObject *v)
     return PyUnicodeWriter_Finish(writer);
 
 error:
+    Py_XDECREF(item);
     PyUnicodeWriter_Discard(writer);
     Py_ReprLeave((PyObject *)v);
     return NULL;