[Bug] Native build 2.1.243 segfaults on glibc 2.44 due to malloc/free symbol interposition

Status Open
Reported on v2.1.239
Maintainer reply None cached
Activity 3 comments · opened Aug 25, 2026

Bug Description
Title: Claude Code 2.1.243 segfaults before argument parsing on glibc 2.44 because the executable interposes malloc/free

Summary

Claude Code native build 2.1.243 crashes with SIGSEGV before command dispatch on an up-to-date CachyOS/Arch Linux x86_64-v3 system. Even claude --version and claude doctor crash. Native builds 2.1.239, 2.1.240, and 2.1.241 run correctly on the same host. The distinguishing ELF change is that 2.1.243 defines and exports malloc, free, and __libc_* allocator aliases; 2.1.241 leaves the allocator to glibc. The core backtrace shows glibc __newlocale() calling the executable's free(), which faults. This is consistent with allocator interposition causing an allocation-ownership mismatch inside glibc locale initialization.

Environment

  • Claude Code: native Linux x86_64 build 2.1.243
  • Claude 2.1.243 commit/build ID available from ELF: BuildID c39577b41056cc1abf1ea7ed72a8b4c1ba407c42
  • SHA-256: 4b0dafeedd0b469c41988e200036fd773e7553ba960349c9f02a82c6d1f2ba27
  • Distribution: CachyOS/Arch Linux
  • glibc package: 2.44+r24+g16be1518495f-1, x86_64-v3
  • getconf GNU_LIBC_VERSION: glibc 2.44
  • Kernel at reproduction: 7.1.8-1-cachyos x86_64
  • CPU class: x86_64-v3
  • Locale tested: C, C.UTF-8, and en_US.UTF-8

Minimal reproducer

~/.local/share/claude/versions/2.1.243 --version
# Segmentation fault (core dumped), exit 139

~/.local/share/claude/versions/2.1.243 doctor
# Segmentation fault (core dumped), exit 139

The crash is independent of Claude settings, MCP configuration, Chrome integration, authentication, and command selection because it occurs before --version can print. Setting LC_ALL=C, LC_ALL=C.UTF-8, or LC_ALL=en_US.UTF-8 does not change the result.

Version boundary

2.1.239 --version -> 2.1.239 (Claude Code), exit 0
2.1.240 --version -> 2.1.240 (Claude Code), exit 0
2.1.241 --version -> 2.1.241 (Claude Code), exit 0
2.1.243 --version -> SIGSEGV, exit 139

Evidence

systemd-coredump reports SIGSEGV, SEGV_MAPERR. The relevant stack is:

#0  0x0000000001d10458 free                    (Claude 2.1.243)
#1  0x00007ffff7c3a1ab __newlocale             (glibc 2.44, newlocale.c:291)
#2  0x0000000001abd282                          (Claude 2.1.243)
#3  __pthread_once_slow                         (glibc)
#4  pthread_once                                (glibc)
...
#16 __libc_start_main_impl                      (glibc)

The faulting address resolves to the free implementation linked into the 2.1.243 executable.

ELF symbol delta

2.1.241 has no defined global malloc or free; only normal glibc imports such as setlocale appear. In contrast, 2.1.243 defines globally visible allocator symbols:

0000000001d102d0 T __libc_malloc
0000000001d102d0 T malloc
0000000001d10320 T __libc_calloc
0000000001d10320 T calloc
0000000001d103c0 T __libc_realloc
0000000001d103c0 T realloc
0000000001d10430 T __libc_cfree
0000000001d10430 T __libc_free
0000000001d10430 T free

readelf -Ws also reports malloc@@GLIBC_2.2.5 and free@@GLIBC_2.2.5 as global definitions in 2.1.243. This allows glibc calls that are semantically paired with glibc-internal allocation paths to resolve to the executable's replacement allocator. The __newlocale() frame followed by the executable's free() is the direct observed failure mechanism.

Expected behavior

claude --version, claude doctor, and interactive startup should initialize and run on supported current glibc releases without crashing.

Actual behavior

Every 2.1.243 invocation crashes during locale/runtime initialization before Claude processes its arguments.

Recommended fix

  1. Remove the allocator-export change introduced between 2.1.241 and 2.1.243, or rebuild the native executable without exporting/interposing malloc, calloc, realloc, free, and especially the __libc_* aliases.
  2. If a bundled allocator is required, keep its entry points hidden or versioned and call it through private names. Do not present replacement allocator functions as glibc symbols from the main executable unless the complete glibc allocation ABI and ownership model is intentionally supported.
  3. Audit linker flags and the Bun/native packaging change that caused allocator symbols to become globally defined in the executable. Compare the dynamic symbol tables of 2.1.241 and 2.1.243 as a release gate.
  4. Add startup smoke tests for --version and doctor in containers or VMs using glibc 2.44, including Arch/CachyOS. These must execute the final distributed ELF, not only an unpackaged development binary.
  5. Add an ELF release check that fails if the native Claude executable unexpectedly exports malloc, calloc, realloc, free, or __libc_* allocator aliases.

Workaround used

I used the native installer from 2.1.241 to reinstall exact version 2.1.241, repointed ~/.local/bin/claude through the supported installer, and disabled automatic updates.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗