Skip to content

mimalloc 3.x segfaults on free(NULL) called before mimalloc is initialized #1341

Closed

Description

@SergeyKasmy

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

Link

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:

  1. 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.
  2. An allocator that interposes malloc/free must 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

  1. nakamorichi commented on Jul 27, 2026

    @nakamorichi

    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.

  2. daanx commented on Jul 27, 2026

    @daanx
    Collaborator

    Ah, I see what happens here. I think I have an efficient way to fix this -- I'll try to push a fix soon.

  3. loynoir commented on Jul 27, 2026

    @loynoir

    nix --version also Segmentation fault core dumped on archlinux seems caused by mimalloc

    https://gitlab.archlinux.org/archlinux/packaging/packages/mimalloc/-/work_items/7

  4. mgnsk commented on Jul 27, 2026

    @mgnsk

    nix --version also Segmentation fault core dumped on archlinux seems caused by mimalloc

    https://gitlab.archlinux.org/archlinux/packaging/packages/mimalloc/-/work_items/7

    With the help of Claude, I got a temporary arch linux quickfix. (Spent an hour trying to downgrade various stuff but didn't work).

    patchelf --remove-needed libmimalloc.so.3 /usr/bin/nix-daemon
    systemctl restart nix-daemon
  5. loynoir commented on Jul 27, 2026

    @loynoir

    Magnus Kokk (@mgnsk)

    You can use archive.archlinux.org

    Server = https://archive.archlinux.org/repos/2026/07/25/$repo/os/$arch
  6. daanx commented on Jul 27, 2026

    @daanx
    Collaborator

    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.

  7. SergeyKasmy commented on Jul 27, 2026

    @SergeyKasmy
    Author

    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 function

  8. daanx commented on Jul 27, 2026

    @daanx
    Collaborator

    Can confirm this fixes the issue.

    Thank you for checking!

    For performance, usually mi_free(p) does not check upfront if p is NULL but 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.

  9. SergeyKasmy commented on Jul 28, 2026

    @SergeyKasmy
    Author

    Got it, thanks for explaining!

  10. Zoxc commented on Jul 30, 2026

    @Zoxc

    This may be the cause of segfaults in rust-lang/rust#155575 too

  11. daanx commented on Aug 1, 2026

    @daanx
    Collaborator

    Hi @zachees -- I upstreamed the mingw fixes you mentioned in commit 60c4f03 (in the latest dev, dev2 and dev3).
    Let me know if that works for you on mingw (as I cannot test it myself).

  12. daanx commented on Aug 1, 2026

    @daanx
    Collaborator

    This should be fixed now in the latest release (v3.4.4)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions