Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 63780f4

Browse filesBrowse files
authored
GH-97592: Fix crash in C remove_done_callback due to evil code (#97660)
Evil code could cause fut_callbacks to be cleared when PyObject_RichCompareBool is called.
1 parent e9d6376 commit 63780f4
Copy full SHA for 63780f4

File tree

Expand file treeCollapse file tree

3 files changed

+23
-2
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+23
-2
lines changed

‎Lib/test/test_asyncio/test_futures.py

Copy file name to clipboardExpand all lines: Lib/test/test_asyncio/test_futures.py
+15Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,21 @@ def __eq__(self, other):
837837

838838
fut.remove_done_callback(evil())
839839

840+
def test_remove_done_callbacks_list_clear(self):
841+
# see https://github.com/python/cpython/issues/97592 for details
842+
843+
fut = self._new_future()
844+
fut.add_done_callback(str)
845+
846+
for _ in range(63):
847+
fut.add_done_callback(id)
848+
849+
class evil:
850+
def __eq__(self, other):
851+
fut.remove_done_callback(other)
852+
853+
fut.remove_done_callback(evil())
854+
840855
def test_schedule_callbacks_list_mutation_1(self):
841856
# see http://bugs.python.org/issue28963 for details
842857

+1Lines changed: 1 addition & 0 deletions
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

Copy file name to clipboardExpand all lines: Modules/_asynciomodule.c
+7-2Lines changed: 7 additions & 2 deletions
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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.