Skip to content

macOS heap corruption at thread exit (SIGTRAP) since v3.4.0/v3.4.1's fixed-TLS-slot change (126/127) -- bisected to f211b370 #1333

Description

@psiha

Summary

Starting with v3.4.0 (and still present in v3.4.1), programs that use mimalloc on macOS/arm64 and also link CoreFoundation can crash reliably at thread-exit time with a heap-corruption abort:

abort -> malloc_vreport -> malloc_report ->
___BUG_IN_CLIENT_OF_LIBMALLOC_POINTER_BEING_FREED_WAS_NOT_ALLOCATED ->
_pthread_tsd_cleanup -> _pthread_exit

git bisect (v3.3.2 good, v3.4.1 bad) lands on exactly one commit:

f211b37 — "update macOS TLS slots to 126/127 to avoid Swift slots"

which moved MI_TLS_MODEL_FIXED_SLOT_DEFAULT/MI_TLS_MODEL_FIXED_SLOT_CACHED from raw pthread TSD slots 108/109 to 126/127 in include/mimalloc/prim.h. The immediate parent commit, 41dd408, is clean (0 crashes across many runs with the same repro). v3.3.2 is also clean.

Environment

  • macOS 26.5.1 (build 25F80), arm64 (Apple Silicon, MacBookAir10,1)
  • Apple clang version 21.0.0 (clang-2100.1.1.101), also reproduced with Homebrew llvm@22
  • mimalloc built as a static library (MI_BUILD_SHARED=OFF), default options otherwise

Minimal repro

Does not reproduce with a plain pthread-only program. Reproduces reliably (SIGTRAP, first iteration, every run) as soon as CoreFoundation is linked — even though the program never calls a single CF function; just having the framework loaded is enough. This suggests slot 126 or 127 collides with something CoreFoundation's own startup claims on this OS version, even though neither slot appears assigned in Apple's public darwin-libpthread's tsd_private.h as of this report (that header lists Swift at 100-109, libmalloc at 110-114, libdispatch workgroups at 115-119, and nothing between 120 and 189 except PerfUtils at 190-194 -- so either that public header lags the shipping OS's actual internal reservations, or the collision mechanism is different from a documented-slot collision).

#include <mimalloc.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>

#define NUM_THREADS 8
#define NUM_ITERATIONS 50
#define ALLOCS_PER_THREAD 1000

static void* worker(void* arg) {
    (void)arg;
    for (int i = 0; i < ALLOCS_PER_THREAD; i++) {
        size_t sz = 32 + (size_t)(i % 512);
        void* p = mi_malloc(sz);
        if (p) memset(p, 0xAB, sz);
        void* q = mi_malloc(16);
        mi_free(p);
        mi_free(q);
    }
    return NULL;
}

int main(void) {
    for (int iter = 0; iter < NUM_ITERATIONS; iter++) {
        pthread_t threads[NUM_THREADS];
        for (int t = 0; t < NUM_THREADS; t++) pthread_create(&threads[t], NULL, worker, NULL);
        for (int t = 0; t < NUM_THREADS; t++) pthread_join(threads[t], NULL);
        printf("iteration %d done\n", iter);
    }
    printf("ALL ITERATIONS COMPLETE, NO CRASH\n");
    return 0;
}

Build (the -framework CoreFoundation is the load-bearing part):

cc -O2 -I <mimalloc>/include repro.c <mimalloc-build>/libmimalloc.a -o repro -lpthread -framework CoreFoundation
./repro

How we found this

We use mimalloc in a Node.js native addon. Bumping our pin from v3.3.2 to v3.4.1 (to pick up one of the issue #1271 fixes) caused a Node.js worker-thread test to start crashing 100% of the time on macOS CI, with the same crash signature as above (multiple pthread-created threads aborting identically at exit). We bisected within mimalloc's own history against a real reproduction of that crash before distilling it down to the standalone repro above.

Workaround

We've reverted to v3.3.2 for now. Commit 41dd408 (the parent of f211b37) also works and has ~88 more commits of fixes over v3.3.2, including several issue #1271 items, so it may be a better interim base for anyone who needs those fixes without the new crash.

Happy to provide the full macOS .ips crash report (with paths/pids redacted) if useful.

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