Fix realloc crash when realloc is the first allocation on a thread (#1304)#1312
Open
gbaraldi wants to merge 1 commit into
Open
Fix realloc crash when realloc is the first allocation on a thread (#1304)#1312gbaraldi wants to merge 1 commit into
gbaraldi wants to merge 1 commit into
Conversation
…ft#1304) On platforms with a fixed or dynamic TLS slot (macOS, Windows, OpenBSD) the thread-local default theap is NULL until the first allocation lazily initializes it (MI_THEAP_INITASNULL). All malloc paths tolerate a NULL theap, but the in-place fast path of _mi_theap_realloc_zero evaluated _mi_theap_heap(theap) unconditionally, dereferencing the NULL theap and crashing (SIGSEGV at offset 0x8) when a fresh thread's first mimalloc call was an in-place-fitting mi_realloc of a block allocated on another thread. Guard the in-place check with mi_theap_is_initialized(theap) under MI_THEAP_INITASNULL, so a NULL (or partially-initialized) theap falls through to mi_theap_umalloc, which lazily initializes the thread heap and copies. The guard compiles out on the THREAD_LOCAL model (Linux), where the default theap is never NULL, so there is no extra hot-path cost there. Add test/test-realloc-thread.c covering an in-place-fitting and a growing realloc as the first allocation on a fresh thread (SIGSEGV before this fix). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author
|
@microsoft-github-policy-service agree company="JuliaHub" |
daanx
added a commit
that referenced
this pull request
Jun 18, 2026
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.
Found this while running Julia with mimalloc 3. Seems like just a missing guard.
AI Slop ahead
Summary
Fixes #1304 — a crash when
reallocis the first mimalloc call on a freshly created thread.On platforms with a fixed or dynamic TLS slot (macOS, Windows, OpenBSD), the thread-local default
theapisNULLuntil the first allocation lazily initializes it (MI_THEAP_INITASNULL). Everymallocpath tolerates aNULLtheap and routes to the generic lazy-init path, but the in-place fast path of_mi_theap_realloc_zeroevaluated_mi_theap_heap(theap)unconditionally:_mi_theap_heap()doesmi_atomic_load_acquire(&theap->heap), so when a fresh thread's first call is an in-place-fittingmi_reallocof a block allocated on another thread,theap == NULLand this dereferences a null pointer → SIGSEGV. (mi_mallocdoes not hit this because all malloc paths already guardtheap != NULLunderMI_THEAP_INITASNULL.)Reproduction
First allocator call on a brand-new thread is an in-place-fitting realloc of a block from another thread:
lldbon macOS arm64 (v3.3.2):EXC_BAD_ACCESS (code=1, address=0x8)in_mi_theap_realloc_zero, instructionldapr x8, [x8]withx8 == 0— i.e. the acquire-load oftheap->heap(offset0x8) through a NULL theap. This matches the crash frame reported in #1304 (libcurl/libopenssl + manystd::asyncworkers).Fix
Guard the in-place check with
mi_theap_is_initialized(theap)under#if MI_THEAP_INITASNULL:A
NULL(or partially-initialized) theap then falls through tomi_theap_umalloc, which lazily initializes the thread heap, copies, and frees the old block — exactly asmi_mallocalready does. This is also semantically correct: if the thread has no heap yet,pnecessarily belongs to a different heap, so the "same heap" in-place check would be false regardless.mi_theap_is_initialized(rather than a baretheap != NULL) additionally covers thetheap != NULL && heap == NULLpartial-init window. The guard compiles out entirely on theTHREAD_LOCALmodel (Linux), where the default theap is neverNULL, so there is no added cost on that hot path.Test
Adds
test/test-realloc-thread.c(registered underMI_BUILD_TESTS): an in-place-fitting and a growingreallocas the first allocation on a fresh thread. It SIGSEGVs without this patch (exit 139) and passes with it.Verified on macOS arm64 (
MI_TLS_MODEL_FIXED_SLOT):test-realloc-thread4/4,test-api37/0,test-api-fill17/0,test-stressclean.🤖 Generated with Claude Code