mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-16 00:51:51 -04:00
Bluetooth: ISO: Fix data-race on iso_pi(sk) in socket and HCI event paths
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: ccf74f2390 ("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>
This commit is contained in:
committed by
Luiz Augusto von Dentz
parent
ca40d48107
commit
f958c7805b
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user