anthropics / anthropics/claude-code

[BUG] v2.1.242 segfaults on every launch (even `--version`) — interposed mimalloc `free` has no NULL check, glibc `newlocale` calls `free(NULL)` pre-main; v2.1.241 unaffected

Open
#89,334 16 comments 11 reactions 0 assignees View on GitHub
area:packaging bug has repro platform:linux regression
Dominant language
Python
Stars
145k
Forks
23.1k
PR merge metrics
PR metrics pending

Description

## Summary

v2.1.242 (native install, Linux x64) segfaults on **every** invocation, including `claude --version`. The crash is pre-`main`, during startup. v2.1.241 and earlier are unaffected.

Root cause: 2.1.242 is the first build to export its bundled mimalloc as **versioned glibc allocator symbols**, which interposes the allocator process-wide. The interposed `free` does not handle a NULL argument, so the first `free(NULL)` anywhere in the process — glibc's own `newlocale` makes one during startup — dereferences a null pointer.

`free(NULL)` is required to be a no-op (C17 7.22.3.3p2), so this will fire on any libc path that frees a null pointer.

## Reproduction

```
$ ~/.local/share/claude/versions/2.1.242 --version
[1] 88020 segmentation fault (core dumped)
$ echo $?
139
```

Every launch, 100% reproducible, no config or arguments required.

Adjacent versions on the same machine, same glibc, same shell:

```
2.1.239: exit=0 2.1.239 (Claude Code)
2.1.240: exit=0 2.1.240 (Claude Code)
2.1.241: exit=0 2.1.241 (Claude Code)
2.1.242: exit=139 (SIGSEGV)
```

## Backtrace

```
Signal: 11 (SEGV) si_code: SEGV_MAPERR
#0 0x0000000001d10458 in free () <- in the claude binary, not libc
#1 0x00007ffff7c3530a in newlocale () from /usr/lib/libc.so.6
#2 0x0000000001abd282 in ?? ()
#3 0x00007ffff7c9d7fc in ?? () from /usr/lib/libc.so.6
#4 0x00007ffff7c9d879 in pthread_once () from /usr/lib/libc.so.6
#5 0x0000000001abc2d2 in ?? ()
...
#16 0x00007ffff7c27892 in __libc_start_main () from /usr/lib/libc.so.6
#17 0x0000000001953a2e in ?? ()
```

## Root cause

**1. 2.1.242 newly interposes the process allocator.** `readelf --dyn-syms` on the two builds:

```
$ readelf --dyn-syms --wide 2.1.241 | grep -E '\b(free|malloc|calloc|realloc|aligned_alloc|posix_memalign)\b'
(no output)

$ readelf --dyn-syms --wide 2.1.242 | grep -E '\b(free|malloc|calloc|realloc|aligned_alloc|posix_memalign)\b'
560: 0000000001d10320 145 FUNC GLOBAL DEFAULT 17 calloc@@GLIBC_2.2.5
700: 0000000001d102d0 79 FUNC GLOBAL DEFAULT 17 malloc@@GLIBC_2.2.5
862: 0000000001d10b70 122 FUNC GLOBAL DEFAULT 17 aligned_alloc@@GLIBC_2.16
927: 0000000001d10ab0 181 FUNC GLOBAL DEFAULT 17 posix_memalign@@GLIBC_2.2.5
1048: 0000000001d10430 126 FUNC GLOBAL DEFAULT 17 free@@GLIBC_2.2.5
1213: 0000000001d103c0 98 FUNC GLOBAL DEFAULT 17 realloc@@GLIBC_2.2.5
```

The executable is first in the global symbol lookup scope, so glibc's own internal calls to `free` now bind to mimalloc inside the claude binary.

**2. The interposed `free` has no NULL check.** Disassembly of `free` in 2.1.242:

```
0000000001d10430 :
1d10430: push %rbp
1d10431: mov %rsp,%rbp
1d10434: mov %rdi,%rsi
1d10437: mov 0x3781982(%rip),%rax # segment map base
1d1043e: mov %rdi,%rcx
1d10441: shr $0x1d,%rcx # ptr >> 29
1d10445: mov 0x80(%rax,%rcx,8),%rax # rax = map[ptr >> 29] -> NULL for ptr==0
1d1044d: mov %esi,%ecx
1d1044f: shr $0xd,%ecx
1d10452: and $0xfff8,%ecx
1d10458: mov (%rax,%rcx,1),%rdi # <-- SIGSEGV, rax=0, rcx=0 => load from 0x0
```

With `ptr == NULL`: `rcx = 0`, the segment-map slot is NULL, and the next load dereferences address 0. No null guard anywhere on this path.

**3. The faulting call really is `free(NULL)`.** Breaking on `free` shows the first call in the process is null, and it is the one that crashes:

```
$ gdb -batch -ex 'break free' -ex 'run --version' -ex 'printf "free arg #1 = %p\n", $rdi' -ex continue --args 2.1.242 --version
Breakpoint 1.1, 0x0000000001d10434 in free ()
free arg #1 = (nil)

Program received signal SIGSEGV, Segmentation fault.
0x0000000001d10458 in free ()
```

Register state at the fault corroborates it: `rax=0`, and `rsi=0` — `rsi` is the preserved copy of the incoming argument made at `free+4` and never rewritten before the fault.

## Ruled out

- **Not locale-dependent.** `LC_ALL=C LANG=C` crashes identically. `newlocale` is simply the first libc path to hit `free(NULL)`.
- **Not a corrupt or truncated download.** Valid ELF, `ldd` resolves cleanly, binary is not stripped.
- **Not a glibc regression.** glibc 2.44 has been installed since 2026-08-12; 2.1.241 was installed 2026-08-23 and works fine on that same glibc.
- **Not launcher/wrapper related.** Executing the versioned binary directly reproduces it.

## Environment

- Claude Code 2.1.242, native install (`~/.local/share/claude/versions/2.1.242`)
- `BUILD_TIME: 2026-08-24T17:00:10Z`, `GIT_SHA: 2482ce9083708842d832441fde1721626f306157`
- Binary size jumped 342,636,848 -> 377,568,472 bytes vs 2.1.241
- Arch Linux, kernel 7.1.8-zen1-3-zen x86_64
- glibc 2.44 (`glibc 2.44+r24+g16be1518495f-1`)
- AMD Ryzen 7 5800X3D

## Impact

The CLI cannot start at all, which also means it cannot auto-update out of the broken state. Recovery requires manually repointing `~/.local/bin/claude` at an older version:

```
ln -sfn ~/.local/share/claude/versions/2.1.241 ~/.local/bin/claude
```

## Suggested fix

Add the standard `if (p == NULL) return;` guard to the exported `free` (and check `realloc(NULL, n)` on the same interposition path), or stop exporting versioned glibc allocator symbols so mimalloc is only used for the runtime's own allocations.

Possibly related pre-`main` startup regression from the same area: #87583.

Contributor guide

No contributing guide indexed for this repository

Research direction

No source files or tests are named. Start by locating the bundled mimalloc allocator and its symbol-export configuration, then compare versions 2.1.241 and 2.1.242 with readelf and inspect the exported free/realloc paths. Reproduce with the versioned binary and --version; done when startup succeeds and free(NULL) no longer crashes.

Written by the indexing model from the issue text.

Assessment

Tech stack
c, linux
Domain
cli, operating-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.