2.1.243 native build segfaults instantly on startup: interposed free() crashes on free(NULL) from glibc 2.44 newlocale
Summary
The native (Bun standalone) Linux x64 build of 2.1.243 segfaults on every invocation — including claude --version — before printing a single byte. 2.1.241, 2.1.236 and 2.1.235 all run fine on the same machine.
This is not a corrupt download: I hashed my copy, deleted it, re-downloaded via claude install latest, and got a byte-identical binary (sha256 4b0dafeedd0b469c41988e200036fd773e7553ba960349c9f02a82c6d1f2ba27) that segfaults identically.
I have a core dump and disassembly; the root cause is below.
Environment
- CachyOS Linux, kernel
7.2.0-1-cachyos, bare metal - glibc 2.44 (
ldd (GNU libc) 2.44) - Intel Core i7-7700 (sse4_2, avx, avx2 — no AVX-512)
- Both 2.1.241 and 2.1.243 embed Bun v1.4.0 (per
strings), so it is not a runtime bump - Locale-independent: crashes under
LC_ALL=C,C.UTF-8, andenv -i HOME=$HOME PATH=/usr/bin - ASLR-independent: crashes under
setarch -R(binary is non-PIE anyway)
Crash
dmesg (identical ip on every run):
claude[15182]: segfault at 0 ip 0000000001d10458 sp 00007ffe2cb0dfb0 error 4 in 2.1.243[1b0f458,1953000+3b33000]
error 4 + at 0 = user-mode read of unmapped page at address 0. si_code: SEGV_MAPERR.
coredumpctl backtrace:
#0 0x0000000001d10458 free (2.1.243 + 0x1b10458)
#1 0x00007fdfbce3a1ab __newlocale (libc.so.6 + 0x3a1ab)
#2 0x0000000001abd282 n/a (2.1.243 + 0x18bd282)
#3 0x00007fdfbceb2584 n/a (libc.so.6 + 0xb2584)
#4 0x00007fdfbceb26a9 pthread_once (libc.so.6 + 0xb26a9)
#5 0x0000000001abc2d2 n/a (2.1.243 + 0x18bc2d2)
...
#16 0x00007fdfbce27e23 __libc_start_main (libc.so.6 + 0x27e23)
Registers at the fault (gdb on the core):
rax 0x0 rcx 0x0 rdi 0x0 rsi 0x0
Root cause
2.1.243 newly interposes the C allocator, and the interposed free crashes on free(NULL).
- 2.1.243 exports
malloc/free/__libc_malloc/__libc_free/__libc_cfreeas strong symbols from the executable. 2.1.241 exports none of these — this is the regression between the two builds:
````
$ nm 2.1.241 | grep -E ' (free|malloc|__libc_)' → (nothing)
$ nm 2.1.243 | grep -E ' (free|malloc|__libc_)'
0000000001d10430 T free
0000000001d10430 T __libc_cfree
0000000000d10430 T __libc_free
0000000001d102d0 T __libc_malloc
0000000001d102d0 T malloc
Because the executable wins symbol resolution, glibc's own internal free calls now land in the bundled allocator (mimalloc).
- During startup, a static initializer in the binary (frame #2, under
pthread_once) calls glibcnewlocale(). On glibc 2.44 this path callsfree(NULL)(legal; must be a no-op).
- The interposed
freedoes its segment-map lookup before any NULL check. Disassembly at the fault:
``asm``
0000000001d10430 <__libc_cfree>:
1d10434: mov %rdi,%rsi
1d10437: mov 0x3781982(%rip),%rax # segment map @ 0x5491dc0
1d1043e: mov %rdi,%rcx
1d10441: shr $0x1d,%rcx
1d10445: mov 0x80(%rax,%rcx,8),%rax # map entry for ptr 0 → 0
1d1044d: mov %esi,%ecx
1d1044f: shr $0xd,%ecx
1d10452: and $0xfff8,%ecx
1d10458: mov (%rax,%rcx,1),%rdi # ← deref of NULL entry: SIGSEGV
1d1045c: test %rdi,%rdi # null check comes one insn too late
1d1045f: je ...
With rdi = 0 (the freed pointer): the segment-map entry for pointer 0 is 0, and the code dereferences it at 1d10458 before the test %rdi,%rdi guard. Deterministic crash at a fixed address on every run — matching the identical ip in all dmesg lines.
So any glibc whose newlocale/startup path calls free(NULL) (glibc 2.44 here) kills 2.1.243 instantly, regardless of locale settings or environment. Upstream mimalloc handles a NULL segment-map entry (mi_free(NULL) is a no-op), so this looks like the check was reordered/dropped in this build's interposition shim or by LTO.
Steps to reproduce
On a glibc 2.44 system (e.g. up-to-date Arch/CachyOS):
~/.local/share/claude/versions/2.1.243 --version # SIGSEGV
~/.local/share/claude/versions/2.1.241 --version # 2.1.241 (Claude Code)
Workaround
ln -sf ~/.local/share/claude/versions/2.1.241 ~/.local/bin/claude
plus pinning in ~/.claude/settings.json:
{ "autoUpdatesChannel": "stable", "minimumVersion": "2.1.241" }
(Note: claude install latest repoints the symlink back at the broken build, so the auto-updater re-breaks the install until pinned.)
Possibly related
#84293 reports a startup crash on Bun 1.4.0/x64-baseline, but that one is si_code=SI_KERNEL (general protection fault) and was closed as a corrupt download. Mine is SEGV_MAPERR with a verified-identical re-download — different fault class, and evidence that report may have been misdiagnosed.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗