Temporary `mi_heap` objects causing AddressSanitizer to report a buffer overrun on the TLD buffer upon calling `mi_heap_destroy`
Summary
I am trying to wrap various mimalloc functions with a C++ class using mi_heap_t objects, however, upon calling mi_heap_destroy AddressSanitizer reports a buffer overrun on the TLD buffer associated with said mi_heap_t object. It appears that such behavior only occurs when mi_heap_destroy is called more than once within the lifetime of the program (even when called on distinct mi_heap_t objects).
(No multithreading involved, all behavior observed on T0)
Environment
- OS:
Ubuntu 24.04 - CPU:
Intel i9 i9900k [x86](don't know if this is relevant or not) - Compiler:
g++ (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 - Relevant compiler flags:
-fsanitize=address,undefined,leak - Relevant linker flags:
-fsanitize=address,undefined,leak - Active
mimalloccommit hash: 09a2709
Minimal reproduction
Simple C++ class that wraps mi_heap_t (deleted move/copy semantics)
class Wrapper {
mi_heap_t* heap;
Wrapper() : heap(mi_heap_new()) {}
~Wrapper() { mi_heap_destroy(heap); }
}
Note that this simple reproduction snippet only appears to crash under specific conditions (that I am not able to observe), as opposed to a certain crash in a larger project that virtually implement mi_heap objects in the same way.
AddressSanitizer output:
=================================================================
==21152==ERROR: AddressSanitizer: global-buffer-overflow on address 0x74a5f71931a0 at pc 0x74a5f7118b16 bp 0x7ffce0d62ae0 sp 0x7ffce0d62ad0
READ of size 8 at 0x74a5f71931a0 thread T0
#0 0x74a5f7118b15 in mi_stat_update /home/user/dev/via-lang/thirdparty/mimalloc/src/stats.c:41
#1 0x74a5f7118f33 in _mi_stat_decrease /home/user/dev/via-lang/thirdparty/mimalloc/src/stats.c:61
#2 0x74a5f710dd24 in mi_segment_page_clear /home/user/dev/via-lang/thirdparty/mimalloc/src/segment.c:1026
#3 0x74a5f710e32b in _mi_segment_page_free /home/user/dev/via-lang/thirdparty/mimalloc/src/segment.c:1058
#4 0x74a5f70d60d2 in _mi_heap_page_destroy /home/user/dev/via-lang/thirdparty/mimalloc/src/heap.c:365
#5 0x74a5f70d342b in mi_heap_visit_pages /home/user/dev/via-lang/thirdparty/mimalloc/src/heap.c:46
#6 0x74a5f70d610d in _mi_heap_destroy_pages /home/user/dev/via-lang/thirdparty/mimalloc/src/heap.c:371
#7 0x74a5f70d633f in mi_heap_destroy /home/user/dev/via-lang/thirdparty/mimalloc/src/heap.c:405
#8 0x56d8a2f8d3f4 in via::ScopedAllocator::~ScopedAllocator() /home/user/dev/via-lang/src/via-core/support/memory.h:201
#9 0x56d8a30bb50a in via::VirtualMachine::~VirtualMachine() (/home/user/dev/via-lang/build/src/via-cli/via-cli+0x1a0350a) (BuildId: cd6ef4c17845494cec4d5aff0bee4566f2fb335b)
#10 0x56d8a30b08b6 in via::Module::load_source_file(via::ModuleManager*, via::Module*, char const*, std::filesystem::__cxx11::path const&, via::ast::StmtImport const*, via::ModulePerms, via::ModuleFlags) /home/user/dev/via-lang/src/via-core/module/module.cpp:324
#11 0x56d8a2f82f6e in main /home/user/dev/via-lang/src/via-cli/main.cpp:158
#12 0x74a5f602a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#13 0x74a5f602a28a in __libc_start_main_impl ../csu/libc-start.c:360
#14 0x56d8a2f80974 in _start (/home/user/dev/via-lang/build/src/via-cli/via-cli+0x18c8974) (BuildId: cd6ef4c17845494cec4d5aff0bee4566f2fb335b)
0x74a5f71931a0 is located 16 bytes after global variable 'tld_main' defined in '/home/user/dev/via-lang/thirdparty/mimalloc/src/init.c:153:37' (0x74a5f7191d80) of size 5136
0x74a5f71931a0 is located 32 bytes before global variable '_mi_heap_main' defined in '/home/user/dev/via-lang/thirdparty/mimalloc/src/init.c:160:31' (0x74a5f71931c0) of size 3088
SUMMARY: AddressSanitizer: global-buffer-overflow /home/user/dev/via-lang/thirdparty/mimalloc/src/stats.c:41 in mi_stat_update
Shadow bytes around the buggy address:
0x74a5f7192f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x74a5f7192f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x74a5f7193000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x74a5f7193080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x74a5f7193100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x74a5f7193180: 00 00 f9 f9[f9]f9 f9 f9 00 00 00 00 00 00 00 00
0x74a5f7193200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x74a5f7193280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x74a5f7193300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x74a5f7193380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x74a5f7193400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 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
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
==21152==ABORTING
Notes
I am not familiar with the internals/footguns of mimalloc, so if I am misusing something please let me know. But from what I understand, such behavior is not intended.
Thanks in advance!
Here are some similar issues (that I was able to find) that follow the same pattern but appear to occur under totally distinct circumstances: #1124 #1055