From bc55838096bcc8bde06d0c7d6ec6fc8fa571e7ed Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 28 Sep 2022 10:06:45 -0700 Subject: [PATCH 1/4] GH-97592: Anticipate fut_callbacks being cleared by PyObject_RichCompareBool --- Modules/_asynciomodule.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 9d2f83bf6c73c1..c6cf45ea586c4d 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); From 00b95363c1b045e1a9e8b52ad8aae95718bccd33 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 29 Sep 2022 16:05:10 -0700 Subject: [PATCH 2/4] Add test --- Lib/test/test_asyncio/test_futures.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index f4a46ec90a16fe..c4c934f547b42a 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -832,6 +832,21 @@ def __eq__(self, other): 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 From 648a6175793f6dc7fdc61aaa1165f42dbe4cd233 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 23:22:27 +0000 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2022-09-29-23-22-24.gh-issue-97592.tpJg_J.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2022-09-29-23-22-24.gh-issue-97592.tpJg_J.rst 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 00000000000000..05def0c9ab8242 --- /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 `asyncio.Future.remove_done_callback()` when an evil argument is passed. From a18d215fac7eee0f462b9a15cf1eafd0328a6799 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 30 Sep 2022 08:52:51 -0700 Subject: [PATCH 4/4] Add a link to the news Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> --- .../next/Library/2022-09-29-23-22-24.gh-issue-97592.tpJg_J.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 05def0c9ab8242..aa245cf944004e 100644 --- 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 @@ -1 +1 @@ -Avoid a crash in the C version of `asyncio.Future.remove_done_callback()` when an evil argument is passed. +Avoid a crash in the C version of :meth:`asyncio.Future.remove_done_callback` when an evil argument is passed.