Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH 0/3] Bluetooth: hci_conn: fix SCO connection reuse
@ 2026-10-27 16:50 Iaroslav Voitovych
  2026-10-27 16:50 ` [PATCH 1/3] Bluetooth: hci_conn: hold a reused SCO connection for its caller Iaroslav Voitovych
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Iaroslav Voitovych @ 2026-10-27 16:50 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: Marcel Holtmann, Luiz Augusto von Dentz, Cen Zhang,
	Aldo Ariel Panzardo, Hitalo Souza, Pauli Virtanen

An HFP codec change can leave a headset silent. The new SCO socket
reports a successful connect(), but no new SCO link is established.

PipeWire closes the old SCO socket and immediately connects another
to the same headset. If the headset replies to AT+BCS before the old
link's Disconnection Complete arrives, hci_connect_sco() reuses the
connection being torn down.

That connection remains BT_CONNECTED while its Disconnect is pending.
No Setup Synchronous Connection is sent, yet the new socket reports
success. When the old link's Disconnection Complete arrives, the new
socket is closed with ECONNABORTED.

This series fixes three related problems.

Patch 1 corrects hold ownership when reusing a SCO connection whose
link is still up. A second connect() correctly returns EBUSY, but
also drops the first socket's hold and disconnects its working link.
Give the second caller its own hold and release the extra ACL hold,
following the CIS fix e06d549fcd4a. EBUSY remains the result, but
the existing link survives.

Patch 2 prevents reuse of a connection already committed to
teardown. If abort_reason is set, return -ECONNABORTED immediately
instead of handing the dying connection to a new socket.

Patch 3 closes the remaining race before abort_reason is set. A
running hci_conn_timeout() may already have observed that no holder
remains when a new connect() takes a hold. Wait for that worker and
check abort_reason again before using the connection.

A connect() arriving before the worker starts cancels the pending
disconnect with the hold patch 1 gives it, and keeps the live link.
Patches 2 and 3 leave that case unchanged.

Testing

The series was tested on bluetooth-next 27731ba09195 in QEMU with
BlueZ's emulator, on uniprocessor, SMP and KCSAN kernels, with
lockdep enabled. The accompanying BlueZ patch adds three sco-tester
cases:

  "Reconnect After Disconnect"
  "Reconnect During Disconnect"
  "Connect Twice"

"Reconnect During Disconnect" and "Connect Twice" fail without the
series on the uniprocessor and SMP kernels, and pass with it in three
runs each on the uniprocessor, SMP and KCSAN kernels. "Reconnect
After Disconnect" passes with and without the series. In the full
sco-tester, iso-tester, l2cap-tester and mgmt-tester runs, the only
other differences were two mgmt-tester cases that timed out
intermittently on SMP. Neither involves SCO, and one also timed out
without the series.

Patch 3's race was reproduced by a debug-only change that pauses
hci_conn_timeout() after it observes no holder; the change widens
the window and is not part of this series. Both host-initiated and
remote-initiated SCO links were tested. With patches 1 and 2 alone,
the new socket is still reported connected and then aborted. With
patch 3, connect() returns -ECONNABORTED.

The series also applies unchanged to bluetooth-next 47d4d9f67e92,
the base of this posting. On that base, sco-tester passes all 33
cases with the series; without it, only "Reconnect During
Disconnect" and "Connect Twice" fail. The other testers were not
rerun on this base.

Hardware testing

The series was also run on the QCA9377 laptop where the bug was
found, with PipeWire 1.0.5 and three headsets. The laptop ran
Ubuntu 24.04's 7.0.0-38 kernel with its Bluetooth module rebuilt
with abd93c85c8667, 816fb1590a4c and this series; the two
prerequisites are explained below.

Over more than a day and a half of ordinary use, from 23 to 25
October, three codec changes on earbuds that reply to AT+BCS before
the old link's Disconnection Complete returned ECONNABORTED instead
of reporting a connected socket without a link. On two other
headsets, which reply after that event, all eight codec changes
established a new link.

All 36 SCO links in the HCI capture of those days ended with a
successful Disconnection Complete for their handle, and no HCI
command timeout was logged. A search of the boot's kernel log found
no WARNING, BUG, Oops, Call Trace, refcount or use-after-free
report. Its only error-level lines are "SCO packet for unknown
connection handle" and "corrupted SCO packet", which this
controller also logs without the series.

Patch 1's case was tested separately on 26 October. While a
headset's eSCO link carried audio, three attempts to connect a
second SCO socket to the same headset each returned EBUSY at once.
The HCI capture shows no new setup or disconnect during the
attempts, and SCO packets kept flowing in both directions.

Patch 3's race was not reproduced on hardware; it is covered by the
emulator test described above.

Remaining codec-switch delay

The series stops connect() from succeeding on a connection already
being torn down. It does not, by itself, make PipeWire's codec
change seamless.

PipeWire 1.0.5 retries ECONNABORTED at once, while the old link is
still being disconnected, so those retries fail too. With a stream
playing on the laptop, audio returned about five seconds later,
when WirePlumber suspended the failed nodes and the next connect
established a new link.

In an earlier reproduction without the series, on Ubuntu 7.0.0-34,
the same codec change produced no Setup Synchronous Connection, and
sounds started 2 to 26 seconds later were still silent. That was a
historical observation, not a same-build control.

A separate prototype defers the new connect until the old link has
been torn down. It lets the retry succeed in the emulator tests but
has not been run on hardware. It builds on this series and changes
connect() semantics, so I can post it as an RFC if that approach is
preferred.

Related work

The series is independent of Hitalo Souza's pending SCO setup
series and applies with or without it:
https://patchwork.kernel.org/project/bluetooth/list/?series=1181908

Jeremy Dean Clifton's pending "hci_core: Fix queuing disc_work while
workqueue is drained" (syzbot's "WARNING in hci_connect_sco") is on
the same path but does not overlap: it changes hci_conn_drop() in
hci_core.h.

Stable and downstream kernels

For stable, patch 1 applies alone on 6.6.y and 6.12.y, which have
the 8a5b0135d4a5 backport and the reuse path but not e6720779ae61.
Patches 2 and 3 apply on top of it on 6.18.y and 7.2.y. Each of
those heads was built and run with sco-tester without and with the
patches it needs: the cases for its bugs fail without them and pass
with them. 6.1.y has no reuse path, hence the "# 6.6.x" on patch 1.

Trees with e6720779ae61 but without 816fb1590a4c, such as Ubuntu
24.04's 7.0 kernel, need these two commits first:

  abd93c85c8667 ("Bluetooth: SCO: give the socket its own sco_conn
                 reference")
  816fb1590a4c ("Bluetooth: SCO: serialise sco_conn lifetime against
                sco_recv_scodata()")

Without them, sco_connect() does not put the sco_conn reference that
sco_conn_add() took when sco_chan_add() fails with EBUSY, and that
reference holds the hci_conn: once patch 1 stops the disconnect, the
SCO link stays up after both sockets are closed. On Ubuntu
7.0.0-38's Bluetooth sources with those two commits, "Reconnect
During Disconnect" and "Connect Twice" fail without the series and
pass with it, uniprocessor and SMP.

Iaroslav Voitovych (3):
  Bluetooth: hci_conn: hold a reused SCO connection for its caller
  Bluetooth: hci_conn: don't reuse an aborting SCO connection
  Bluetooth: hci_conn: synchronize with SCO disconnect work before reuse

 net/bluetooth/hci_conn.c | 36 ++++++++++++++++++++++++++++++++----
 1 file changed, 32 insertions(+), 4 deletions(-)


base-commit: 47d4d9f67e92d56ef3965f2727f9c98a5a611368
-- 
2.43.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-10-27 21:24 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-27 16:50 [PATCH 0/3] Bluetooth: hci_conn: fix SCO connection reuse Iaroslav Voitovych
2026-10-27 16:50 ` [PATCH 1/3] Bluetooth: hci_conn: hold a reused SCO connection for its caller Iaroslav Voitovych
2026-10-27 21:24   ` Bluetooth: hci_conn: fix SCO connection reuse bluez.test.bot
2026-10-27 16:50 ` [PATCH 2/3] Bluetooth: hci_conn: don't reuse an aborting SCO connection Iaroslav Voitovych
2026-10-27 16:50 ` [PATCH 3/3] Bluetooth: hci_conn: synchronize with SCO disconnect work before reuse Iaroslav Voitovych

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox