From: Sasha Levin <sashal@kernel.org>
To: stable@vger.kernel.org
Cc: SeungJu Cheon <suunj1331@gmail.com>,
Luiz Augusto von Dentz <luiz.von.dentz@intel.com>,
Sasha Levin <sashal@kernel.org>
Subject: [PATCH 6.12.y 3/4] Bluetooth: ISO: Fix data-race on iso_pi(sk) in socket and HCI event paths
Date: Wed, 30 Sep 2026 11:31:15 -0400 [thread overview]
Message-ID: <20260930153116.2781860-3-sashal@kernel.org> (raw)
In-Reply-To: <20260930153116.2781860-1-sashal@kernel.org>
From: SeungJu Cheon <suunj1331@gmail.com>
[ Upstream commit f958c7805b18e9d69f6b322b231ecee46ec6f331 ]
Several iso_pi(sk) fields (qos, qos_user_set, bc_sid, base, base_len,
sync_handle, bc_num_bis) are written under lock_sock in
iso_sock_setsockopt() and iso_sock_bind(), but read and written under
hci_dev_lock only in two other paths:
- iso_connect_bis() / iso_connect_cis(), invoked from connect(2),
read qos/base/bc_sid and reset qos to default_qos on the
qos_user_set validation failure -- all without lock_sock.
- iso_connect_ind(), invoked from hci_rx_work, writes sync_handle,
bc_sid, qos.bcast.encryption, bc_num_bis, base and base_len on
PA_SYNC_ESTABLISHED / PAST_RECEIVED / BIG_INFO_ADV_REPORT /
PER_ADV_REPORT events. The BIG_INFO handler additionally passes
&iso_pi(sk)->qos together with sync_handle / bc_num_bis / bc_bis
to hci_conn_big_create_sync() while setsockopt may be mutating
them.
Acquire lock_sock around the affected accesses in both paths.
The locking order hci_dev_lock -> lock_sock matches the existing
iso_conn_big_sync() precedent, whose comment documents the same
requirement for hci_conn_big_create_sync(). The HCI connect/bind
helpers do not wait for command completion -- they enqueue work via
hci_cmd_sync_queue{,_once}() / hci_le_create_cis_pending() and
return -- so the added hold time is comparable to iso_conn_big_sync().
KCSAN report:
BUG: KCSAN: data-race in iso_connect_cis / iso_sock_setsockopt
read to 0xffffa3ae8ce3cdc8 of 1 bytes by task 335 on cpu 0:
iso_connect_cis+0x49f/0xa20
iso_sock_connect+0x60e/0xb40
__sys_connect_file+0xbd/0xe0
__sys_connect+0xe0/0x110
__x64_sys_connect+0x40/0x50
x64_sys_call+0xcad/0x1c60
do_syscall_64+0x133/0x590
entry_SYSCALL_64_after_hwframe+0x77/0x7f
write to 0xffffa3ae8ce3cdc8 of 60 bytes by task 334 on cpu 1:
iso_sock_setsockopt+0x69a/0x930
do_sock_setsockopt+0xc3/0x170
__sys_setsockopt+0xd1/0x130
__x64_sys_setsockopt+0x64/0x80
x64_sys_call+0x1547/0x1c60
do_syscall_64+0x133/0x590
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Reported by Kernel Concurrency Sanitizer on:
CPU: 1 UID: 0 PID: 334 Comm: iso_setup_race Not tainted 7.0.0-10949-g8541d8f725c6 #44 PREEMPT(lazy)
The iso_connect_ind() races were found by inspection.
Fixes: ccf74f2390d6 ("Bluetooth: Add BTPROTO_ISO socket type")
Signed-off-by: SeungJu Cheon <suunj1331@gmail.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Stable adaptation for 6.12:
- Omit the PAST_RECEIVED locking hunk: this tree has neither that event
path nor iso_match_sid_past(). Keep locking for the existing connect,
PA sync, BIG info, and periodic advertising paths.
- Fold in the connection lifetime prerequisites from aa9f7cb2bd3a2 and
fdfde532ab1ca. Give each socket association its own iso_conn reference,
balance temporary references at all iso_conn_add() callers, and detach
hcon explicitly during teardown. Use kref_get() directly instead of
adding the upstream iso_conn_hold() helper.
- Fold in e06d549fcd4a0 so hci_bind_cis() always returns an owned hold and
hci_connect_cis() preserves it when reusing an existing link. Without
this, the target's unused-hold drops could release an unowned hold.
These prerequisites let 0fcd4dad555c96e0bd3a1b8c569f989be85c7341 apply
unchanged with the reference ownership it expects. Its seven-line fix
is intentionally left for the target commit.
Stable-dep-of: 0fcd4dad555c ("Bluetooth: ISO: release unused CIS holds after channel attach")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
net/bluetooth/hci_conn.c | 9 +++-
net/bluetooth/iso.c | 96 ++++++++++++++++++++++++----------------
2 files changed, 66 insertions(+), 39 deletions(-)
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index d2ad9a5dd1ac7..e1a02a05ce272 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -1937,6 +1937,8 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
cis->iso_qos.ucast.cis = BT_ISO_QOS_CIS_UNSET;
}
+ hci_conn_hold(cis);
+
if (cis->state == BT_CONNECTED)
return cis;
@@ -1978,7 +1980,6 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
return ERR_PTR(-EINVAL);
}
- hci_conn_hold(cis);
cis->state = BT_BOUND;
return cis;
@@ -2341,6 +2342,12 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
return cis;
}
+ /* The existing link already owns the hold on its parent. */
+ if (cis->link) {
+ hci_conn_drop(le);
+ return cis;
+ }
+
link = hci_conn_link(le, cis);
hci_conn_drop(cis);
if (!link) {
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
index b2a4cb12b6b70..1674e8bc95a93 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -106,9 +106,6 @@ static void iso_conn_free(struct kref *ref)
BT_DBG("conn %p", conn);
- if (conn->sk)
- iso_pi(conn->sk)->conn = NULL;
-
if (conn->hcon) {
conn->hcon->iso_data = NULL;
if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags))
@@ -208,7 +205,6 @@ static struct iso_conn *iso_conn_add(struct hci_conn *hcon)
conn->hcon = hcon;
iso_conn_unlock(conn);
}
- iso_conn_put(conn);
return conn;
}
@@ -261,6 +257,7 @@ static void iso_chan_del(struct sock *sk, int err)
}
static void iso_conn_del(struct hci_conn *hcon, int err)
+ __must_hold(&hcon->hdev->lock)
{
struct iso_conn *conn = hcon->iso_data;
struct sock *sk;
@@ -275,12 +272,9 @@ static void iso_conn_del(struct hci_conn *hcon, int err)
iso_conn_lock(conn);
sk = iso_sock_hold(conn);
iso_conn_unlock(conn);
- iso_conn_put(conn);
- if (!sk) {
- iso_conn_put(conn);
- return;
- }
+ if (!sk)
+ goto done;
iso_sock_disable_timer(sk);
@@ -289,6 +283,15 @@ static void iso_conn_del(struct hci_conn *hcon, int err)
release_sock(sk);
iso_sock_kill(sk);
sock_put(sk);
+
+done:
+ /* No sk access to conn->hcon any more (lock_sock + hdev->lock) */
+ iso_conn_lock(conn);
+ conn->hcon = NULL;
+ hcon->iso_data = NULL;
+ iso_conn_unlock(conn);
+
+ iso_conn_put(conn);
}
static int __iso_chan_add(struct iso_conn *conn, struct sock *sk,
@@ -304,6 +307,12 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk,
return -EBUSY;
}
+ if (!conn->hcon) {
+ BT_ERR("conn->hcon missing");
+ return -EIO;
+ }
+
+ kref_get(&conn->ref);
iso_pi(sk)->conn = conn;
conn->sk = sk;
clear_bit(ISO_CONN_DROPPED, conn->flags);
@@ -357,6 +366,7 @@ static int iso_connect_bis(struct sock *sk)
return -EHOSTUNREACH;
hci_dev_lock(hdev);
+ lock_sock(sk);
if (!bis_capable(hdev)) {
err = -EOPNOTSUPP;
@@ -407,13 +417,10 @@ static int iso_connect_bis(struct sock *sk)
goto unlock;
}
- lock_sock(sk);
-
err = iso_chan_add(conn, sk, NULL);
- if (err) {
- release_sock(sk);
+ iso_conn_put(conn);
+ if (err)
goto unlock;
- }
/* Update source addr of the socket */
bacpy(&iso_pi(sk)->src, &hcon->src);
@@ -429,9 +436,8 @@ static int iso_connect_bis(struct sock *sk)
iso_sock_set_timer(sk, sk->sk_sndtimeo);
}
- release_sock(sk);
-
unlock:
+ release_sock(sk);
hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
@@ -459,6 +465,7 @@ static int iso_connect_cis(struct sock *sk)
return -EHOSTUNREACH;
hci_dev_lock(hdev);
+ lock_sock(sk);
if (!cis_central_capable(hdev)) {
err = -EOPNOTSUPP;
@@ -504,13 +511,10 @@ static int iso_connect_cis(struct sock *sk)
goto unlock;
}
- lock_sock(sk);
-
err = iso_chan_add(conn, sk, NULL);
- if (err) {
- release_sock(sk);
+ iso_conn_put(conn);
+ if (err)
goto unlock;
- }
/* Update source addr of the socket */
bacpy(&iso_pi(sk)->src, &hcon->src);
@@ -526,9 +530,8 @@ static int iso_connect_cis(struct sock *sk)
iso_sock_set_timer(sk, sk->sk_sndtimeo);
}
- release_sock(sk);
-
unlock:
+ release_sock(sk);
hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
@@ -1206,10 +1209,9 @@ static int iso_listen_bis(struct sock *sk)
}
err = iso_chan_add(conn, sk, NULL);
- if (err) {
- hci_conn_drop(hcon);
+ iso_conn_put(conn);
+ if (err)
goto unlock;
- }
unlock:
release_sock(sk);
@@ -2133,8 +2135,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
sk = iso_get_sock(&hdev->bdaddr, bdaddr, BT_LISTEN,
iso_match_sid, ev1);
if (sk && !ev1->status) {
+ lock_sock(sk);
iso_pi(sk)->sync_handle = le16_to_cpu(ev1->handle);
iso_pi(sk)->bc_sid = ev1->sid;
+ release_sock(sk);
}
goto done;
@@ -2161,27 +2165,35 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
ev2);
if (sk) {
- int err;
- struct hci_conn *hcon = iso_pi(sk)->conn->hcon;
+ int err = 0;
+ bool big_sync;
+ struct hci_conn *hcon;
+ lock_sock(sk);
+
+ hcon = iso_pi(sk)->conn->hcon;
iso_pi(sk)->qos.bcast.encryption = ev2->encryption;
if (ev2->num_bis < iso_pi(sk)->bc_num_bis)
iso_pi(sk)->bc_num_bis = ev2->num_bis;
- if (!test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags) &&
- !test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) {
+ big_sync = !test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags) &&
+ !test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags);
+
+ if (big_sync)
err = hci_conn_big_create_sync(hdev, hcon,
&iso_pi(sk)->qos,
iso_pi(sk)->sync_handle,
iso_pi(sk)->bc_num_bis,
iso_pi(sk)->bc_bis);
- if (err) {
- bt_dev_err(hdev, "hci_le_big_create_sync: %d",
- err);
- sock_put(sk);
- sk = NULL;
- }
+
+ release_sock(sk);
+
+ if (big_sync && err) {
+ bt_dev_err(hdev, "hci_le_big_create_sync: %d",
+ err);
+ sock_put(sk);
+ sk = NULL;
}
}
@@ -2235,8 +2247,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
if (!base || base_len > BASE_MAX_LENGTH)
goto done;
+ lock_sock(sk);
memcpy(iso_pi(sk)->base, base, base_len);
iso_pi(sk)->base_len = base_len;
+ release_sock(sk);
} else {
/* This is a PA data fragment. Keep pa_data_len set to 0
* until all data has been reassembled.
@@ -2261,6 +2275,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
}
static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
+ __must_hold(&hcon->hdev->lock)
{
if (hcon->type != CIS_LINK && hcon->type != BIS_LINK &&
hcon->type != PA_LINK) {
@@ -2272,8 +2287,10 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
struct hci_link *link, *t;
list_for_each_entry_safe(link, t, &hcon->link_list,
- list)
+ list) {
+ lockdep_assert_held(&link->conn->hdev->lock);
iso_conn_del(link->conn, bt_to_errno(status));
+ }
return;
}
@@ -2295,14 +2312,17 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
struct iso_conn *conn;
conn = iso_conn_add(hcon);
- if (conn)
+ if (conn) {
iso_conn_ready(conn);
+ iso_conn_put(conn);
+ }
} else {
iso_conn_del(hcon, bt_to_errno(status));
}
}
static void iso_disconn_cfm(struct hci_conn *hcon, __u8 reason)
+ __must_hold(&hcon->hdev->lock)
{
if (hcon->type != CIS_LINK && hcon->type != BIS_LINK &&
hcon->type != PA_LINK)
--
2.53.0
next prev parent reply other threads:[~2026-09-30 15:31 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-29 11:49 FAILED: patch "[PATCH] Bluetooth: ISO: release unused CIS holds after channel attach" failed to apply to 6.12-stable tree gregkh
2026-09-30 15:31 ` [PATCH 6.12.y 1/4] Bluetooth: Fix spelling mistakes Sasha Levin
2026-09-30 15:31 ` [PATCH 6.12.y 2/4] Bluetooth: hci_core: Fix using {cis,bis}_capable for current settings Sasha Levin
2026-09-30 15:31 ` Sasha Levin [this message]
2026-09-30 15:31 ` [PATCH 6.12.y 4/4] Bluetooth: ISO: release unused CIS holds after channel attach Sasha Levin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260930153116.2781860-3-sashal@kernel.org \
--to=sashal@kernel.org \
--cc=luiz.von.dentz@intel.com \
--cc=stable@vger.kernel.org \
--cc=suunj1331@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox