mi_free() crashes on foreign pointers in release MI_SECURE=OFF#1285
Open
jaschiu wants to merge 1 commit intomicrosoft:dev3from
Open
mi_free() crashes on foreign pointers in release MI_SECURE=OFF#1285jaschiu wants to merge 1 commit intomicrosoft:dev3from
MI_SECURE=OFF#1285jaschiu wants to merge 1 commit intomicrosoft:dev3from
Conversation
Release Linux builds with MI_SECURE=OFF (and no MI_DEBUG, not __APPLE__) used _mi_unchecked_ptr_page, which dereferences _mi_page_map_at(idx) without a NULL check. Any `mi_free(p)` on a pointer outside mimalloc's committed arena slices (e.g. a stack pointer or an allocation from a library that wasn't intercepted by the override) therefore SIGSEGVs, while glibc / jemalloc / tcmalloc / mimalloc-secure / mimalloc-debug / mimalloc-__APPLE__ all handle such calls gracefully. Observed via LD_PRELOAD=libmimalloc.so on LibreWolf 150 (Firefox derivative) on Linux: `gbm_create_device` dlopens libLLVM.so.20.1 whose static initializer calls `free()` on a Mesa/llvmpipe worker-thread stack pointer. Fix: route `_mi_ptr_page` through `_mi_checked_ptr_page` unconditionally. The extra cost is one predicted-not-taken branch per free; stress tests show no measurable regression. Adds a targeted `test-free-foreign` regression test.
Author
@microsoft-github-policy-service agree |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hi, on my Gentoo Linux system where I have Librewolf (a fork of firefox) use mimalloc via
LD_PRELOAD, I noticed that when running it with mimalloc secure mode off, it segfaults. I had my LLM vibecode this PR and the below description.Summary
In a standard
CMAKE_BUILD_TYPE=Releasebuild withMI_SECURE=OFF(and noMI_DEBUG, non-Apple),mi_free(p)dereferences a NULL pagemap submap pointer and SIGSEGVs wheneverpis a valid-but-foreign pointer — i.e. a pointer that was not allocated out of a mimalloc arena. Every other production allocator (glibc malloc, jemalloc, tcmalloc) tolerates such calls; mimalloc's own debug /MI_SECURE/__APPLE__builds already do as well. Only the default release Linux fast path is affected.This is observable in the wild:
LD_PRELOAD=libmimalloc.so librewolf(LibreWolf 150.0.1, identical for Firefox with any Mesa-accelerated build) reliably segfaults duringgbm_create_device→dlopen("libLLVM.so.20.1"), because an LLVM static initializer callsfree()on a pointer that lives on a Mesa/llvmpipe worker thread's stack (or its pthread TCB page). I assume most Mesa-accelerated Firefox/Chromium setups on Linux hit this if they build mimalloc withoutMI_SECURE.Repro (tested versions: v3.1.5, v3.2.8, v3.3.2, current
dev3tip (8e4230a) — all affected)Environment:
CFLAGS=-march=native -O3 -flto ...(reproduces with plain-O2 -march=nativetoo)MI_PAGE_MAP_FLAT=0)Steps:
Backtrace (
RelWithDebInfo)Faulting instruction:
mov (%rax,%rcx,8),%rdiwithrax=0x0(submap pointer),rcx=0xf02(sub_idx) → SIGSEGV atsi_addr = 0x7810. Matches the strace-visible signaturesi_addr=0x6650/0x7810/etc. (differentsub_idxper run).Pointer provenance
/proc/<pid>/mapsshows0x7fffcf029320lying in a ~1008 KiB anonymousrw-pregion bracketed by---pguard pages — the canonical Mesa/llvmpipe worker-thread stack layout. It is NOT inside the mimalloc arena range (0x7fff85cd0000-0x7fffc5cd0000in this run). Glibc'sfree()on this same pointer is a no-op (or a graceful "invalid pointer" error) — LibreWolf runs fine withoutLD_PRELOADand withLD_PRELOAD=libmimalloc-secure.so.Root cause
include/mimalloc/internal.h:658-664:_mi_unchecked_ptr_pageunconditionally dereferences_mi_page_map_at(idx):The
// NULL if p==NULLcomment is incorrect — the load crashes on anypwhose page-map submap slot is NULL, which happens for every pointer outside mimalloc's committed arena slices.mi_free_ex(src/free.c:170-171) is already written to toleratepage == NULL, and_mi_checked_ptr_page(include/mimalloc/internal.h:647-653) literally adds oneif mi_unlikely(sub == NULL) return NULL;. The graceful contract exists — the release Linux fast path is the only call site that skips it.Proposed fix (one hunk)
Cost: one predicted-not-taken
test/jeperfree. Benchmarked no measurable change intest-stressruntime on this box. Brings release Linux builds into behavioral parity with glibc / jemalloc / tcmalloc and with mimalloc's ownMI_SECURE=ON/MI_DEBUG/__APPLE__builds.Regression test
Added
test/test-free-foreign.ccoveringmi_free(NULL),mi_free(stack_ptr),mi_free(static_ptr),mi_free(arbitrary_va), and a roundtripmi_malloc→mi_freeafterward. Registered via the existingforeach(TEST_NAME api api-fill stress free-foreign)loop in the top-levelCMakeLists.txt. All 5 subtests pass with the fix; thestack_ptr/static_ptr/arbitrary_vasubtests crash without it in the default release build.Verification
On the repro machine (3 consecutive trials per row,
timeout 12 librewolf -no-remote --profile <tmp> about:blank):LD_PRELOADMI_SECURE=OFFMI_SECURE=ONMI_SECURE=OFFMI_SECURE=ONMI_SECURE=OFF+ the patch aboveMI_SECURE=OFFMI_SECURE=OFF+ the patch abovedev3@8e4230a9 +MI_SECURE=OFFdev3@8e4230a9 +MI_SECURE=OFF+ the patch aboveThe patch applies cleanly via
git applyagainst v3.2.8, v3.3.2, and currentdev3tip; the hunk in_mi_ptr_pagehasn't been touched upstream since v3.1.5. Fullctestsuite (test-api,test-api-fill,test-stress,test-free-foreign,test-stress-dynamic) is 5/5 pass on both v3.3.2 anddev3after the patch.Why this wasn't caught earlier
LD_PRELOADuse applications that don't dlopen LLVM/Mesa after mimalloc init (e.g. server workloads, benchmarks).mimalloc-3.1.5ebuild exposed ahardenedUSE flag that mapped to-DMI_SECURE=ON; Firefox/LibreWolf users withhardenedset (a common Gentoo default) implicitly used the checked path and never hit this.mimalloc-3.2.8-r1ebuild hard-codes-DMI_SECURE=OFF, newly exposing release users to the fast path.