[PATCH v3 2/6] Bluetooth: hci_conn: Don't set up a SCO link that is already up
From: Hitalo Souza
Date: Thu Oct 08 2026 - 18:27:58 EST
Since commit a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for
aborting connections"), a SCO/eSCO setup abandoned while pending can
complete after a new connection to the same device was made, and it is
then matched to that connection by address. The new connection's own
setup may not have been sent yet at that point: Enhanced Setup
Synchronous Connection is sent later from the cmd_sync queue, and any
setup is deferred while the ACL leaves sniff mode. When it runs, it
sets the connection back to BT_CONNECT and sends a second setup, which
the controller rejects since a link already exists. The connection that
is up is then failed by the rejection, or left in BT_CONNECT and later
deleted without a Disconnect when its socket is closed.
This is the order of events in the report: the second Enhanced Setup
Synchronous Connection was sent after the first setup had completed,
and was rejected with Invalid HCI Command Parameters.
Don't send a setup for a connection that already has a handle. In
hci_enhanced_setup_sync(), take hdev->lock before checking that, and
check under it that the connection still exists, as
configure_datapath_sync() runs without the lock.
Closes: https://github.com/bluez/bluez/issues/2562
Fixes: a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for aborting connections")
Assisted-by: LLM
Signed-off-by: Hitalo Souza <enghitalo@xxxxxxxxx>
---
net/bluetooth/hci_conn.c | 24 ++++++++++++++++++++++--
1 file changed, 22 insertions(+), 2 deletions(-)
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 29da3fe2b..399c7db77 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -290,6 +290,22 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data)
configure_datapath_sync(hdev, &conn->codec);
+ hci_dev_lock(hdev);
+
+ /* configure_datapath_sync() runs without the lock */
+ if (!hci_conn_valid(hdev, conn)) {
+ hci_dev_unlock(hdev);
+ return -ECANCELED;
+ }
+
+ /* The link may have come up while this was queued, see
+ * hci_sco_setup().
+ */
+ if (!HCI_CONN_HANDLE_UNSET(conn->handle)) {
+ hci_dev_unlock(hdev);
+ return 0;
+ }
+
conn->state = BT_CONNECT;
conn->out = true;
@@ -302,8 +318,6 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data)
cp.tx_bandwidth = cpu_to_le32(0x00001f40);
cp.rx_bandwidth = cpu_to_le32(0x00001f40);
- hci_dev_lock(hdev);
-
switch (conn->codec.id) {
case BT_CODEC_MSBC:
if (!find_next_esco_param(conn, esco_param_msbc,
@@ -625,6 +639,12 @@ void hci_sco_setup(struct hci_conn *conn, __u8 status)
if (!link || !link->conn)
return;
+ /* The link may already be up: a setup abandoned while pending can
+ * complete after a new connection was made and be matched to it.
+ */
+ if (!HCI_CONN_HANDLE_UNSET(link->conn->handle))
+ return;
+
BT_DBG("hcon %p", conn);
if (!status) {
--
2.55.0