Global buffer overflow during umount
Description
We found a buffer out-of-bound crash when MicroPython attempted to unmount a wrong object. All the PoCs we found essentially put random strings to os.umount(). We uploaded the representative one here.
Also, seeing how they all involved random strings, we suspect this crash happened during a string parsing that incorrectly assumed the provided string was a path.
Proof of Concept
File: poc.py
import os
v1 = "WjeePMR1E";
os.umount(v1);
Execution:
$ # build unix port with ASAN, at the root source code directory.
$ export CC=clang
$ export CXX=clang++
$ export CFLAGS="-fsanitize=address -fno-omit-frame-pointer"
$ export CXXFLAGS=$CFLAGS
$ export LDFLAGS=$CFLAGS
$ export DEBUG=1
$ make -C mpy-cross -j
$ make -C ports/unix -j all lib
$
$ # run a poc.
$ export ASAN_OPTIONS="detect_leaks=0"
$ ./ports/unix/build-standard/micropython poc.py
Stack trace:
=================================================================
==238041==ERROR: AddressSanitizer: global-buffer-overflow on address 0x55b50f924ba2 at pc 0x7fbbb9aa54b5 bp 0x7ffeb92103b0 sp 0x7ffeb920fb58
READ of size 10 at 0x55b50f924ba2 thread T0
#0 0x7fbbb9aa54b4 in MemcmpInterceptorCommon(void*, int (*)(void const*, void const*, unsigned long), void const*, void const*, unsigned long) ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:861
#1 0x7fbbb9aa5bc6 in __interceptor_memcmp ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:892
#2 0x7fbbb9aa5bc6 in __interceptor_memcmp ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:887
#3 0x55b50f875d75 in mp_vfs_umount ../../extmod/vfs.c:276
#4 0x55b50f81e691 in fun_builtin_1_call ../../py/objfun.c:68
#5 0x55b50f8094ec in mp_call_function_n_kw ../../py/runtime.c:712
#6 0x55b50f80996b in mp_call_method_n_kw ../../py/runtime.c:728
#7 0x55b50f84f75d in mp_execute_bytecode ../../py/vm.c:1042
#8 0x55b50f81ee46 in fun_bc_call ../../py/objfun.c:273
#9 0x55b50f8094ec in mp_call_function_n_kw ../../py/runtime.c:712
#10 0x55b50f809546 in mp_call_function_0 ../../py/runtime.c:686
#11 0x55b50f917cfd in execute_from_lexer /workspaces/Latest/micropython/ports/unix/main.c:160
#12 0x55b50f917dc2 in do_file /workspaces/Latest/micropython/ports/unix/main.c:309
#13 0x55b50f918ded in main_ /workspaces/Latest/micropython/ports/unix/main.c:721
#14 0x55b50f91921a in main /workspaces/Latest/micropython/ports/unix/main.c:476
#15 0x7fbbb971ed8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#16 0x7fbbb971ee3f in __libc_start_main_impl ../csu/libc-start.c:392
#17 0x55b50f7c84c4 in _start (/workspaces/Latest/micropython/ports/unix/build-standard/micropython+0x854c4)
0x55b50f924ba2 is located 62 bytes to the left of global variable '*.LC15' defined in '../../py/qstr.c' (0x55b50f924be0) of size 9
'*.LC15' is ascii string '<module>'
0x55b50f924ba2 is located 0 bytes to the right of global variable '*.LC14' defined in '../../py/qstr.c' (0x55b50f924ba0) of size 2
'*.LC14' is ascii string '/'
SUMMARY: AddressSanitizer: global-buffer-overflow ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:861 in MemcmpInterceptorCommon(void*, int (*)(void const*, void const*, unsigned long), void const*, void const*, unsigned long)
Shadow bytes around the buggy address:
0x0ab721f1c920: 00 00 00 00 00 00 00 07 f9 f9 f9 f9 00 00 f9 f9
0x0ab721f1c930: f9 f9 f9 f9 00 07 f9 f9 f9 f9 f9 f9 00 f9 f9 f9
0x0ab721f1c940: f9 f9 f9 f9 07 f9 f9 f9 f9 f9 f9 f9 01 f9 f9 f9
0x0ab721f1c950: f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9 02 f9 f9 f9
0x0ab721f1c960: f9 f9 f9 f9 02 f9 f9 f9 f9 f9 f9 f9 02 f9 f9 f9
=>0x0ab721f1c970: f9 f9 f9 f9[02]f9 f9 f9 f9 f9 f9 f9 00 01 f9 f9
0x0ab721f1c980: f9 f9 f9 f9 02 f9 f9 f9 f9 f9 f9 f9 00 01 f9 f9
0x0ab721f1c990: f9 f9 f9 f9 00 02 f9 f9 f9 f9 f9 f9 00 04 f9 f9
0x0ab721f1c9a0: f9 f9 f9 f9 00 02 f9 f9 f9 f9 f9 f9 00 01 f9 f9
0x0ab721f1c9b0: f9 f9 f9 f9 00 04 f9 f9 f9 f9 f9 f9 00 04 f9 f9
0x0ab721f1c9c0: f9 f9 f9 f9 00 01 f9 f9 f9 f9 f9 f9 00 01 f9 f9
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
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
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
Shadow gap: cc
==238041==ABORTING
Environment
Ubuntu 20.04
Intel(R) Xeon(R) Gold 5218 CPU @ 2.30GHz
Memory: 64 GB
Affected Version
v1.20.0 (commit a3862e726, latest as of 2023-09-26)
v1.20.0 (commit 813d559bc, 2023-06-19)
Discovered in the UNIX port version.
Various crashes while executing uctypes.sizeof()
Description
We found a crash case involving uctypes.sizeof() that was exhibited in global-buffer-overflow sometimes or in null-dereference the other times. We couldn't identify the apparent difference between PoCs exhibiting global-buffer-overflow and null-dereference. We have attached three PoCs for each crash type.
Proof of Concept
$ # build unix port with ASAN, at the root source code directory.
$ export CC=clang
$ export CXX=clang++
$ export CFLAGS="-fsanitize=address -fno-omit-frame-pointer"
$ export CXXFLAGS=$CFLAGS
$ export LDFLAGS=$CFLAGS
$ export DEBUG=1
$ make -C mpy-cross -j
$ make -C ports/unix -j all lib
$
$ # run a poc.
$ export ASAN_OPTIONS="detect_leaks=0"
$ ./ports/unix/build-standard/micropython <poc_file>
Environment
Ubuntu 20.04
Intel(R) Xeon(R) Gold 5218 CPU @ 2.30GHz
Memory: 64 GB
Affected Version
v1.20.0 (commit a3862e726, latest as of 2023-09-26)
v1.20.0 (commit 813d559bc, 2023-06-19)
Discovered in the UNIX port version.