From 2edc8652fefd20cc038fb0204a85fe5e60a6fd4a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 22 Apr 2025 22:09:35 +0200 Subject: [PATCH 1/2] gh-132713: Fix repr(list) race condition (#132801) Hold a strong reference to the item while calling repr(item). (cherry picked from commit a4ea80d52394bafffb2257abbe815c7ffdb003a3) --- Lib/test/test_list.py | 13 +++++++++++++ .../2025-04-22-16-38-43.gh-issue-132713.mBWTSZ.rst | 2 ++ Objects/listobject.c | 12 +++++++++++- 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2025-04-22-16-38-43.gh-issue-132713.mBWTSZ.rst diff --git a/Lib/test/test_list.py b/Lib/test/test_list.py index 005374f429399f..23ef902aa0be34 100644 --- a/Lib/test/test_list.py +++ b/Lib/test/test_list.py @@ -116,6 +116,19 @@ def test_list_resize_overflow(self): 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 index 00000000000000..877b42374a396e --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2025-04-22-16-38-43.gh-issue-132713.mBWTSZ.rst @@ -0,0 +1,2 @@ +Fix ``repr(list)`` race condition: hold a strong reference to the item while +calling ``repr(item)``. Patch by Victor Stinner. diff --git a/Objects/listobject.c b/Objects/listobject.c index 65a8b06d978df4..6d808485e7babf 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -585,6 +585,7 @@ list_repr_impl(PyListObject *v) { PyObject *s; _PyUnicodeWriter writer; + PyObject *item = NULL; Py_ssize_t i = Py_ReprEnter((PyObject*)v); if (i != 0) { return i > 0 ? PyUnicode_FromString("[...]") : NULL; @@ -601,12 +602,19 @@ 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 (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_WriteASCIIString(&writer, ", ", 2) < 0) goto error; } - s = PyObject_Repr(v->ob_item[i]); + s = PyObject_Repr(item); if (s == NULL) goto error; @@ -615,6 +623,7 @@ list_repr_impl(PyListObject *v) goto error; } Py_DECREF(s); + Py_CLEAR(item); } writer.overallocate = 0; @@ -625,6 +634,7 @@ list_repr_impl(PyListObject *v) return _PyUnicodeWriter_Finish(&writer); error: + Py_XDECREF(item); _PyUnicodeWriter_Dealloc(&writer); Py_ReprLeave((PyObject *)v); return NULL; From 00f556bbf0a77befb29ae24fff23a45f67e774b0 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 23 Apr 2025 12:04:58 +0200 Subject: [PATCH 2/2] Simplify list_repr_impl() --- Objects/listobject.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Objects/listobject.c b/Objects/listobject.c index 6d808485e7babf..04a31f1f34a4bb 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -602,12 +602,8 @@ 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 (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; - } + /* Hold a strong reference since repr(item) can mutate the list */ + item = Py_NewRef(v->ob_item[i]); if (i > 0) { if (_PyUnicodeWriter_WriteASCIIString(&writer, ", ", 2) < 0)