Commit f958c780 authored by SeungJu Cheon's avatar SeungJu Cheon Committed by Luiz Augusto von Dentz
Browse files

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: ccf74f23 ("Bluetooth: Add BTPROTO_ISO socket type")
Signed-off-by: default avatarSeungJu Cheon <suunj1331@gmail.com>
Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
parent ca40d481
Loading
Loading
Loading
Loading
+30 −24
Original line number Diff line number Diff line
@@ -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,29 +2292,37 @@ 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) {

			release_sock(sk);

			if (big_sync && err) {
				bt_dev_err(hdev, "hci_le_big_create_sync: %d",
					   err);
				sock_put(sk);
				sk = NULL;
			}
		}
		}

		goto done;
	}
@@ -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.