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

This commit is contained in:
Kumar Aditya 2025-12-11 15:04:49 +05:30 committed by GitHub
parent da8199f884
commit 42d2bedb87
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 29 additions and 9 deletions

View 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

View file

@ -0,0 +1 @@
Fix crash when a task gets re-registered during finalization in :mod:`asyncio`. Patch by Kumar Aditya.

View 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);