Skip to content

Commit 3124618

Browse files
GH-97592: Fix crash in C remove_done_callback due to evil code (GH-97660)
Evil code could cause fut_callbacks to be cleared when PyObject_RichCompareBool is called. (cherry picked from commit 63780f4) Co-authored-by: Guido van Rossum <[email protected]>
1 parent cdc01ea commit 3124618

File tree

3 files changed

+23
-2
lines changed

3 files changed

+23
-2
lines changed

Lib/test/test_asyncio/test_futures.py

+15
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,21 @@ def __eq__(self, other):
832832

833833
fut.remove_done_callback(evil())
834834

835+
def test_remove_done_callbacks_list_clear(self):
836+
# see https://github.com/python/cpython/issues/97592 for details
837+
838+
fut = self._new_future()
839+
fut.add_done_callback(str)
840+
841+
for _ in range(63):
842+
fut.add_done_callback(id)
843+
844+
class evil:
845+
def __eq__(self, other):
846+
fut.remove_done_callback(other)
847+
848+
fut.remove_done_callback(evil())
849+
835850
def test_schedule_callbacks_list_mutation_1(self):
836851
# see http://bugs.python.org/issue28963 for details
837852

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Avoid a crash in the C version of :meth:`asyncio.Future.remove_done_callback` when an evil argument is passed.

Modules/_asynciomodule.c

+7-2
Original file line numberDiff line numberDiff line change
@@ -1052,7 +1052,11 @@ _asyncio_Future_remove_done_callback(FutureObj *self, PyObject *fn)
10521052
return NULL;
10531053
}
10541054

1055-
for (i = 0; i < PyList_GET_SIZE(self->fut_callbacks); i++) {
1055+
// Beware: PyObject_RichCompareBool below may change fut_callbacks.
1056+
// See GH-97592.
1057+
for (i = 0;
1058+
self->fut_callbacks != NULL && i < PyList_GET_SIZE(self->fut_callbacks);
1059+
i++) {
10561060
int ret;
10571061
PyObject *item = PyList_GET_ITEM(self->fut_callbacks, i);
10581062
Py_INCREF(item);
@@ -1071,7 +1075,8 @@ _asyncio_Future_remove_done_callback(FutureObj *self, PyObject *fn)
10711075
}
10721076
}
10731077

1074-
if (j == 0) {
1078+
// Note: fut_callbacks may have been cleared.
1079+
if (j == 0 || self->fut_callbacks == NULL) {
10751080
Py_CLEAR(self->fut_callbacks);
10761081
Py_DECREF(newlist);
10771082
return PyLong_FromSsize_t(len + cleared_callback0);

0 commit comments

Comments
 (0)