-
-
Notifications
You must be signed in to change notification settings - Fork 8.4k
Closed
Closed
Copy link
Labels
Description
Hi all, I'm Wonil Jang, from the research group S2Lab in UNIST.
We found a use after free bug from micropython by our custom tool. The detailed information is as follows.
Environment
- OS: Ubuntu 22.04
- Version: micropython at commit a5bdd39
- Build: unix ports
- Bug Type: use-after-free
- Bug Location: micropython/py/objarray.c:509
- Credits: Junwha Hong and Wonil Jang, from S2Lab in UNIST
Proof-of-Concept (PoC)
b = bytearray(b'1234567')
for i in range(3):
b[-1:] = b
Problem Statement
By PoC,
STATIC mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t **value**) {
...
size_t src_len;
void *src_items;
size_t item_sz = mp_binary_get_size('@', o->typecode & TYPECODE_MASK, NULL);
if (mp_obj_is_obj(value) && MP_OBJ_TYPE_GET_SLOT_OR_NULL(((mp_obj_base_t *)MP_OBJ_TO_PTR(value))->type, subscr) == array_subscr) {
// value is array, bytearray or memoryview
**mp_obj_array_t *src_slice = MP_OBJ_TO_PTR(value);**
if (item_sz != mp_binary_get_size('@', src_slice->typecode & TYPECODE_MASK, NULL)) {
compat_error:
mp_raise_ValueError(MP_ERROR_TEXT("lhs and rhs should be compatible"));
}
**src_len = src_slice->len;**
**src_items = src_slice->items;**
#if MICROPY_PY_BUILTINS_MEMORYVIEW
if (mp_obj_is_type(value, &mp_type_memoryview)) {
src_items = (uint8_t *)src_items + (src_slice->memview_offset * item_sz);
}
#endif
}
...
if ((size_t)len_adj > o->free) {
// TODO: alloc policy; at the moment we go conservative
**o->items = m_renew(byte, o->items, (o->len + o->free) * item_sz, (o->len + len_adj) * item_sz);**
o->free = len_adj;
dest_items = o->items;
}
mp_seq_replace_slice_grow_inplace(dest_items, o->len,
slice.start, slice.stop, **src_items**, src_len, len_adj, item_sz);
If you run the PoC, self_in
and value
can be same in array_subscr
function. This is because when subscr()
function is called, It just uses reference without copying. and then, if the address is rebased after calling realloc, only self_in
changed and value
(src) becomes a dangling pointer. So when copying it, the use after free bug occurs.
Patch
It should keep source before calling fixed realloc().
Log
#1 0x5555557340a4 in array_subscr /home/qbit/testing-2023/micropython/ports/unix/../../py/objarray.c:509:21
#2 0x555555731de6 in mp_obj_subscr /home/qbit/testing-2023/micropython/ports/unix/../../py/obj.c:536:24
#3 0x555555781d05 in mp_execute_bytecode /home/qbit/testing-2023/micropython/ports/unix/../../py/vm.c:487:21
#4 0x55555574261b in fun_bc_call /home/qbit/testing-2023/micropython/ports/unix/../../py/objfun.c:273:42
#5 0x555555903f3d in execute_from_lexer /home/qbit/testing-2023/micropython/ports/unix/main.c:161:13
#6 0x555555902ad5 in do_file /home/qbit/testing-2023/micropython/ports/unix/main.c:310:12
#7 0x555555902ad5 in main_ /home/qbit/testing-2023/micropython/ports/unix/main.c:722:19
#8 0x7ffff7c29d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#9 0x7ffff7c29e3f in __libc_start_main csu/../csu/libc-start.c:392:3
#10 0x555555593a34 in _start (/home/qbit/testing-2023/micropython/ports/unix/build-standard/micropython+0x3fa34)
Thank you for reading my report.