]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
Bluetooth: ISO: Fix data-race on iso_pi(sk) in socket and HCI event paths
authorSeungJu Cheon <suunj1331@gmail.com>
Tue, 21 Apr 2026 02:51:22 +0000 (11:51 +0900)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Wed, 6 May 2026 20:22:05 +0000 (16:22 -0400)
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>
net/bluetooth/iso.c

index 290a1b9a9daa94d42eebdd3591c7ed0962ea2b6b..7cb2864fe872411464edfb574265537958be84c7 100644 (file)
@@ -347,6 +347,7 @@ static int iso_connect_bis(struct sock *sk)
                return -EHOSTUNREACH;
 
        hci_dev_lock(hdev);
+       lock_sock(sk);
 
        if (!bis_capable(hdev)) {
                err = -EOPNOTSUPP;
@@ -399,13 +400,9 @@ 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);
+       if (err)
                goto unlock;
-       }
 
        /* Update source addr of the socket */
        bacpy(&iso_pi(sk)->src, &hcon->src);
@@ -421,9 +418,8 @@ static int iso_connect_bis(struct sock *sk)
                iso_sock_set_timer(sk, READ_ONCE(sk->sk_sndtimeo));
        }
 
-       release_sock(sk);
-
 unlock:
+       release_sock(sk);
        hci_dev_unlock(hdev);
        hci_dev_put(hdev);
        return err;
@@ -444,6 +440,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;
@@ -498,13 +495,9 @@ 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);
+       if (err)
                goto unlock;
-       }
 
        /* Update source addr of the socket */
        bacpy(&iso_pi(sk)->src, &hcon->src);
@@ -520,9 +513,8 @@ static int iso_connect_cis(struct sock *sk)
                iso_sock_set_timer(sk, READ_ONCE(sk->sk_sndtimeo));
        }
 
-       release_sock(sk);
-
 unlock:
+       release_sock(sk);
        hci_dev_unlock(hdev);
        hci_dev_put(hdev);
        return err;
@@ -2256,8 +2248,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
                sk = iso_get_sock(hdev, &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;
@@ -2268,8 +2262,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
                sk = iso_get_sock(hdev, &hdev->bdaddr, bdaddr, BT_LISTEN,
                                  iso_match_sid_past, ev1a);
                if (sk && !ev1a->status) {
+                       lock_sock(sk);
                        iso_pi(sk)->sync_handle = le16_to_cpu(ev1a->sync_handle);
                        iso_pi(sk)->bc_sid = ev1a->sid;
+                       release_sock(sk);
                }
 
                goto done;
@@ -2296,27 +2292,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;
                        }
                }
 
@@ -2370,8 +2374,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.