summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2022-09-30 19:57:09 (GMT)
committerGitHub <noreply@github.com>2022-09-30 19:57:09 (GMT)
commit63780f4599acc2c5ee8af5f37ab76c162ad21065 (patch)
tree541db070bca69edcb73a5f344b14d4493080d57d
parente9d63760fea8748638f6e495b5b07bd1805c9591 (diff)
downloadcpython-63780f4599acc2c5ee8af5f37ab76c162ad21065.zip
cpython-63780f4599acc2c5ee8af5f37ab76c162ad21065.tar.gz
cpython-63780f4599acc2c5ee8af5f37ab76c162ad21065.tar.bz2
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.
-rw-r--r--Lib/test/test_asyncio/test_futures.py15
-rw-r--r--Misc/NEWS.d/next/Library/2022-09-29-23-22-24.gh-issue-97592.tpJg_J.rst1
-rw-r--r--Modules/_asynciomodule.c9
3 files changed, 23 insertions, 2 deletions
diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py
index 11d4273..3dc6b65 100644
--- a/Lib/test/test_asyncio/test_futures.py
+++ b/Lib/test/test_asyncio/test_futures.py
@@ -837,6 +837,21 @@ class BaseFutureDoneCallbackTests():
fut.remove_done_callback(evil())
+ def test_remove_done_callbacks_list_clear(self):
+ # see https://github.com/python/cpython/issues/97592 for details
+
+ fut = self._new_future()
+ fut.add_done_callback(str)
+
+ for _ in range(63):
+ fut.add_done_callback(id)
+
+ class evil:
+ def __eq__(self, other):
+ fut.remove_done_callback(other)
+
+ fut.remove_done_callback(evil())
+
def test_schedule_callbacks_list_mutation_1(self):
# see http://bugs.python.org/issue28963 for details
diff --git a/Misc/NEWS.d/next/Library/2022-09-29-23-22-24.gh-issue-97592.tpJg_J.rst b/Misc/NEWS.d/next/Library/2022-09-29-23-22-24.gh-issue-97592.tpJg_J.rst
new file mode 100644
index 0000000..aa245cf
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-09-29-23-22-24.gh-issue-97592.tpJg_J.rst
@@ -0,0 +1 @@
+Avoid a crash in the C version of :meth:`asyncio.Future.remove_done_callback` when an evil argument is passed.
diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index 5a5881b..90917115 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -1052,7 +1052,11 @@ _asyncio_Future_remove_done_callback(FutureObj *self, PyObject *fn)
return NULL;
}
- for (i = 0; i < PyList_GET_SIZE(self->fut_callbacks); i++) {
+ // Beware: PyObject_RichCompareBool below may change fut_callbacks.
+ // See GH-97592.
+ for (i = 0;
+ self->fut_callbacks != NULL && i < PyList_GET_SIZE(self->fut_callbacks);
+ i++) {
int ret;
PyObject *item = PyList_GET_ITEM(self->fut_callbacks, i);
Py_INCREF(item);
@@ -1071,7 +1075,8 @@ _asyncio_Future_remove_done_callback(FutureObj *self, PyObject *fn)
}
}
- if (j == 0) {
+ // Note: fut_callbacks may have been cleared.
+ if (j == 0 || self->fut_callbacks == NULL) {
Py_CLEAR(self->fut_callbacks);
Py_DECREF(newlist);
return PyLong_FromSsize_t(len + cleared_callback0);