gh-132713: Fix repr(list) race condition (#132801)

Hold a strong reference to the item while calling repr(item).
This commit is contained in:
Victor Stinner 2025-04-22 22:09:35 +02:00 committed by GitHub
parent 722c501dba
commit a4ea80d523
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 26 additions and 1 deletions

View 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):

View File

@ -0,0 +1,2 @@
Fix ``repr(list)`` race condition: hold a strong reference to the item while
calling ``repr(item)``. Patch by Victor Stinner.

View 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;