Commit ab39b113 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge tag 'for-net-2023-06-05' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth

Luiz Augusto von Dentz says:

====================
bluetooth pull request for net:

 - Fixes to debugfs registration
 - Fix use-after-free in hci_remove_ltk/hci_remove_irk
 - Fixes to ISO channel support
 - Fix missing checks for invalid L2CAP DCID
 - Fix l2cap_disconnect_req deadlock
 - Add lock to protect HCI_UNREGISTER

* tag 'for-net-2023-06-05' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth:
  Bluetooth: L2CAP: Add missing checks for invalid DCID
  Bluetooth: ISO: use correct CIS order in Set CIG Parameters event
  Bluetooth: ISO: don't try to remove CIG if there are bound CIS left
  Bluetooth: Fix l2cap_disconnect_req deadlock
  Bluetooth: hci_qca: fix debugfs registration
  Bluetooth: fix debugfs registration
  Bluetooth: hci_sync: add lock to protect HCI_UNREGISTER
  Bluetooth: Fix use-after-free in hci_remove_ltk/hci_remove_irk
  Bluetooth: ISO: Fix CIG auto-allocation to select configurable CIG
  Bluetooth: ISO: consider right CIS when removing CIG at cleanup
====================

Link: https://lore.kernel.org/r/20230606003454.2392552-1-luiz.dentz@gmail.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 20c47646 75767213
...@@ -78,7 +78,8 @@ enum qca_flags { ...@@ -78,7 +78,8 @@ enum qca_flags {
QCA_HW_ERROR_EVENT, QCA_HW_ERROR_EVENT,
QCA_SSR_TRIGGERED, QCA_SSR_TRIGGERED,
QCA_BT_OFF, QCA_BT_OFF,
QCA_ROM_FW QCA_ROM_FW,
QCA_DEBUGFS_CREATED,
}; };
enum qca_capabilities { enum qca_capabilities {
...@@ -635,6 +636,9 @@ static void qca_debugfs_init(struct hci_dev *hdev) ...@@ -635,6 +636,9 @@ static void qca_debugfs_init(struct hci_dev *hdev)
if (!hdev->debugfs) if (!hdev->debugfs)
return; return;
if (test_and_set_bit(QCA_DEBUGFS_CREATED, &qca->flags))
return;
ibs_dir = debugfs_create_dir("ibs", hdev->debugfs); ibs_dir = debugfs_create_dir("ibs", hdev->debugfs);
/* read only */ /* read only */
......
...@@ -350,6 +350,7 @@ enum { ...@@ -350,6 +350,7 @@ enum {
enum { enum {
HCI_SETUP, HCI_SETUP,
HCI_CONFIG, HCI_CONFIG,
HCI_DEBUGFS_CREATED,
HCI_AUTO_OFF, HCI_AUTO_OFF,
HCI_RFKILLED, HCI_RFKILLED,
HCI_MGMT, HCI_MGMT,
......
...@@ -515,6 +515,7 @@ struct hci_dev { ...@@ -515,6 +515,7 @@ struct hci_dev {
struct work_struct cmd_sync_work; struct work_struct cmd_sync_work;
struct list_head cmd_sync_work_list; struct list_head cmd_sync_work_list;
struct mutex cmd_sync_work_lock; struct mutex cmd_sync_work_lock;
struct mutex unregister_lock;
struct work_struct cmd_sync_cancel_work; struct work_struct cmd_sync_cancel_work;
struct work_struct reenable_adv_work; struct work_struct reenable_adv_work;
...@@ -1201,7 +1202,8 @@ static inline struct hci_conn *hci_conn_hash_lookup_cis(struct hci_dev *hdev, ...@@ -1201,7 +1202,8 @@ static inline struct hci_conn *hci_conn_hash_lookup_cis(struct hci_dev *hdev,
if (id != BT_ISO_QOS_CIS_UNSET && id != c->iso_qos.ucast.cis) if (id != BT_ISO_QOS_CIS_UNSET && id != c->iso_qos.ucast.cis)
continue; continue;
if (ba_type == c->dst_type && !bacmp(&c->dst, ba)) { /* Match destination address if set */
if (!ba || (ba_type == c->dst_type && !bacmp(&c->dst, ba))) {
rcu_read_unlock(); rcu_read_unlock();
return c; return c;
} }
......
...@@ -947,8 +947,8 @@ static void find_cis(struct hci_conn *conn, void *data) ...@@ -947,8 +947,8 @@ static void find_cis(struct hci_conn *conn, void *data)
{ {
struct iso_list_data *d = data; struct iso_list_data *d = data;
/* Ignore broadcast */ /* Ignore broadcast or if CIG don't match */
if (!bacmp(&conn->dst, BDADDR_ANY)) if (!bacmp(&conn->dst, BDADDR_ANY) || d->cig != conn->iso_qos.ucast.cig)
return; return;
d->count++; d->count++;
...@@ -963,12 +963,17 @@ static void cis_cleanup(struct hci_conn *conn) ...@@ -963,12 +963,17 @@ static void cis_cleanup(struct hci_conn *conn)
struct hci_dev *hdev = conn->hdev; struct hci_dev *hdev = conn->hdev;
struct iso_list_data d; struct iso_list_data d;
if (conn->iso_qos.ucast.cig == BT_ISO_QOS_CIG_UNSET)
return;
memset(&d, 0, sizeof(d)); memset(&d, 0, sizeof(d));
d.cig = conn->iso_qos.ucast.cig; d.cig = conn->iso_qos.ucast.cig;
/* Check if ISO connection is a CIS and remove CIG if there are /* Check if ISO connection is a CIS and remove CIG if there are
* no other connections using it. * no other connections using it.
*/ */
hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_BOUND, &d);
hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_CONNECT, &d);
hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_CONNECTED, &d); hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_CONNECTED, &d);
if (d.count) if (d.count)
return; return;
...@@ -1766,24 +1771,23 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos) ...@@ -1766,24 +1771,23 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
memset(&data, 0, sizeof(data)); memset(&data, 0, sizeof(data));
/* Allocate a CIG if not set */ /* Allocate first still reconfigurable CIG if not set */
if (qos->ucast.cig == BT_ISO_QOS_CIG_UNSET) { if (qos->ucast.cig == BT_ISO_QOS_CIG_UNSET) {
for (data.cig = 0x00; data.cig < 0xff; data.cig++) { for (data.cig = 0x00; data.cig < 0xf0; data.cig++) {
data.count = 0; data.count = 0;
data.cis = 0xff;
hci_conn_hash_list_state(hdev, cis_list, ISO_LINK, hci_conn_hash_list_state(hdev, find_cis, ISO_LINK,
BT_BOUND, &data); BT_CONNECT, &data);
if (data.count) if (data.count)
continue; continue;
hci_conn_hash_list_state(hdev, cis_list, ISO_LINK, hci_conn_hash_list_state(hdev, find_cis, ISO_LINK,
BT_CONNECTED, &data); BT_CONNECTED, &data);
if (!data.count) if (!data.count)
break; break;
} }
if (data.cig == 0xff) if (data.cig == 0xf0)
return false; return false;
/* Update CIG */ /* Update CIG */
......
...@@ -1416,10 +1416,10 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) ...@@ -1416,10 +1416,10 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type) int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type)
{ {
struct smp_ltk *k; struct smp_ltk *k, *tmp;
int removed = 0; int removed = 0;
list_for_each_entry_rcu(k, &hdev->long_term_keys, list) { list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) {
if (bacmp(bdaddr, &k->bdaddr) || k->bdaddr_type != bdaddr_type) if (bacmp(bdaddr, &k->bdaddr) || k->bdaddr_type != bdaddr_type)
continue; continue;
...@@ -1435,9 +1435,9 @@ int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type) ...@@ -1435,9 +1435,9 @@ int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type)
void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type) void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type)
{ {
struct smp_irk *k; struct smp_irk *k, *tmp;
list_for_each_entry_rcu(k, &hdev->identity_resolving_keys, list) { list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) {
if (bacmp(bdaddr, &k->bdaddr) || k->addr_type != addr_type) if (bacmp(bdaddr, &k->bdaddr) || k->addr_type != addr_type)
continue; continue;
...@@ -2686,7 +2686,9 @@ void hci_unregister_dev(struct hci_dev *hdev) ...@@ -2686,7 +2686,9 @@ void hci_unregister_dev(struct hci_dev *hdev)
{ {
BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
mutex_lock(&hdev->unregister_lock);
hci_dev_set_flag(hdev, HCI_UNREGISTER); hci_dev_set_flag(hdev, HCI_UNREGISTER);
mutex_unlock(&hdev->unregister_lock);
write_lock(&hci_dev_list_lock); write_lock(&hci_dev_list_lock);
list_del(&hdev->list); list_del(&hdev->list);
......
...@@ -3804,48 +3804,56 @@ static u8 hci_cc_le_set_cig_params(struct hci_dev *hdev, void *data, ...@@ -3804,48 +3804,56 @@ static u8 hci_cc_le_set_cig_params(struct hci_dev *hdev, void *data,
struct sk_buff *skb) struct sk_buff *skb)
{ {
struct hci_rp_le_set_cig_params *rp = data; struct hci_rp_le_set_cig_params *rp = data;
struct hci_cp_le_set_cig_params *cp;
struct hci_conn *conn; struct hci_conn *conn;
int i = 0; u8 status = rp->status;
int i;
bt_dev_dbg(hdev, "status 0x%2.2x", rp->status); bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_CIG_PARAMS);
if (!cp || rp->num_handles != cp->num_cis || rp->cig_id != cp->cig_id) {
bt_dev_err(hdev, "unexpected Set CIG Parameters response data");
status = HCI_ERROR_UNSPECIFIED;
}
hci_dev_lock(hdev); hci_dev_lock(hdev);
if (rp->status) { if (status) {
while ((conn = hci_conn_hash_lookup_cig(hdev, rp->cig_id))) { while ((conn = hci_conn_hash_lookup_cig(hdev, rp->cig_id))) {
conn->state = BT_CLOSED; conn->state = BT_CLOSED;
hci_connect_cfm(conn, rp->status); hci_connect_cfm(conn, status);
hci_conn_del(conn); hci_conn_del(conn);
} }
goto unlock; goto unlock;
} }
rcu_read_lock(); /* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E page 2553
*
* If the Status return parameter is zero, then the Controller shall
* set the Connection_Handle arrayed return parameter to the connection
* handle(s) corresponding to the CIS configurations specified in
* the CIS_IDs command parameter, in the same order.
*/
for (i = 0; i < rp->num_handles; ++i) {
conn = hci_conn_hash_lookup_cis(hdev, NULL, 0, rp->cig_id,
cp->cis[i].cis_id);
if (!conn || !bacmp(&conn->dst, BDADDR_ANY))
continue;
list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) { if (conn->state != BT_BOUND && conn->state != BT_CONNECT)
if (conn->type != ISO_LINK ||
conn->iso_qos.ucast.cig != rp->cig_id ||
conn->state == BT_CONNECTED)
continue; continue;
conn->handle = __le16_to_cpu(rp->handle[i++]); conn->handle = __le16_to_cpu(rp->handle[i]);
bt_dev_dbg(hdev, "%p handle 0x%4.4x parent %p", conn, bt_dev_dbg(hdev, "%p handle 0x%4.4x parent %p", conn,
conn->handle, conn->parent); conn->handle, conn->parent);
/* Create CIS if LE is already connected */ /* Create CIS if LE is already connected */
if (conn->parent && conn->parent->state == BT_CONNECTED) { if (conn->parent && conn->parent->state == BT_CONNECTED)
rcu_read_unlock();
hci_le_create_cis(conn); hci_le_create_cis(conn);
rcu_read_lock();
}
if (i == rp->num_handles)
break;
} }
rcu_read_unlock();
unlock: unlock:
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
......
...@@ -629,6 +629,7 @@ void hci_cmd_sync_init(struct hci_dev *hdev) ...@@ -629,6 +629,7 @@ void hci_cmd_sync_init(struct hci_dev *hdev)
INIT_WORK(&hdev->cmd_sync_work, hci_cmd_sync_work); INIT_WORK(&hdev->cmd_sync_work, hci_cmd_sync_work);
INIT_LIST_HEAD(&hdev->cmd_sync_work_list); INIT_LIST_HEAD(&hdev->cmd_sync_work_list);
mutex_init(&hdev->cmd_sync_work_lock); mutex_init(&hdev->cmd_sync_work_lock);
mutex_init(&hdev->unregister_lock);
INIT_WORK(&hdev->cmd_sync_cancel_work, hci_cmd_sync_cancel_work); INIT_WORK(&hdev->cmd_sync_cancel_work, hci_cmd_sync_cancel_work);
INIT_WORK(&hdev->reenable_adv_work, reenable_adv); INIT_WORK(&hdev->reenable_adv_work, reenable_adv);
...@@ -692,14 +693,19 @@ int hci_cmd_sync_submit(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, ...@@ -692,14 +693,19 @@ int hci_cmd_sync_submit(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy) void *data, hci_cmd_sync_work_destroy_t destroy)
{ {
struct hci_cmd_sync_work_entry *entry; struct hci_cmd_sync_work_entry *entry;
int err = 0;
if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) mutex_lock(&hdev->unregister_lock);
return -ENODEV; if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) {
err = -ENODEV;
goto unlock;
}
entry = kmalloc(sizeof(*entry), GFP_KERNEL); entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) if (!entry) {
return -ENOMEM; err = -ENOMEM;
goto unlock;
}
entry->func = func; entry->func = func;
entry->data = data; entry->data = data;
entry->destroy = destroy; entry->destroy = destroy;
...@@ -710,7 +716,9 @@ int hci_cmd_sync_submit(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, ...@@ -710,7 +716,9 @@ int hci_cmd_sync_submit(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
queue_work(hdev->req_workqueue, &hdev->cmd_sync_work); queue_work(hdev->req_workqueue, &hdev->cmd_sync_work);
return 0; unlock:
mutex_unlock(&hdev->unregister_lock);
return err;
} }
EXPORT_SYMBOL(hci_cmd_sync_submit); EXPORT_SYMBOL(hci_cmd_sync_submit);
...@@ -4543,6 +4551,9 @@ static int hci_init_sync(struct hci_dev *hdev) ...@@ -4543,6 +4551,9 @@ static int hci_init_sync(struct hci_dev *hdev)
!hci_dev_test_flag(hdev, HCI_CONFIG)) !hci_dev_test_flag(hdev, HCI_CONFIG))
return 0; return 0;
if (hci_dev_test_and_set_flag(hdev, HCI_DEBUGFS_CREATED))
return 0;
hci_debugfs_create_common(hdev); hci_debugfs_create_common(hdev);
if (lmp_bredr_capable(hdev)) if (lmp_bredr_capable(hdev))
......
...@@ -4306,6 +4306,10 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn, ...@@ -4306,6 +4306,10 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
result = __le16_to_cpu(rsp->result); result = __le16_to_cpu(rsp->result);
status = __le16_to_cpu(rsp->status); status = __le16_to_cpu(rsp->status);
if (result == L2CAP_CR_SUCCESS && (dcid < L2CAP_CID_DYN_START ||
dcid > L2CAP_CID_DYN_END))
return -EPROTO;
BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x",
dcid, scid, result, status); dcid, scid, result, status);
...@@ -4337,6 +4341,11 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn, ...@@ -4337,6 +4341,11 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
switch (result) { switch (result) {
case L2CAP_CR_SUCCESS: case L2CAP_CR_SUCCESS:
if (__l2cap_get_chan_by_dcid(conn, dcid)) {
err = -EBADSLT;
break;
}
l2cap_state_change(chan, BT_CONFIG); l2cap_state_change(chan, BT_CONFIG);
chan->ident = 0; chan->ident = 0;
chan->dcid = dcid; chan->dcid = dcid;
...@@ -4663,7 +4672,9 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, ...@@ -4663,7 +4672,9 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
chan->ops->set_shutdown(chan); chan->ops->set_shutdown(chan);
l2cap_chan_unlock(chan);
mutex_lock(&conn->chan_lock); mutex_lock(&conn->chan_lock);
l2cap_chan_lock(chan);
l2cap_chan_del(chan, ECONNRESET); l2cap_chan_del(chan, ECONNRESET);
mutex_unlock(&conn->chan_lock); mutex_unlock(&conn->chan_lock);
...@@ -4702,7 +4713,9 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, ...@@ -4702,7 +4713,9 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn,
return 0; return 0;
} }
l2cap_chan_unlock(chan);
mutex_lock(&conn->chan_lock); mutex_lock(&conn->chan_lock);
l2cap_chan_lock(chan);
l2cap_chan_del(chan, 0); l2cap_chan_del(chan, 0);
mutex_unlock(&conn->chan_lock); mutex_unlock(&conn->chan_lock);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment