Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork33.7k
Closed
Description
What happened?
DuringTask teardownTaskObj_dealloc() calls the finalizer (TaskObj_finalize) which invokes the loop'scall_exception_handler (user code). If that handler re-registers the sameTask (e.g. calls_asyncio._register_task(context["task"])), the runtime may add the freed task pointer into a registry whiletp_free is still running, causing a heap use-after-free when the registry later touches the pointer.
Proof of Concept:
import_asyncioclassEvilLoop:defget_debug(self):returnFalsedefcall_exception_handler(self,context):_asyncio._register_task(context["task"])asyncdefcoro():passloop=EvilLoop()task=_asyncio.Task(coro(),loop=loop)
Affected Versions:
| Python Version | Status | Exit Code |
|---|---|---|
Python 3.9.24+ (heads/3.9:9c4638d, Oct 17 2025, 11:19:30) | Exception | 1 |
Python 3.10.19+ (heads/3.10:0142619, Oct 17 2025, 11:20:05) [GCC 13.3.0] | Exception | 1 |
Python 3.11.14+ (heads/3.11:88f3f5b, Oct 17 2025, 11:20:44) [GCC 13.3.0] | Exception | 1 |
Python 3.12.12+ (heads/3.12:8cb2092, Oct 17 2025, 11:21:35) [GCC 13.3.0] | Exception | 1 |
Python 3.13.9+ (heads/3.13:0760a57, Oct 17 2025, 11:22:25) [GCC 13.3.0] | Exception | 1 |
Python 3.14.0+ (heads/3.14:889e918, Oct 17 2025, 11:23:02) [GCC 13.3.0] | ASAN | 1 |
Python 3.15.0a1+ (heads/main:fbf0843, Oct 17 2025, 11:23:37) [GCC 13.3.0] | ASAN | 1 |
Vulnerable Code Snippet
static voidTaskObj_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); // Call TaskObj_finalize if (_PyObject_ResurrectEnd(self)) { return; } PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); PyObject_ClearWeakRefs(self); (void)TaskObj_clear(self); tp->tp_free(self); Py_DECREF(tp);}static voidTaskObj_finalize(PyObject *op){ TaskObj *task = (TaskObj*)op; PyObject *context; PyObject *message = NULL; PyObject *func; if (task->task_state != STATE_PENDING || !task->task_log_destroy_pending) { goto done; } /* Save the current exception, if any. */ PyObject *exc = PyErr_GetRaisedException(); context = PyDict_New(); if (context == NULL) { goto finally; } message = PyUnicode_FromString("Task was destroyed but it is pending!"); if (message == NULL) { goto finally; } if (PyDict_SetItem(context, &_Py_ID(message), message) < 0 || PyDict_SetItem(context, &_Py_ID(task), (PyObject*)task) < 0) { goto finally; } if (task->task_source_tb != NULL) { if (PyDict_SetItem(context, &_Py_ID(source_traceback), task->task_source_tb) < 0) { goto finally; } } // Bug: In the call_exception_handler, we re-register the task so that the registry holds its pointer// While later, the tp->tp_free(self); in TaskObj_dealloc, the task will be freed and any access to the pointer will cause UAF.// When task has ended, the call_exception_handler method will be invoked. func = PyObject_GetAttr(task->task_loop, &_Py_ID(call_exception_handler)); if (func != NULL) { PyObject *res = PyObject_CallOneArg(func, context); if (res == NULL) { PyErr_FormatUnraisable("Exception ignored while calling asyncio " "function %R", func); } else { Py_DECREF(res); } Py_DECREF(func); }finally: Py_XDECREF(context); Py_XDECREF(message); /* Restore the saved exception. */ PyErr_SetRaisedException(exc);done: FutureObj_finalize((PyObject*)task);}Sanitizer
===================================================================1485200==ERROR:AddressSanitizer:heap-use-after-freeonaddress0x5150000657d0atpc0x62a676285a06bp0x7ffe931f2c30sp0x7ffe931f2c20WRITEofsize8at0x5150000657d0threadT0#0 0x62a676285a05 in llist_concat Include/internal/pycore_llist.h:95#1 0x62a676285a05 in PyThreadState_Clear Python/pystate.c:1687#2 0x62a676285bb1 in interpreter_clear Python/pystate.c:746#3 0x62a676286636 in _PyInterpreterState_Clear Python/pystate.c:904#4 0x62a67627cb73 in finalize_interp_clear Python/pylifecycle.c:1920#5 0x62a67627d669 in _Py_Finalize Python/pylifecycle.c:2337#6 0x62a67627d6f2 in Py_FinalizeEx Python/pylifecycle.c:2378#7 0x62a6762df847 in Py_RunMain Modules/main.c:774#8 0x62a6762dfa2e in pymain_main Modules/main.c:802#9 0x62a6762dfdb3 in Py_BytesMain Modules/main.c:826#10 0x62a675d63645 in main Programs/python.c:15#11 0x7405bae2a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58#12 0x7405bae2a28a in __libc_start_main_impl ../csu/libc-start.c:360#13 0x62a675d63574 in _start (/home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/python+0x2dd574) (BuildId: ff3dc40ea460bd4beb2c3a72283cca525b319bf0)0x5150000657d0islocated208bytesinsideof504-byteregion [0x515000065700,0x5150000658f8)freedbythreadT0here:#0 0x7405bb2fc4d8 in free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:52#1 0x62a675f9596d in _PyMem_RawFree Objects/obmalloc.c:91#2 0x62a675f97cd9 in _PyMem_DebugRawFree Objects/obmalloc.c:2955#3 0x62a675f97d1a in _PyMem_DebugFree Objects/obmalloc.c:3100#4 0x62a675fc006c in PyObject_Free Objects/obmalloc.c:1522#5 0x62a6761fecf7 in PyObject_GC_Del Python/gc.c:2435#6 0x62a6762eef87 in TaskObj_dealloc Modules/_asynciomodule.c:3010#7 0x62a675f8c481 in _Py_Dealloc Objects/object.c:3200#8 0x62a675fdd81c in Py_DECREF Include/refcount.h:401#9 0x62a675ff5646 in type_call Objects/typeobject.c:2463#10 0x62a675ecec71 in _PyObject_MakeTpCall Objects/call.c:242#11 0x62a675ecef19 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:167#12 0x62a675ecef72 in PyObject_Vectorcall Objects/call.c:327#13 0x62a676154c60 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:2920#14 0x62a676190e54 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121#15 0x62a676191148 in _PyEval_Vector Python/ceval.c:2001#16 0x62a6761913f8 in PyEval_EvalCode Python/ceval.c:884#17 0x62a676288507 in run_eval_code_obj Python/pythonrun.c:1365#18 0x62a676288723 in run_mod Python/pythonrun.c:1459#19 0x62a67628957a in pyrun_file Python/pythonrun.c:1293#20 0x62a67628c220 in _PyRun_SimpleFileObject Python/pythonrun.c:521#21 0x62a67628c4f6 in _PyRun_AnyFileObject Python/pythonrun.c:81#22 0x62a6762dd74d in pymain_run_file_obj Modules/main.c:410#23 0x62a6762dd9b4 in pymain_run_file Modules/main.c:429#24 0x62a6762df1b2 in pymain_run_python Modules/main.c:691#25 0x62a6762df842 in Py_RunMain Modules/main.c:772#26 0x62a6762dfa2e in pymain_main Modules/main.c:802#27 0x62a6762dfdb3 in Py_BytesMain Modules/main.c:826#28 0x62a675d63645 in main Programs/python.c:15#29 0x7405bae2a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58previouslyallocatedbythreadT0here:#0 0x7405bb2fd9c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69#1 0x62a675f96284 in _PyMem_RawMalloc Objects/obmalloc.c:63#2 0x62a675f95655 in _PyMem_DebugRawAlloc Objects/obmalloc.c:2887#3 0x62a675f956bd in _PyMem_DebugRawMalloc Objects/obmalloc.c:2920#4 0x62a675f96f3b in _PyMem_DebugMalloc Objects/obmalloc.c:3085#5 0x62a675fbff28 in PyObject_Malloc Objects/obmalloc.c:1493#6 0x62a675ff203b in _PyObject_MallocWithType Include/internal/pycore_object_alloc.h:46#7 0x62a675ff203b in _PyType_AllocNoTrack Objects/typeobject.c:2504#8 0x62a675ff21c7 in PyType_GenericAlloc Objects/typeobject.c:2535#9 0x62a675fd9dd9 in PyType_GenericNew Objects/typeobject.c:2549#10 0x62a675ff5346 in type_call Objects/typeobject.c:2448#11 0x62a675ecec71 in _PyObject_MakeTpCall Objects/call.c:242#12 0x62a675ecef19 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:167#13 0x62a675ecef72 in PyObject_Vectorcall Objects/call.c:327#14 0x62a676154c60 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:2920#15 0x62a676190e54 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121#16 0x62a676191148 in _PyEval_Vector Python/ceval.c:2001#17 0x62a6761913f8 in PyEval_EvalCode Python/ceval.c:884#18 0x62a676288507 in run_eval_code_obj Python/pythonrun.c:1365#19 0x62a676288723 in run_mod Python/pythonrun.c:1459#20 0x62a67628957a in pyrun_file Python/pythonrun.c:1293#21 0x62a67628c220 in _PyRun_SimpleFileObject Python/pythonrun.c:521#22 0x62a67628c4f6 in _PyRun_AnyFileObject Python/pythonrun.c:81#23 0x62a6762dd74d in pymain_run_file_obj Modules/main.c:410#24 0x62a6762dd9b4 in pymain_run_file Modules/main.c:429#25 0x62a6762df1b2 in pymain_run_python Modules/main.c:691#26 0x62a6762df842 in Py_RunMain Modules/main.c:772#27 0x62a6762dfa2e in pymain_main Modules/main.c:802#28 0x62a6762dfdb3 in Py_BytesMain Modules/main.c:826#29 0x62a675d63645 in main Programs/python.c:15#30 0x7405bae2a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58SUMMARY:AddressSanitizer:heap-use-after-freeInclude/internal/pycore_llist.h:95inllist_concatShadowbytesaroundthebuggyaddress:0x515000065500:fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfd0x515000065580:fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfd0x515000065600:fdfdfdfdfdfdfdfdfdfdfdfdfdfdfafa0x515000065680:fafafafafafafafafafafafafafafafa0x515000065700:fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfd=>0x515000065780:fdfdfdfdfdfdfdfdfdfd[fd]fdfdfdfdfd0x515000065800:fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfd0x515000065880:fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfa0x515000065900:fafafafafafafafafafafafafafafafa0x515000065980:fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfd0x515000065a00:fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdShadowbytelegend (oneshadowbyterepresents8applicationbytes):Addressable:00Partiallyaddressable:01020304050607Heapleftredzone:faFreedheapregion:fdStackleftredzone:f1Stackmidredzone:f2Stackrightredzone:f3Stackafterreturn:f5Stackuseafterscope:f8Globalredzone:f9Globalinitorder:f6Poisonedbyuser:f7Containeroverflow:fcArraycookie:acIntraobjectredzone:bbASaninternal:feLeftallocaredzone:caRightallocaredzone:cb==1485200==ABORTING
Linked PRs
Metadata
Metadata
Assignees
Labels
Projects
Status
Done