Commit 67caf26d authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files
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.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents ae9b15fb 6ce5169e
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -1319,17 +1319,17 @@ static void nxp_serdev_remove(struct serdev_device *serdev)
	hci_free_dev(hdev);
}

static struct btnxpuart_data w8987_data = {
static struct btnxpuart_data w8987_data __maybe_unused = {
	.helper_fw_name = NULL,
	.fw_name = FIRMWARE_W8987,
};

static struct btnxpuart_data w8997_data = {
static struct btnxpuart_data w8997_data __maybe_unused = {
	.helper_fw_name = FIRMWARE_HELPER,
	.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,88w8997-bt", .data = &w8997_data },
	{ }
+1 −1
Original line number Diff line number Diff line
@@ -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,
			      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_check_pending(struct hci_dev *hdev);

+41 −36
Original line number Diff line number Diff line
@@ -1083,8 +1083,28 @@ static void hci_conn_unlink(struct hci_conn *conn)
	if (!conn->parent) {
		struct hci_link *link, *t;

		list_for_each_entry_safe(link, t, &conn->link_list, list)
			hci_conn_unlink(link->conn);
		list_for_each_entry_safe(link, t, &conn->link_list, list) {
			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;
	}
@@ -1092,35 +1112,30 @@ static void hci_conn_unlink(struct hci_conn *conn)
	if (!conn->link)
		return;

	hci_conn_put(conn->parent);
	conn->parent = NULL;

	list_del_rcu(&conn->link->list);
	synchronize_rcu();

	hci_conn_drop(conn->parent);
	hci_conn_put(conn->parent);
	conn->parent = NULL;

	kfree(conn->link);
	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;

	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->auto_accept_work);
	cancel_delayed_work_sync(&conn->idle_work);

	if (conn->type == ACL_LINK) {
		hci_conn_unlink(conn);
		/* Unacked frames */
		hdev->acl_cnt += conn->sent;
	} else if (conn->type == LE_LINK) {
@@ -1131,13 +1146,6 @@ int hci_conn_del(struct hci_conn *conn)
		else
			hdev->acl_cnt += conn->sent;
	} else {
		struct hci_conn *acl = conn->parent;

		if (acl) {
			hci_conn_unlink(conn);
			hci_conn_drop(acl);
		}

		/* Unacked ISO frames */
		if (conn->type == ISO_LINK) {
			if (hdev->iso_pkts)
@@ -1160,8 +1168,6 @@ int hci_conn_del(struct hci_conn *conn)
	 * rest of hci_conn_del.
	 */
	hci_conn_cleanup(conn);

	return 0;
}

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)
/* Drop all connection on the device */
void hci_conn_hash_flush(struct hci_dev *hdev)
{
	struct hci_conn_hash *h = &hdev->conn_hash;
	struct hci_conn *c, *n;
	struct list_head *head = &hdev->conn_hash.list;
	struct hci_conn *conn;

	BT_DBG("hdev %s", hdev->name);

	list_for_each_entry_safe(c, n, &h->list, list) {
		c->state = BT_CLOSED;

		hci_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM);

		/* Unlink before deleting otherwise it is possible that
		 * hci_conn_del removes the link which may cause the list to
		 * contain items already freed.
	/* We should not traverse the list here, because hci_conn_del
	 * can remove extra links, which may cause the list traversal
	 * to hit items that have already been released.
	 */
		hci_conn_unlink(c);
		hci_conn_del(c);
	while ((conn = list_first_entry_or_null(head,
						struct hci_conn,
						list)) != NULL) {
		conn->state = BT_CLOSED;
		hci_disconn_cfm(conn, HCI_ERROR_LOCAL_HOST_TERM);
		hci_conn_del(conn);
	}
}