https://github.com/python/cpython/commit/42d2bedb875f42f0580ee2cfb4bd80a5a229bbd4
commit: 42d2bedb875f42f0580ee2cfb4bd80a5a229bbd4
branch: main
author: Kumar Aditya <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2025-12-11T15:04:49+05:30
summary:

gh-142556: fix crash when a task gets re-registered during finalization in 
`asyncio` (#142565)

files:
A Misc/NEWS.d/next/Library/2025-12-11-09-03-07.gh-issue-142556.RuiBte.rst
M Lib/test/test_asyncio/test_tasks.py
M Modules/_asynciomodule.c

diff --git a/Lib/test/test_asyncio/test_tasks.py 
b/Lib/test/test_asyncio/test_tasks.py
index 9809621a324450..a3c5351fed0252 100644
--- a/Lib/test/test_asyncio/test_tasks.py
+++ b/Lib/test/test_asyncio/test_tasks.py
@@ -3045,6 +3045,26 @@ class BaseTaskIntrospectionTests:
     _enter_task = None
     _leave_task = None
     all_tasks = None
+    Task = None
+
+    def test_register_task_resurrection(self):
+        register_task = self._register_task
+        class EvilLoop:
+            def get_debug(self):
+                return False
+
+            def call_exception_handler(self, context):
+                register_task(context["task"])
+
+        async def coro_fn ():
+            pass
+
+        coro = coro_fn()
+        self.addCleanup(coro.close)
+        loop = EvilLoop()
+        with self.assertRaises(AttributeError):
+            self.Task(coro, loop=loop)
+
 
     def test__register_task_1(self):
         class TaskLike:
@@ -3175,6 +3195,7 @@ class PyIntrospectionTests(test_utils.TestCase, 
BaseTaskIntrospectionTests):
     _leave_task = staticmethod(tasks._py_leave_task)
     all_tasks = staticmethod(tasks._py_all_tasks)
     current_task = staticmethod(tasks._py_current_task)
+    Task = tasks._PyTask
 
 
 @unittest.skipUnless(hasattr(tasks, '_c_register_task'),
@@ -3187,10 +3208,12 @@ class CIntrospectionTests(test_utils.TestCase, 
BaseTaskIntrospectionTests):
         _leave_task = staticmethod(tasks._c_leave_task)
         all_tasks = staticmethod(tasks._c_all_tasks)
         current_task = staticmethod(tasks._c_current_task)
+        Task = tasks._CTask
     else:
         _register_task = _unregister_task = _enter_task = _leave_task = None
 
 
+
 class BaseCurrentLoopTests:
     current_task = None
 
diff --git 
a/Misc/NEWS.d/next/Library/2025-12-11-09-03-07.gh-issue-142556.RuiBte.rst 
b/Misc/NEWS.d/next/Library/2025-12-11-09-03-07.gh-issue-142556.RuiBte.rst
new file mode 100644
index 00000000000000..782e62b65a36f3
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-12-11-09-03-07.gh-issue-142556.RuiBte.rst
@@ -0,0 +1 @@
+Fix crash when a task gets re-registered during finalization in 
:mod:`asyncio`. Patch by Kumar Aditya.
diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index 9b2b7011244d77..0e6a1e93e04f33 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -2990,16 +2990,12 @@ static PyType_Spec Task_spec = {
 static void
 TaskObj_dealloc(PyObject *self)
 {
-    _PyObject_ResurrectStart(self);
-    // Unregister the task here so that even if any subclass of Task
-    // which doesn't end up calling TaskObj_finalize not crashes.
-    unregister_task((TaskObj *)self);
-
-    PyObject_CallFinalizer(self);
-
-    if (_PyObject_ResurrectEnd(self)) {
-        return;
+    if (PyObject_CallFinalizerFromDealloc(self) < 0) {
+        return; // resurrected
     }
+    // unregister the task after finalization so that
+    // if the task gets resurrected, it remains registered
+    unregister_task((TaskObj *)self);
 
     PyTypeObject *tp = Py_TYPE(self);
     PyObject_GC_UnTrack(self);

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to