New submission from Ned Williamson: There are two cases of use-after-free in the new Modules/_asynciomodule.c in the release candidate for Python 3.6, but I'm filing these together because it's the same underlying issue.
In both cases in this file where the unsafe `PyList_GET_ITEM` is called, `_asyncio_Future_remove_done_callback` and `future_schedule_callbacks`, there is a function called on the fetched item that allows the user to mutate the callbacks list (`PyObject_RichCompareBool` and `_PyObject_CallMethodId`), which then leads to OOB access on subsequent iterations. For example, this script can trigger the vulnerability for `remove_done_callback`: ``` import asyncio fut = asyncio.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(id) return False fut.remove_done_callback(evil()) ``` On an ASAN build we can see that there is indeed a UAF occurring: ``` ================================================================= ==19239==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60300000e7f0 at pc 0x000106fe6704 bp 0x7fff5cda09c0 sp 0x7fff5cda09b8 READ of size 8 at 0x60300000e7f0 thread T0 #0 0x106fe6703 in _asyncio_Future_remove_done_callback _asynciomodule.c:526 #1 0x102f5af35 in _PyCFunction_FastCallDict methodobject.c:192 #2 0x1030e9044 in call_function ceval.c:4788 #3 0x1030da2f0 in _PyEval_EvalFrameDefault ceval.c:3275 #4 0x1030eb09b in _PyEval_EvalCodeWithName ceval.c:718 #5 0x1030ced53 in PyEval_EvalCode ceval.c:4140 #6 0x10317da47 in PyRun_FileExFlags pythonrun.c:980 #7 0x10317c110 in PyRun_SimpleFileExFlags pythonrun.c:396 #8 0x1031c76b8 in Py_Main main.c:320 #9 0x102e5ed40 in main python.c:69 #10 0x7fffc9bd8254 in start (libdyld.dylib+0x5254) 0x60300000e7f0 is located 0 bytes to the right of 32-byte region [0x60300000e7d0,0x60300000e7f0) allocated by thread T0 here: #0 0x1039d5f87 in wrap_realloc (libclang_rt.asan_osx_dynamic.dylib+0x4af87) #1 0x102efb089 in list_ass_slice listobject.c:63 #2 0x106fe6605 in _asyncio_Future_remove_done_callback _asynciomodule.c:541 #3 0x102f5af35 in _PyCFunction_FastCallDict methodobject.c:192 #4 0x1030e9044 in call_function ceval.c:4788 #5 0x1030da2f0 in _PyEval_EvalFrameDefault ceval.c:3275 #6 0x1030ed94a in _PyFunction_FastCallDict ceval.c:718 #7 0x102e81308 in _PyObject_FastCallDict abstract.c:2295 #8 0x102e816b1 in _PyObject_Call_Prepend abstract.c:2358 #9 0x102e81286 in _PyObject_FastCallDict abstract.c:2316 #10 0x102fa125a in slot_tp_richcompare typeobject.c:6287 #11 0x102f61f66 in PyObject_RichCompare object.c:671 #12 0x102f62421 in PyObject_RichCompareBool object.c:741 #13 0x106fe6544 in _asyncio_Future_remove_done_callback _asynciomodule.c:528 #14 0x102f5af35 in _PyCFunction_FastCallDict methodobject.c:192 #15 0x1030e9044 in call_function ceval.c:4788 #16 0x1030da2f0 in _PyEval_EvalFrameDefault ceval.c:3275 #17 0x1030eb09b in _PyEval_EvalCodeWithName ceval.c:718 #18 0x1030ced53 in PyEval_EvalCode ceval.c:4140 #19 0x10317da47 in PyRun_FileExFlags pythonrun.c:980 #20 0x10317c110 in PyRun_SimpleFileExFlags pythonrun.c:396 #21 0x1031c76b8 in Py_Main main.c:320 #22 0x102e5ed40 in main python.c:69 #23 0x7fffc9bd8254 in start (libdyld.dylib+0x5254) SUMMARY: AddressSanitizer: heap-buffer-overflow _asynciomodule.c:526 in _asyncio_Future_remove_done_callback Shadow bytes around the buggy address: 0x1c0600001ca0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x1c0600001cb0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x1c0600001cc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x1c0600001cd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x1c0600001ce0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa =>0x1c0600001cf0: fa fa fa fa fa fa fa fa fa fa 00 00 00 00[fa]fa 0x1c0600001d00: 00 00 00 fa fa fa 00 00 00 fa fa fa 00 00 00 02 0x1c0600001d10: fa fa fd fd fd fd fa fa fd fd fd fd fa fa fd fd 0x1c0600001d20: fd fd fa fa fd fd fd fd fa fa fd fd fd fd fa fa 0x1c0600001d30: fd fd fd fd fa fa fd fd fd fd fa fa fd fd fd fd 0x1c0600001d40: fa fa fd fd fd fd fa fa 00 00 00 00 fa fa 00 00 Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Heap right redzone: fb Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack partial redzone: f4 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb ==19239==ABORTING [1] 19239 abort ASAN_OPTIONS=halt_on_error=0 ./python.exe test.py ``` ---------- messages: 283134 nosy: Ned Williamson priority: normal severity: normal status: open title: Use-after-free in _asynciomodule.c type: crash versions: Python 3.6 _______________________________________ Python tracker <rep...@bugs.python.org> <http://bugs.python.org/issue28963> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com