ValveSoftware / ValveSoftware/steam-runtime
sniper: bundled PipeWire 0.3.65 segfaults/deadlocks games on Bluetooth profile switch (fixed upstream in 1.2.2)
Nobody has claimed this yet.
- Dominant language
- Shell
- Stars
- 1.5k
- Forks
- 97
- PR merge metrics
- No merged PRs in 30d
Description
Summary
The sniper runtime bundles pipewire 0.3.65-3+deb12u1~steamrt3.3+bsrt3.1 (upstream 0.3.65, released 2023-01-24). That version contains a known data race on SPA_IO_Buffers between the main thread and the pw-data-loop RT thread during port/format renegotiation, fixed upstream in PipeWire 1.2.2 (2024-07-29).
On a host with a modern PipeWire daemon, a Bluetooth headset switching profile (A2DP 48 kHz stereo <-> HSP/HFP 16 kHz mono) forces a mid-stream format renegotiation. The bundled 0.3.65 client then either SIGSEGVs in libspa-audioconvert.so or deadlocks the game.
In CS2 this reproduces on demand: with the headset connected, open Settings -> Audio and click the audio device selector dropdown. Enumerating devices re-creates the streams and drives the renegotiation straight into the racy window.
Observed with Counter-Strike 2, but this is a runtime-level defect and should affect any sniper game using audio with a Bluetooth headset.
Environment
| Component | Version |
|---|---|
| OS | Fedora 44 |
| Kernel | 7.1.8-200.fc44.x86_64 |
| CPU / GPU | Ryzen 9 5900X / Radeon RX 6700 XT (Mesa 26.1.7) |
| Host PipeWire | 1.6.8 |
| Host WirePlumber | 0.5.14 |
| Host BlueZ | 5.87 |
| sniper | 3.0.20260805.254768, pressure-vessel 0.20260805.0 |
| Bundled PipeWire | 0.3.65-3+deb12u1~steamrt3.3+bsrt3.1 |
From sniper_platform_3.0.20260805.254768/files/manifest.dpkg:
libpipewire-0.3-0:amd64 0.3.65-3+deb12u1~steamrt3.3+bsrt3.1
libpipewire-0.3-modules:amd64 0.3.65-3+deb12u1~steamrt3.3+bsrt3.1
libspa-0.2-modules:amd64 0.3.65-3+deb12u1~steamrt3.3+bsrt3.1
pipewire-bin 0.3.65-3+deb12u1~steamrt3.3+bsrt3.1
The host daemon flags the client as outdated on every stream creation:
pipewire[2300]: mod.client-node: detected old client version 4
Failure mode 1: SIGSEGV in libspa-audioconvert
kernel: pw-data-loop[1044288]: segfault at 21 ip 00007fdd599bba71 sp 00007fdd2c3fa270 \
error 4 in libspa-audioconvert.so[31a71,7fdd5999a000+3f000]
systemd-coredump: Process 1044176 (cs2) of user 1000 dumped core.
#0 0x00007fdd599bba71 n/a (/usr/lib/x86_64-linux-gnu/spa-0.2/audioconvert/libspa-audioconvert.so + 0x31a71)
Note the path /usr/lib/x86_64-linux-gnu/...: inside the pressure-vessel container, i.e. the runtime's bundled SPA plugin, not the host's /usr/lib64/.
objdump -d of the bundled libspa-audioconvert.so at the faulting offset:
31a54: cmp %r15d,0x463c(%r12) ; loop bound (port->blocks)
31a5e: mov 0x18(%r14),%rsi
31a65: lea (%rax,%rax,4),%rdx ; i * 5
31a69: mov 0x10(%rsi),%rax ; -> datas array base
31a6d: lea (%rax,%rdx,8),%rbx ; &datas[i], stride 40 == sizeof(struct spa_data)
31a71: mov 0x20(%rbx),%rax ; <-- FAULTS reading spa_data.chunk
31a75: movl $0x0,(%rax) ; chunk->offset = 0
31a7f: movl $0x0,0x4(%rax) ; chunk->size = 0
This is impl_node_process() in spa/plugins/audioconvert/audioconvert.c:
for (j = 0; j < port->blocks; j++) {
bd = &buf->buf->datas[j];
bd->chunk->offset = 0;
bd->chunk->size = 0;
Fault address 0x21 with error 4 (userspace read of an unmapped page): the datas array base resolved to 1, i.e. a stale/freed buffer touched during renegotiation.
Failure mode 2: deadlock (same trigger)
On a subsequent launch the game hangs instead of crashing:
1044793 cs2 [S] futex_do_wait
1044924 pw-data-loop [S] ep_poll
1044926 pw-data-loop [S] ep_poll
1044930 pw-data-loop [S] ep_poll
Three pw-data-loop threads in a single client, consistent with repeated stream re-creation without teardown. Server side:
pipewire-pulse: mod.protocol-pulse: [] timeout on stream ... channel:0
... channels 0-7 all time out
This also wedges the host audio graph: host-side wpctl status hangs, and the desktop volume applet shows two simultaneously-selected default sinks until wireplumber/pipewire are restarted.
Trigger evidence
kernel: Bluetooth: hci0: SCO packet for unknown connection handle 257
bluetoothd: btd_service_connect() a2dp-source profile connect failed for XX:XX:XX:XX:XX:XX: Device or resource busy
wireplumber: <WpSiStandardLink> link failed: item deactivated before format was set
At crash time the card was stuck in the voice profile, with A2DP not offered at all:
Active Profile: headset-head-unit
off : Off
headset-head-unit-cvsd : Headset Head Unit (HSP/HFP, codec CVSD)
headset-head-unit : Headset Head Unit (HSP/HFP, codec MSBC)
Upstream fix
Upstream issue: https://gitlab.freedesktop.org/pipewire/pipewire/-/work_items/4094 (closed 2024-08-01). Different surface trigger (EasyEffects creating a new output stream), same underlying race.
The defect is not a missing null check at the crash site. It is a race on port->io / SPA_IO_Buffers between the main thread performing format renegotiation and the RT data loop running impl_node_process(). Fix commits, all dated 2024-07-29:
| Commit | First release | Subject |
|---|---|---|
628f292fb877440f9531cc452f81b59ac3471637 |
1.2.2 | audioconvert: set IO_Buffers only when buffers are negotiated |
0d7c20760e56fc60db13f7d972baed4bc0760767 |
1.2.2 | impl-port: improve IO_Buffers management on ports |
61dcd8dedec3c56375e333113156ec7fbd0cd372 |
1.3.81 / 1.4.0 | audioconvert: set IO_Buffers only when buffers are negotiated (mainline) |
The fix routes port->io assignment through the RT data loop, and clears SPA_IO_Buffers on SPA_PARAM_Format change before the port leaves the data-thread schedule, closing exactly the window an A2DP <-> HFP switch hits.
Verified by ancestry check against the upstream repo:
$ git merge-base --is-ancestor 628f292fb 0.3.65 # -> false (0.3.65 predates the fix)
$ git merge-base --is-ancestor 61dcd8ded 1.6.8 # -> true
0.3.65 is dated 2023-01-24, roughly 18 months before the fix landed.
Steps to reproduce
Deterministic path, via CS2's in-game device enumeration:
- Host running a modern PipeWire (1.2.2+) and WirePlumber, with
bluetooth.autoswitch-to-headset-profileat its defaulttrue. - Pair a Bluetooth headset exposing both A2DP and HSP/HFP, and connect it.
- Launch CS2 (sniper runtime).
- Open Settings -> Audio and click the audio device selector dropdown.
- The game segfaults in
libspa-audioconvert.so, or deadlocks with orphanedpw-data-loopthreads.
Opening that dropdown makes the game enumerate and open the available audio devices, which tears down and re-creates streams and drives the port/format renegotiation into the racy window. This is consistent with the three orphaned pw-data-loop threads observed in failure mode 2: repeated stream re-creation without teardown.
The same crash also occurs without touching the menu, whenever the headset microphone is acquired and the profile autoswitches A2DP -> HSP/HFP mid-stream. The dropdown is simply the reliable way to trigger it on demand.
Workaround
Prevent the renegotiation entirely by disabling profile autoswitching on the host:
wpctl settings --save bluetooth.autoswitch-to-headset-profile false
Then reconnect the headset so it returns to a2dp-sink. This costs the headset microphone, since HFP is what was disabled, so it is not viable for players who need headset voice chat.
Request
Backport a current PipeWire into sniper. The steamrt README's backporting policy already names PipeWire as suitable for this (stable SONAME libpipewire-0.3.so.0), and recent sniper releases have carried updates for SDL, Vulkan-Loader, nss and others while PipeWire has stayed at the 0.3.65 baseline.
Possibly related, never root-caused: https://github.com/ValveSoftware/csgo-osx-linux/issues/3349 ("Game Freeze when bluetooth headset is connected", closed 2023), which matches failure mode 2 and was closed after the reporter removed PipeWire entirely.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the steamrt README's backporting policy and the bundled package versions in sniper_platform_3.0.20260805.254768/files/manifest.dpkg. Review PipeWire fixes 628f292fb877440f9531cc452f81b59ac3471637 and 0d7c20760e56fc60db13f7d972baed4bc0760767, then trace the runtime's package update process. Done means the bundled PipeWire includes the 1.2.2 fix while retaining the stable SONAME, and the Bluetooth profile-switch reproduction no longer crashes or deadlocks.
Written by the indexing model from the issue text.
Assessment
- Domain
- audio-video-rtc
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 55/100