[BUG] Recurring mid-session SIGSEGV of native linux-x64 binary across 2.1.210-2.1.278 (Bun 1.4.3, glibc 2.42) - distinct from the glibc 2.44 startup cluster
Summary
The native linux-x64 binary SIGSEGVs mid-session, not at startup, and has done so repeatedly across nine releases over the last two months. This appears distinct from the large cluster of startup-segfault reports (#89370, #89412, #89415, #89504, #89732, #89759, #92749, #94462), which are tied to glibc 2.44 / Arch and fault inside __newlocale during init. This machine runs glibc 2.42, startup is reliable, and the crash lands after a long, healthy session.
Environment
| | |
|---|---|
| Claude Code | 2.1.278 (native install, ~/.local/share/claude/versions/2.1.278) |
| Bundled Bun | 1.4.3 |
| OS | Mageia 10 |
| Kernel | 6.18.44-desktop-1.mga10 x86_64 |
| glibc | 2.42-10.mga10 |
| Terminal | Konsole |
Crash history
systemd-coredump has recorded six SIGSEGVs of the claude binary on this machine:
| Date | Version |
|---|---|
| 2026-07-28 | 2.1.210 |
| 2026-07-28 | 2.1.218 |
| 2026-08-22 | 2.1.233 |
| 2026-09-10 | 2.1.266 |
| 2026-09-11 | 2.1.268 |
| 2026-09-20 | 2.1.278 |
So this is not a regression introduced by one release — it spans 2.1.210 through 2.1.278 and at least three different bundled Bun versions.
Evidence that it is not a startup crash
For the 2026-09-20 crash (PID 365693):
- The session transcript's first entry is
2026-09-20T08:28:52Z; its last entry is2026-09-20T09:54:48Z. The core was dumped at09:56:21Z— roughly 1 h 27 min into the session, and ~93 s after the last recorded activity. - The thread list in the core shows two distinct generations of thread IDs: the startup threads (365697–365811, contemporaneous with the main thread's PID 365693) and a much later generation (509729, 509731, 509733, 509755, 510082) created once the system PID counter had advanced by ~144 000. Those later threads are all parked in
pthread_cond_timedwait. - The transcript's final line is structurally complete, so the process was not killed mid-write; it faulted while idle or between operations.
Faulting thread
Binary is stripped, so only offsets are available. The shape is a fault handler re-raising: __restore_rt at #6 means frames #7 and above are the original fault, and frames #12–#14 are the same address repeated, which suggests a recursive path.
Stack trace of thread 365693:
#0 0x00007f5f78068c6c __pthread_kill_implementation (libc.so.6 + 0x92c6c)
#1 0x00007f5f78011da6 raise (libc.so.6 + 0x3bda6)
#2 0x00000000037c88b2 n/a (claude 2.1.278 + 0x35c88b2)
#3 0x00000000037c7c9d n/a (claude 2.1.278 + 0x35c7c9d)
#4 0x00000000037c8664 n/a (claude 2.1.278 + 0x35c8664)
#5 0x0000000003b2ecdc n/a (claude 2.1.278 + 0x392ecdc)
#6 0x00007f5f78011ef0 __restore_rt (libc.so.6 + 0x3bef0) <-- signal boundary
#7 0x0000000004beb15d n/a (claude 2.1.278 + 0x49eb15d) <-- fault site
#8 0x0000000003b8cfe6 n/a (claude 2.1.278 + 0x398cfe6)
#9 0x0000000004ebcf5f n/a (claude 2.1.278 + 0x4cbcf5f)
#10 0x00000000046e5acb n/a (claude 2.1.278 + 0x44e5acb)
#11 0x00007f5f73c8c017 n/a (n/a + 0x0) <-- JIT frame
#12 0x0000000003cbfc55 n/a (claude 2.1.278 + 0x3abfc55)
#13 0x0000000003cbfc55 n/a (claude 2.1.278 + 0x3abfc55)
#14 0x0000000003cbfc55 n/a (claude 2.1.278 + 0x3abfc55)
#15 0x0000000003c9fcc4 n/a (claude 2.1.278 + 0x3a9fcc4)
#16 0x0000000004b693c9 n/a (claude 2.1.278 + 0x49693c9)
#17 0x0000000004b64908 n/a (claude 2.1.278 + 0x4964908)
#18 0x0000000004e2865e n/a (claude 2.1.278 + 0x4c2865e)
#19 0x0000000004f83494 n/a (claude 2.1.278 + 0x4d83494)
#20 0x000000000233c79d n/a (claude 2.1.278 + 0x213c79d)
#21 0x00007f5f740d6f1a n/a (n/a + 0x0) <-- JIT frame
#22 0x00007f5f740d6f41 n/a (n/a + 0x0) <-- JIT frame
#23 0x0000000003c9fcc4 n/a (claude 2.1.278 + 0x3a9fcc4)
#24 0x00000000046287f7 n/a (claude 2.1.278 + 0x44287f7)
#25 0x000000000497f433 n/a (claude 2.1.278 + 0x477f433)
#26 0x000000000226af80 n/a (claude 2.1.278 + 0x206af80)
#27 0x00000000022b7d26 n/a (claude 2.1.278 + 0x20b7d26)
#28 0x0000000002fd5933 n/a (claude 2.1.278 + 0x2dd5933)
#29 0x000000000213df88 n/a (claude 2.1.278 + 0x1f3df88)
#30 0x0000000002fd5bd7 n/a (claude 2.1.278 + 0x2dd5bd7)
#31 0x0000000001d84226 n/a (claude 2.1.278 + 0x1b84226)
#32 0x0000000002047b06 n/a (claude 2.1.278 + 0x1e47b06)
#33 0x000000000204289b n/a (claude 2.1.278 + 0x1e4289b)
#34 0x000000000214258c n/a (claude 2.1.278 + 0x1f4258c)
#35 0x00007f5f77ffc2f9 __libc_start_call_main (libc.so.6 + 0x262f9)
#36 0x00007f5f77ffc3ab __libc_start_main@@GLIBC_2.34 (libc.so.6 + 0x263ab)
#37 0x0000000001922c2e n/a (claude 2.1.278 + 0x1722c2e)
Reproduction
No deterministic trigger identified. Ordinary interactive use; crashes recur every week or two.
What would help
- Shipping symbols, or a
.debugsidecar / build ID, so offsets like+0x49eb15dcan be resolved. As it stands, every Linux crash report is just offsets and no one can act on them. The binary does carryBuildID[sha1]=83effae1a76aa74ce70326f83068a1242f99094a, but there is nothing public to match it against. - I still have the 98.5 MB core for the 2026-09-20 crash and am happy to run any requested
gdbcommands against it, or send it privately. Earlier cores have been rotated away, so it will not last indefinitely.
Note on the workaround question
There is currently no way to run Claude Code off the Bun runtime. The npm package @anthropic-ai/claude-code is a 184 KB wrapper whose optionalDependencies fetch the same 234 MB native binary (@anthropic-ai/claude-code-linux-x64); its engines: node >=22 only governs the postinstall script. So for users hitting Bun-runtime faults there is no Node-based fallback to switch to. A supported way to run under Node would be a genuine mitigation for this whole class of report.
3 Comments
I've now examined the retained core under gdb. Two corrections to the original report, and a fault classification that I think rules this firmly out of the startup-segfault cluster.
Corrections
83effae1a76aa74ce70326f83068a1242f99094a(.note.gnu.build-id). I wrongly said it did not; I've fixed that sentence in the issue body.filereports the binary as "not stripped", but that is misleading:.symtabcontains 1234 entries and all of them are undefined imports (U abort,U accept, …). There are no defined symbols for any Bun or Claude Code function, so neither gdb noreu-stackcan resolve a single frame. The request for symbols stands.This is a #GP, not a null dereference
I recovered the kernel's saved
sigcontextfrom thert_sigframeon the crashed thread's stack (trampoline pointer found at0x7ffc391e67f8):| Field | Value |
|---|---|
|
trapno| 0xd (13) — #GP, general protection fault ||
err| 0 ||
cr2| 0 ||
RIP|0x4beb15d(binary + 0x49eb15d) ||
RSP|0x7ffc391e73e0||
r14|0x523c9baf8f90c376||
r8|0x24d47470cc8||
rax|0x24d3ff00010||
rbx/r13|0x24d38180060||
r12|0x24d3f30c310||
r15|0x24d38220088|Trap 13 with
cr2 = 0means this is not a null-pointer dereference — it is a non-canonical address access. Every report in the startup cluster (#89390, #89732, #89759 et al.) is trap 14 withsi_addr = NULLinside__newlocale. Different fault, different class of bug.The faulting instruction and where the bad pointer came from
r14is loaded from(%r8)and dereferenced eight instructions later with no validation.0x523c9baf8f90c376is non-canonical (bits 63:48 are not a sign-extension of bit 47), hence #GP rather than a page fault.The source memory is high-entropy data, not an object
Dumping around
r8:Every word in the neighbourhood is high-entropy. There are no object headers, no small integers, no canonical pointers — nothing that looks like a JS cell or a NaN-boxed JSValue.
Mapping the addresses against the core's LOAD segments,
r8and all the live object registers (rax,rbx,r12,r13,r15) fall inside a single 1024 MiB RW reservation at0x24d37000000–0x24d77000000— i.e. the JSC cage. RIP is in the binary's 58.9 MiB text segment (0x1922000–0x5408000), so the faulting frame is native code, not JIT; frames #11, #21 and #22 of the backtrace are at0x7f5f73c8c017/0x7f5f740d6f1a/0x7f5f740d6f41, outside any file-backed mapping, and are the JIT frames.So: native code read a 64-bit word out of the cage and used it directly as an object pointer, but that word came from a region holding buffer-like data rather than object graph. That is the signature of a stale or confused pointer — a use-after-free or a type confusion — rather than an allocator/libc interaction. The cage is what turned it into a clean #GP instead of silent corruption.
Also worth noting
Frames #12–#14 are the same address
0x3cbfc55three times. That is not runaway recursion — the call site is a generic indirect dispatch:so three levels of nested dispatch through a function-pointer table, which is unremarkable.
Offer
I still have the core (98.5 MiB compressed, 5.3 GiB expanded) and the exact binary, kept together. With even a stripped-symbol sidecar for build
83effae1a76aa74ce70326f83068a1242f99094aI could name the function at+0x49eb15dand the allocation site. Happy to run any gdb commands you want against it, or to hand the core over privately.Second crash 39 hours later — and the core says this is a wild write, not a bad null check
Same machine, same binary image (2.1.278, sha256
5c4735937844e84f8a93306e841a5b0e12252909b07870f789b190468da147ab,BuildID[sha1]=83effae1a76aa74ce70326f83068a1242f99094a), same boot: another SIGSEGV at 2026-09-21 01:03:33 BST, PID 509935. Both cores are preserved locally and I am happy to hand either one over, or run further commands against them, if that is useful.The two crashes look different at the top of the stack but are the same underlying fault, and the second core is much more informative because the fault landed somewhere I could reason about.
Fault
Registers recovered from the
rt_sigframeon the main stack (gdb cannot unwind past the signal frame here; locate__restore_rt=libc+0x3bef0on the stack, then readuc_mcontextat +40):trapno 13witherr 0andcr2 0means a non-canonical address, i.e. a #GP — not a null dereference. The 2026-09-20 crash was the same:trapno 13,err 0,cr2 0, withr14 = 0x523c9baf8f90c376. This remains clearly distinct from the glibc-2.44 startup cluster, which is trap 14 withsi_addr = NULL.The faulting code is JSC's
isMarked()So the collector formed a
MarkedBlock*from a garbage cell word and died reading the block's marking version.The cell is destroyed, header and all
[rdi+0x70] = 0x82ed85d935c35005— garbage with bit 0 set, so it is taken as a cell. But the whole 128-byte object atrdiis garbage, including word 0 where the StructureID + type/flags belong:The containing block is valid:
0x356700c4000is 16 KiB aligned, its marking version is 146 and matches the heap's, and its cells are a uniform 128-byte class whose header word is0x00000c0000a906b0in slot after slot with mostly-zero bodies. Entropy map of that block at 64-byte granularity:Four live cells overwritten with high-entropy data while their immediate neighbours are untouched.
The same fingerprint in the 2026-09-20 core
I scanned both 1 GiB JSC cage reservations for isolated high-entropy 256-byte units — the cage legitimately holds plenty of contiguous high-entropy buffer data, so the signature is an isolated run sitting inside a cell block:
| Core | Matching blocks | Hot offsets | Faulting load |
|---|---|---|---|
| 2026-09-21 |
0x356700c4000| +0x2800, +0x2c00 |0x356700c6800= +0x2800 || 2026-09-20 |
0x24d47470000| +0x800, +0xc00 |0x24d47470cc8= +0xcc8 || 2026-09-20 |
0x24d4582c000| +0x3500, +0x3900 | — |Three sites across two independent crashes. Every one is two 256-byte runs exactly 0x400 apart, 256-byte aligned. In both crashes the faulting load came out of one of them.
The 32 bytes at the 2026-09-21 site occur exactly once in the 5.3 GiB core, so this is not a stale copy of a buffer that is still live somewhere — whatever produced the data has since gone away.
Reading
This is an out-of-bounds or use-after-free write, not a GC logic slip: something writes 256-byte payloads at a 1024-byte stride into memory that JSC has already handed back out as cells, and the next collection to visit those cells dies. High-entropy 256-byte payloads in a process that is otherwise idle suggest a freed network or crypto buffer still being written after its backing store was reclaimed.
That also explains why the faulting RIP differs between the two crashes (binary-relative
0x49eb15dvs0x4376048): the GC is simply whoever trips over the damage first, so the visible crash site is incidental. Both faulted on the main thread, but by different routes — the 2026-09-20 stack has JIT frames below the fault, so the collection was entered from running JavaScript, whereas the 2026-09-21 stack has none and runs straight back to__libc_start_main. Chasing the faulting instruction is unlikely to lead anywhere — the writer is the bug.Both crashes hit while the session was idle
| | 2026-09-20 | 2026-09-21 |
|---|---|---|
| Session span | 08:28:52Z → 09:54:48Z | 2026-09-20 09:55:48Z → 2026-09-20 23:00:24Z (~13 h) |
| Core dumped | 09:56:21Z, ~93 s after last activity | 2026-09-21 00:03:33Z, ~63 min after the last turn |
| Faulting thread | main thread, with JIT frames below the fault | main thread, no JIT frames — straight from
__libc_start_main|Platform note: this is a brand-new OS underneath an unchanged binary
Worth stating explicitly, because it may be the reason the crash rate jumped. This machine was upgraded Mageia 9 → Mageia 10 on 2026-09-19, two days before this crash:
| | before | after |
|---|---|---|
| Kernel | 6.6.141-1.mga9 | 6.18.44-1.mga10 (installed 19 Sep 14:01 BST) |
| glibc | 2.36-59.mga9 | 2.42-10.mga10 (installed 19 Sep 12:28 BST) |
| gcc | — | 15.2.0-1.mga10 |
| Desktop | Plasma 5 / X11 | Plasma 6 / Wayland |
That is six glibc releases (2.36 → 2.42) and twelve kernel releases (6.6 → 6.18) in one step, so malloc, mmap/VMA handling and the page-fault paths under the binary all changed at once.
Crash counts either side of that line, with the Claude Code binary unchanged:
So the new OS did not introduce the bug — it goes back to July across nine releases and at least three bundled Bun versions — but the rate is roughly an order of magnitude higher on it. A new kernel and a new allocator plausibly change the reuse timing that decides whether the errant write lands on a live cell. To be clear, this is glibc 2.42 on kernel 6.18, not the glibc 2.44 seen in the startup-crash reports.
Other tunables, for the record: THP
enabled = always [madvise] never,vm.max_map_count = 65530,vm.overcommit_memory = 0,kernel.randomize_va_space = 2.What would help
Symbolisation is still impossible from outside.
filereports the binary as "not stripped", but all 1234.symtabentries are undefined imports, so no frame resolves. A debug sidecar matchingBuildID[sha1] 83effae1a76aa74ce70326f83068a1242f99094awould let me name the writer rather than describe its footprint. Failing that, an ASAN or debug build of the same release that I could run for a few days would very likely catch the write at its source, given the rate on this machine.Another one, now on 2.1.287: four more releases since the 2.1.283 crash on 2026-09-27, and the crash still happens.
Crash details
versions/2.1.287,BuildID[sha1]=ca2032b38650b44e05b2074617d524c7475c80f0What's different this time: the 09-20 and 09-21 cores faulted on the main thread, inside JSC marking. This one faulted on a worker thread (
start_threadat the root). That suggests the heap corruption described above (two high-entropy 256-byte splats 0x400 apart over live cells) is also being hit by a background/concurrent thread, not only by synchronous GC on main.Faulting thread (binary-relative offsets; frames #2–#5 look like Bun's crash handler re-raising):
Rate: four crashes in 13 days since the OS upgrade (09-20, 09-21, 09-27, 10-02, on 2.1.278 ×2, 2.1.283 and 2.1.287). Before the upgrade it was roughly one every 1–2 weeks.
I've kept the full core (~5 GiB uncompressed) and the matching binary for 2.1.278, 2.1.283 and 2.1.287. I can't symbolise them because the binary's
.symtabhas only undefined imports. If someone at Anthropic can resolve the offsets above against the 2.1.287 build, or wants the core, I'm happy to provide whatever helps.