Repository navigation
mimalloc 3.x segfaults on free(NULL) called before mimalloc is initialized #1341
Description
glibc 2.44's __newlocale calls free(NULL) during libstdc++'s ios_basestatic initializer, i.e. during loader init, before mimalloc's own constructor has run. mi_free indexes _mi_page_map before null-checking p, and at that point _mi_page_map is still NULL, so the process dies before main(). By the way, this is already checked for null in the MI_DEBUG branch's _mi_safe_ptr_page (link).
Environment:
Arch Linux, glibc 2.44 (currently in core-testing), mimalloc 3.4.3, gcc 16.1.1.
Repro'd on dev3 commit 579f8c0.
Repro (crashes 100% of the time):
Reproduces in the shared library build, which gets -DMI_FREE_IS_CHECKED=1 via mi_cflags_dynamic under MI_OVERRIDE (CMakeLists.txt). Confirmed with RelWithDebInfo and MI_DEBUG:BOOL=OFF. Debug builds do not reproduce, because mi_validate_ptr_page's #if MI_DEBUG branch calls _mi_safe_ptr_page instead.
// The body is irrelevant; including <iostream> is what pulls in
// the libstdc++ ios_base static initializer that triggers the crash
#include <iostream>
int main() {
std::cout << "hello world" << std::endl;
return 0;
}g++ -o repro repro.cpp
./repro # OK
LD_PRELOAD=/usr/lib/libmimalloc.so.3 ./repro # SIGSEGV
Backtrace (against dev3, commit 579f8c0):
#0 _mi_checked_ptr_page (p=0x0) include/mimalloc/internal.h:724
#1 _mi_ptr_page (p=0x0) include/mimalloc/internal.h:735
#2 mi_validate_ptr_page (p=0x0) src/free.c:181
#3 mi_free (p=0x0) src/free.c:216
#4 __newlocale (...) newlocale.c:291
#5 std::locale::facet::_S_create_c_locale (...) c++locale.cc:135
...
#17 std::ios_base::Init::Init ios_init.cc:85
...
#20 _GLOBAL__sub_I.00090_globals_io.cc globals_io.cc:118
#21 call_init dl-init.c:74
...
#23 _dl_init dl-init.c:121
#24 _dl_start_user ld-linux-x86-64.so.2
(gdb) p (void*)_mi_page_map
$1 = (void *) 0x0
glibc newlocale.c:291 is just free (tmp_buffer); where tmp_buffer is still NULL when __newlocale_1 allocated nothing.
Source snippet
static inline mi_page_t* _mi_checked_ptr_page(const void* p) {
size_t sub_idx;
const size_t idx = _mi_page_map_index(p, &sub_idx);
mi_submap_t const sub = _mi_page_map_at(idx); // <-- doesn't NULL check the map, only the submap on the next line
if mi_unlikely(sub == NULL) return NULL;
return sub[sub_idx];
}Why this is mimalloc's bug, not glibc's:
free(NULL)is a mandated no-op (C23 7.24.3.3p2) — glibc is doing nothing wrong; glibc 2.44 merely changed which early paths call it.- An allocator that interposes
malloc/freemust be safe when called before its own constructor runs; it cannot assume it wins the init-order race.
Versions:
mimalloc 3.2.8, 3.4.3, and my local dev3 build all crash. mimalloc 2.2.4 is fine.
Expected:
mi_free null-checks p before touching _mi_page_map, and tolerates being called pre-init.
AI usage note: AI was used to help debug the issue but every step it took was supervised by me, the cause was then tested and confirmed by me, and the report was written by me using my & AI's findings.
Activity
nakamorichi commentedon Jul 27, 2026
Not sure if related, but after upgrading mimalloc on cachyos - 3.4.3-1.1 / cachyos-extra-v4 (x86_64_v4) - I also began experiencing segfaults. Don't know how to submit ticket to arch/cachyos team.
daanx commentedon Jul 27, 2026
Ah, I see what happens here. I think I have an efficient way to fix this -- I'll try to push a fix soon.
loynoir commentedon Jul 27, 2026
nix --versionalsoSegmentation faultcore dumpedon archlinux seems caused by mimallochttps://gitlab.archlinux.org/archlinux/packaging/packages/mimalloc/-/work_items/7
mgnsk commentedon Jul 27, 2026
With the help of Claude, I got a temporary arch linux quickfix. (Spent an hour trying to downgrade various stuff but didn't work).
loynoir commentedon Jul 27, 2026
Magnus Kokk (@mgnsk)
You can use
archive.archlinux.orgServer = https://archive.archlinux.org/repos/2026/07/25/$repo/os/$archensure free(NULL) works even if no previous allocation has taken plac…
daanx commentedon Jul 27, 2026
I pushed a potential fix for this issue. The root cause is that
free(NULL)is called before mimalloc is initialized (as we initialize on the first allocation or the main process start). The fix uses a static initial page map that maps NULL correctly.If you can test this in your repro that would be great -- I am planning to release an update of v3 soon and it would be nice to include this.
SergeyKasmy commentedon Jul 27, 2026
Can confirm this fixes the issue.
Just out of curiosity, why wasn't this just null-checked like it already is in
_mi_safe_ptr_page? Any particular reason for the asymmetry? The submap is already null-checked in this functiondaanx commentedon Jul 27, 2026
Thank you for checking!
For performance, usually
mi_free(p)does not check upfront ifpisNULLbut instead looks it up via the page map first and we do a NULL check on the returned page. This one check now works for any invalid pointers (and NULL). However, mimalloc only initializes the page_map on a first allocation or the main process start -- in this case that would not hold and page_map was not yet initialized. The fix now uses a static initial page_map to handlemi_free(NULL)correctly.SergeyKasmy commentedon Jul 28, 2026
Got it, thanks for explaining!
Zoxc commentedon Jul 30, 2026
This may be the cause of segfaults in rust-lang/rust#155575 too
page-map: drop the atomic `= NULL` initializers so the TU compiles as…
daanx commentedon Aug 1, 2026
Hi @zachees -- I upstreamed the mingw fixes you mentioned in commit 60c4f03 (in the latest
dev,dev2anddev3).Let me know if that works for you on mingw (as I cannot test it myself).
daanx commentedon Aug 1, 2026
This should be fixed now in the latest release (v3.4.4)
fix: bootstrap the library in mi_heap_new and mi_subproc_new
Bump mimalloc to 3.4.4 (#1921)
mimalloc: free(NULL) before the allocator initializes no longer fault…