]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-142556: fix crash when a task gets re-registered during finalization in `asyncio...
authorKumar Aditya <kumaraditya@python.org>
Thu, 11 Dec 2025 09:34:49 +0000 (15:04 +0530)
committerGitHub <noreply@github.com>
Thu, 11 Dec 2025 09:34:49 +0000 (15:04 +0530)
Lib/test/test_asyncio/test_tasks.py
Misc/NEWS.d/next/Library/2025-12-11-09-03-07.gh-issue-142556.RuiBte.rst [new file with mode: 0644]
Modules/_asynciomodule.c

index 9809621a3244509d5b532fce64e5819434c3d28e..a3c5351fed02522deb6ddc9cbe20f132269b61d9 100644 (file)
@@ -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 (file)
index 0000000..782e62b
--- /dev/null
@@ -0,0 +1 @@
+Fix crash when a task gets re-registered during finalization in :mod:`asyncio`. Patch by Kumar Aditya.
index 9b2b7011244d7721b42c18798acfe3e377429341..0e6a1e93e04f331dbece69a9d41bc023d3e2d2f0 100644 (file)
@@ -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);