I thought it may be useful to add: it does also happen on Arch distro. So there's chance it's not distro-dependent. Although in may case it just restarts after crash like nothing happened and then works extremely stable. So it may be a slightly different problem. Here's small part of a log that i thought was somewhat relevant, including crash dump. (it was pretty much only thing that caught my eye)
In general Steam is acting weird lately, can even cause CPU soft lockup sometimes, but that's completely different topic.
Environment: Arch (rolling) + Hyprland, Steam runs as an X11 client under XWayland. NVIDIA RTX 5070 Ti, proprietary 615.78.08. Native install (not Flatpak). Client 1792618285, and publicbeta 1792711043. Mouse: 8 kHz polling (USB bInterval=1).
One function calls itself until the thread stack is exhausted. coredumpctl truncates the trace; measuring the faulting thread's stack in an extracted core gives:
| measurement | value |
|---|---|
| repeated frames of the same return address | 18 664 |
| bytes per frame | 448 |
| stack consumed by the recursion | 0x7f9440 = 7 MiB (8 MiB thread stack) |
#1 libcef.so + 0x67cae43 <- innermost libcef frame: the function that calls realpath()
#2 libcef.so + 0x7cab84a
#3..#N libcef.so + 0x7cabe3b <- repeated 18 664 times
Recursive function start libcef.so+0x7cab460, recursive call site libcef.so+0x7cabe3b. Disassembly of the call site is a walk over a container of 24-byte elements that calls the same function per element until one returns non-null:
7cabe20: cmp %r14,%rbx ; end of container
7cabe23: je 7cabe55
7cabe2c: mov -0xc8(%rbp),%rsi ; context
7cabe33: mov %rbx,%rdi
7cabe36: call 7cab460 ; <- recursion, return address 0x7cabe3b
7cabe3b: test %rax,%rax
7cabe3e: jne 7cabe55
7cabe40: add $0x18,%rbx ; sizeof(element) == 24
7cabe4e: jne 7cabe33
Deepest recovered frames sit in the message-loop / thread-bootstrap range (0x67b7210, 0x67b72a3, 0x679e264, 0x67a59bc, 0x675b2ec). The faulting thread is a CEF ThreadPoolForeg worker (TID 107786), not the browser thread; I'm not claiming to know which handler chain recurses, only that the recursion and its depth are exactly reproducible:
Stack trace of thread 107786: (coredumpctl -1 info steamwebhelper)
#0 /run/host/usr/lib/libc.so.6 + 0x3fea0 -> realpath (addr2line; .dynsym nearest preceding realpath@@GLIBC_2.3 @0x3fe60)
#1 libcef.so + 0x67cae43
#2 libcef.so + 0x7cab84a
#3..[#18666](/issue/ValveSoftware/steam-for-linux/18666) libcef.so + 0x7cabe3b
Signal: 11 (SEGV) si_code: SEGV_MAPERR
Frame #0 being inside glibc's realpath, with SEGV_MAPERR, is consistent with that call trying to extend a stack that the recursion has already filled — i.e. the recursion bottomed out in a path-resolution call.
MotionNotify events, not their rateReproduced with XTEST-injected motion, so it bypasses evdev, the kernel input layer and any pointer-acceleration driver — no real device is involved. Steam window focused, coordinates inside it:
| injected motion events | result |
|---|---|
| 0 (idle 90 s) | stable, helper PID unchanged |
| 625 events @ 125 Hz | no crash |
| 48 000 events @ 8 kHz (6 s) | SIGSEGV, offsets above |
| 240 000 events @ 8 kHz (30 s, beta) | 2 crash-restarts |
| 60 000 events @ 1 kHz (60 s, beta) | 7 crash-restarts |
~18.6k events is the budget, at any delivery rate. The 1 kHz row is why the crash becomes a loop rather than a one-off: after the watchdog respawns the helper, the events already queued in the X socket hit it in one burst and it dies again within seconds — which matches the -startcount= incrementing every ~10 s.
#!/usr/bin/env python3
# flood.py <rate_hz> <seconds> <x0> <y0> <x1> <y1> (needs python3-xlib)
import sys, time
from Xlib import X, display
from Xlib.ext import xtest
hz, secs = float(sys.argv[1]), float(sys.argv[2])
x0, y0, x1, y1 = map(int, sys.argv[3:7])
d = display.Display(':1')
n, batch, sent, i = int(hz*secs), max(1, int(hz/500)), 0, 0
t0 = time.time()
while sent < n:
for _ in range(batch):
xtest.fake_input(d, X.MotionNotify, x=x0+((i*37)%(x1-x0)), y=y0+((i*53)%(y1-y0)))
i += 1; sent += 1
d.sync()
t = t0 + sent/hz
if t > time.time(): time.sleep(t-time.time())
d.sync()
libcef.so, opposite crash rate)1791918634 (Oct 21 → Oct 24): 1 steamwebhelper core in 3 days, with this same 8 kHz mouse attached the whole time.1792618285 at 12:42; first SIGSEGV 12:43:22; 19 more in 12 minutes.So the engine binary did not change between those two builds — the client's event dispatch around it did.
libcef.so is byte-identical on release 1792618285 and publicbeta 1792711043: 219 444 168 bytes, mtime Aug 15, md5 b001c1ca15dcad948b7081cd8bd01d1d. Same recursion offsets in beta cores.
GPU compositing on and off for web views (GPUAccelWebViewsV3=0 → helper really does get --disable-gpu --disable-gpu-compositing, vs =1 with GPU compositing active) — identical either way; X cursor theme; a kernel pointer-acceleration module (the XTEST repro never touches kernel input); second, rotated monitor; -cef-disable-gpu; verification / reinstall (Steam restores ubuntu12_64/steamwebhelper_sniper_wrap.sh on launch, so wrapper-level flag edits don't survive).
The core itself is 13.7 MB (/var/lib/systemd/coredump/core.steamwebhelper.1000.97485d4d151b404c9dcb914435a6ec89.107773.1792844064000000.zst) — over GitHub's issue-attachment size limit, so it is not attached; say the word and I'll put it somewhere you can pull it from.
Is a bundled Chromium/CEF refresh pending that addresses re-entrant X11 event dispatch, or is a stack guard / motion-event cap something that can be turned on client-side?
Motion is injected with XTEST at a fixed rate — 8 kHz x 30 s = 240 000 MotionNotify events — and only the hovered content varies:
| pointer region | SIGSEGV count |
|---|---|
| library hero / "What's New" animated tiles | 3 |
| collection sidebar (static game list) | 0 |
| idle, cursor off the window | 0 |
Identical event count and rate, different pixel: 3 vs 0. So this is not an event-count budget. My earlier comment's 18 664 identical frames x 448 B = 7 MiB of an 8 MiB thread stack is the recursion depth at the crash, not a quota of events that gets spent.
Also tested: raising the process stack limit does not help. With Max stack size 67108864 in steamwebhelper's limits, the same 240 000 events gave 3 crashes with the same trace (realpath in libc at the innermost frame, libcef+0x67cae43 then repeated libcef+0x7cabe3b). The recursion looks unbounded, so the depth comes from whatever is being hit-tested rather than from a chain that grows over the process lifetime.
For the same reason, throttling the rate reaching the helper does not prevent it either: a compositor-side motion cap (~250 Hz while Steam is focused, all other clients untouched) still gave 5 SIGSEGVs in about 2 minutes of ordinary library browsing.
I don't have much to add, just another corroboration: Arch + Hyprland, steam client stable and beta. steamwebhelper dumps core in a loop, ~2-3s apart. coredumpctl reports Signal: 11 (SEGV) si_code: SEGV_MAPERR, with the entire stack inside libcef.so and the same offsets (+0x673f6be, +0x677f721, +0x6714ee7) recurring across threads.
Nothing extracted yet.
Steam client won't stay open. The tray icon runs fine (I can use it to quit Steam), but the main library/store window never opens or stays open. steamwebhelper crashes and restarts in a loop, confirmed via cef_log.txt showing startcount incrementing every ~10 seconds. Big Picture mode also crashes immediately.
Confirmed via coredumpctl: steamwebhelper crashes with SIGSEGV, with the crash occurring inside libcef.so specifically (the same crash address is repeated across the entire stack trace, suggesting a stack overflow/infinite recursion inside CEF).
This happens identically across two completely separate install methods:
Both show the same SIGSEGV crash in libcef.so.
Things already tried, none of which fixed it:
System info:
Since this crashes identically on two separate install methods with different bundled libraries, this seems like a real bug rather than a local config issue. Happy to provide any additional logs or test any suggested fixes.