Commit 67caf26d authored by Jakub Kicinski's avatar Jakub Kicinski

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

Luiz Augusto von Dentz says:

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

 - Fix compiler warnings on btnxpuart
 - Fix potential double free on hci_conn_unlink
 - Fix UAF on hci_conn_hash_flush

* tag 'for-net-2023-05-19' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth:
  Bluetooth: btnxpuart: Fix compiler warnings
  Bluetooth: Unlink CISes when LE disconnects in hci_conn_del
  Bluetooth: Fix UAF in hci_conn_hash_flush again
  Bluetooth: Refcnt drop must be placed last in hci_conn_unlink
  Bluetooth: Fix potential double free caused by hci_conn_unlink
====================

Link: https://lore.kernel.org/r/20230519233056.2024340-1-luiz.dentz@gmail.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents ae9b15fb 6ce5169e
...@@ -1319,17 +1319,17 @@ static void nxp_serdev_remove(struct serdev_device *serdev) ...@@ -1319,17 +1319,17 @@ static void nxp_serdev_remove(struct serdev_device *serdev)
hci_free_dev(hdev); hci_free_dev(hdev);
} }
static struct btnxpuart_data w8987_data = { static struct btnxpuart_data w8987_data __maybe_unused = {
.helper_fw_name = NULL, .helper_fw_name = NULL,
.fw_name = FIRMWARE_W8987, .fw_name = FIRMWARE_W8987,
}; };
static struct btnxpuart_data w8997_data = { static struct btnxpuart_data w8997_data __maybe_unused = {
.helper_fw_name = FIRMWARE_HELPER, .helper_fw_name = FIRMWARE_HELPER,
.fw_name = FIRMWARE_W8997, .fw_name = FIRMWARE_W8997,
}; };
static const struct of_device_id nxpuart_of_match_table[] = { static const struct of_device_id nxpuart_of_match_table[] __maybe_unused = {
{ .compatible = "nxp,88w8987-bt", .data = &w8987_data }, { .compatible = "nxp,88w8987-bt", .data = &w8987_data },
{ .compatible = "nxp,88w8997-bt", .data = &w8997_data }, { .compatible = "nxp,88w8997-bt", .data = &w8997_data },
{ } { }
......
...@@ -1327,7 +1327,7 @@ int hci_le_create_cis(struct hci_conn *conn); ...@@ -1327,7 +1327,7 @@ int hci_le_create_cis(struct hci_conn *conn);
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
u8 role); u8 role);
int hci_conn_del(struct hci_conn *conn); void hci_conn_del(struct hci_conn *conn);
void hci_conn_hash_flush(struct hci_dev *hdev); void hci_conn_hash_flush(struct hci_dev *hdev);
void hci_conn_check_pending(struct hci_dev *hdev); void hci_conn_check_pending(struct hci_dev *hdev);
......
...@@ -1083,8 +1083,28 @@ static void hci_conn_unlink(struct hci_conn *conn) ...@@ -1083,8 +1083,28 @@ static void hci_conn_unlink(struct hci_conn *conn)
if (!conn->parent) { if (!conn->parent) {
struct hci_link *link, *t; struct hci_link *link, *t;
list_for_each_entry_safe(link, t, &conn->link_list, list) list_for_each_entry_safe(link, t, &conn->link_list, list) {
hci_conn_unlink(link->conn); struct hci_conn *child = link->conn;
hci_conn_unlink(child);
/* If hdev is down it means
* hci_dev_close_sync/hci_conn_hash_flush is in progress
* and links don't need to be cleanup as all connections
* would be cleanup.
*/
if (!test_bit(HCI_UP, &hdev->flags))
continue;
/* Due to race, SCO connection might be not established
* yet at this point. Delete it now, otherwise it is
* possible for it to be stuck and can't be deleted.
*/
if ((child->type == SCO_LINK ||
child->type == ESCO_LINK) &&
child->handle == HCI_CONN_HANDLE_UNSET)
hci_conn_del(child);
}
return; return;
} }
...@@ -1092,35 +1112,30 @@ static void hci_conn_unlink(struct hci_conn *conn) ...@@ -1092,35 +1112,30 @@ static void hci_conn_unlink(struct hci_conn *conn)
if (!conn->link) if (!conn->link)
return; return;
hci_conn_put(conn->parent);
conn->parent = NULL;
list_del_rcu(&conn->link->list); list_del_rcu(&conn->link->list);
synchronize_rcu(); synchronize_rcu();
hci_conn_drop(conn->parent);
hci_conn_put(conn->parent);
conn->parent = NULL;
kfree(conn->link); kfree(conn->link);
conn->link = NULL; conn->link = NULL;
/* Due to race, SCO connection might be not established
* yet at this point. Delete it now, otherwise it is
* possible for it to be stuck and can't be deleted.
*/
if (conn->handle == HCI_CONN_HANDLE_UNSET)
hci_conn_del(conn);
} }
int hci_conn_del(struct hci_conn *conn) void hci_conn_del(struct hci_conn *conn)
{ {
struct hci_dev *hdev = conn->hdev; struct hci_dev *hdev = conn->hdev;
BT_DBG("%s hcon %p handle %d", hdev->name, conn, conn->handle); BT_DBG("%s hcon %p handle %d", hdev->name, conn, conn->handle);
hci_conn_unlink(conn);
cancel_delayed_work_sync(&conn->disc_work); cancel_delayed_work_sync(&conn->disc_work);
cancel_delayed_work_sync(&conn->auto_accept_work); cancel_delayed_work_sync(&conn->auto_accept_work);
cancel_delayed_work_sync(&conn->idle_work); cancel_delayed_work_sync(&conn->idle_work);
if (conn->type == ACL_LINK) { if (conn->type == ACL_LINK) {
hci_conn_unlink(conn);
/* Unacked frames */ /* Unacked frames */
hdev->acl_cnt += conn->sent; hdev->acl_cnt += conn->sent;
} else if (conn->type == LE_LINK) { } else if (conn->type == LE_LINK) {
...@@ -1131,13 +1146,6 @@ int hci_conn_del(struct hci_conn *conn) ...@@ -1131,13 +1146,6 @@ int hci_conn_del(struct hci_conn *conn)
else else
hdev->acl_cnt += conn->sent; hdev->acl_cnt += conn->sent;
} else { } else {
struct hci_conn *acl = conn->parent;
if (acl) {
hci_conn_unlink(conn);
hci_conn_drop(acl);
}
/* Unacked ISO frames */ /* Unacked ISO frames */
if (conn->type == ISO_LINK) { if (conn->type == ISO_LINK) {
if (hdev->iso_pkts) if (hdev->iso_pkts)
...@@ -1160,8 +1168,6 @@ int hci_conn_del(struct hci_conn *conn) ...@@ -1160,8 +1168,6 @@ int hci_conn_del(struct hci_conn *conn)
* rest of hci_conn_del. * rest of hci_conn_del.
*/ */
hci_conn_cleanup(conn); hci_conn_cleanup(conn);
return 0;
} }
struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src, uint8_t src_type) struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src, uint8_t src_type)
...@@ -2462,22 +2468,21 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active) ...@@ -2462,22 +2468,21 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active)
/* Drop all connection on the device */ /* Drop all connection on the device */
void hci_conn_hash_flush(struct hci_dev *hdev) void hci_conn_hash_flush(struct hci_dev *hdev)
{ {
struct hci_conn_hash *h = &hdev->conn_hash; struct list_head *head = &hdev->conn_hash.list;
struct hci_conn *c, *n; struct hci_conn *conn;
BT_DBG("hdev %s", hdev->name); BT_DBG("hdev %s", hdev->name);
list_for_each_entry_safe(c, n, &h->list, list) { /* We should not traverse the list here, because hci_conn_del
c->state = BT_CLOSED; * can remove extra links, which may cause the list traversal
* to hit items that have already been released.
hci_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM); */
while ((conn = list_first_entry_or_null(head,
/* Unlink before deleting otherwise it is possible that struct hci_conn,
* hci_conn_del removes the link which may cause the list to list)) != NULL) {
* contain items already freed. conn->state = BT_CLOSED;
*/ hci_disconn_cfm(conn, HCI_ERROR_LOCAL_HOST_TERM);
hci_conn_unlink(c); hci_conn_del(conn);
hci_conn_del(c);
} }
} }
......
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