Commit 160c1317 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files
Luiz Augusto von Dentz says:

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

 - Fix not setting Dath Path for broadcast sink
 - Fix not cleaning up on LE Connection failure
 - SCO: Fix possible circular locking dependency
 - L2CAP: Fix use-after-free in l2cap_disconnect_{req,rsp}
 - Fix race condition in hidp_session_thread
 - btbcm: Fix logic error in forming the board name
 - btbcm: Fix use after free in btsdio_remove

* tag 'for-net-2023-04-10' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth:
  Bluetooth: L2CAP: Fix use-after-free in l2cap_disconnect_{req,rsp}
  Bluetooth: Set ISO Data Path on broadcast sink
  Bluetooth: hci_conn: Fix possible UAF
  Bluetooth: SCO: Fix possible circular locking dependency sco_sock_getsockopt
  Bluetooth: SCO: Fix possible circular locking dependency on sco_connect_cfm
  bluetooth: btbcm: Fix logic error in forming the board name.
  Bluetooth: btsdio: fix use after free bug in btsdio_remove due to race condition
  Bluetooth: Fix race condition in hidp_session_thread
  Bluetooth: Fix printing errors if LE Connection times out
  Bluetooth: hci_conn: Fix not cleaning up on LE Connection failure
====================

Link: https://lore.kernel.org/r/20230410172718.4067798-1-luiz.dentz@gmail.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents a4506722 a2a9339e
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -511,7 +511,7 @@ static const char *btbcm_get_board_name(struct device *dev)
	len = strlen(tmp) + 1;
	board_type = devm_kzalloc(dev, len, GFP_KERNEL);
	strscpy(board_type, tmp, len);
	for (i = 0; i < board_type[i]; i++) {
	for (i = 0; i < len; i++) {
		if (board_type[i] == '/')
			board_type[i] = '-';
	}
+1 −0
Original line number Diff line number Diff line
@@ -358,6 +358,7 @@ static void btsdio_remove(struct sdio_func *func)
	if (!data)
		return;

	cancel_work_sync(&data->work);
	hdev = data->hdev;

	sdio_set_drvdata(func, NULL);
+1 −0
Original line number Diff line number Diff line
@@ -954,6 +954,7 @@ enum {
	HCI_CONN_STK_ENCRYPT,
	HCI_CONN_AUTH_INITIATOR,
	HCI_CONN_DROP,
	HCI_CONN_CANCEL,
	HCI_CONN_PARAM_REMOVAL_PEND,
	HCI_CONN_NEW_LINK_KEY,
	HCI_CONN_SCANNING,
+53 −36
Original line number Diff line number Diff line
@@ -68,7 +68,7 @@ static const struct sco_param esco_param_msbc[] = {
};

/* This function requires the caller holds hdev->lock */
static void hci_connect_le_scan_cleanup(struct hci_conn *conn)
static void hci_connect_le_scan_cleanup(struct hci_conn *conn, u8 status)
{
	struct hci_conn_params *params;
	struct hci_dev *hdev = conn->hdev;
@@ -88,9 +88,28 @@ static void hci_connect_le_scan_cleanup(struct hci_conn *conn)

	params = hci_pend_le_action_lookup(&hdev->pend_le_conns, bdaddr,
					   bdaddr_type);
	if (!params || !params->explicit_connect)
	if (!params)
		return;

	if (params->conn) {
		hci_conn_drop(params->conn);
		hci_conn_put(params->conn);
		params->conn = NULL;
	}

	if (!params->explicit_connect)
		return;

	/* If the status indicates successful cancellation of
	 * the attempt (i.e. Unknown Connection Id) there's no point of
	 * notifying failure since we'll go back to keep trying to
	 * connect. The only exception is explicit connect requests
	 * where a timeout + cancel does indicate an actual failure.
	 */
	if (status && status != HCI_ERROR_UNKNOWN_CONN_ID)
		mgmt_connect_failed(hdev, &conn->dst, conn->type,
				    conn->dst_type, status);

	/* The connection attempt was doing scan for new RPA, and is
	 * in scan phase. If params are not associated with any other
	 * autoconnect action, remove them completely. If they are, just unmark
@@ -178,7 +197,7 @@ static void le_scan_cleanup(struct work_struct *work)
	rcu_read_unlock();

	if (c == conn) {
		hci_connect_le_scan_cleanup(conn);
		hci_connect_le_scan_cleanup(conn, 0x00);
		hci_conn_cleanup(conn);
	}

@@ -1049,6 +1068,17 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
	return conn;
}

static bool hci_conn_unlink(struct hci_conn *conn)
{
	if (!conn->link)
		return false;

	conn->link->link = NULL;
	conn->link = NULL;

	return true;
}

int hci_conn_del(struct hci_conn *conn)
{
	struct hci_dev *hdev = conn->hdev;
@@ -1060,15 +1090,16 @@ int hci_conn_del(struct hci_conn *conn)
	cancel_delayed_work_sync(&conn->idle_work);

	if (conn->type == ACL_LINK) {
		struct hci_conn *sco = conn->link;
		if (sco) {
			sco->link = NULL;
		struct hci_conn *link = conn->link;

		if (link) {
			hci_conn_unlink(conn);
			/* 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 (sco->handle == HCI_CONN_HANDLE_UNSET)
				hci_conn_del(sco);
			if (link->handle == HCI_CONN_HANDLE_UNSET)
				hci_conn_del(link);
		}

		/* Unacked frames */
@@ -1084,7 +1115,7 @@ int hci_conn_del(struct hci_conn *conn)
		struct hci_conn *acl = conn->link;

		if (acl) {
			acl->link = NULL;
			hci_conn_unlink(conn);
			hci_conn_drop(acl);
		}

@@ -1179,31 +1210,8 @@ EXPORT_SYMBOL(hci_get_route);
static void hci_le_conn_failed(struct hci_conn *conn, u8 status)
{
	struct hci_dev *hdev = conn->hdev;
	struct hci_conn_params *params;

	params = hci_pend_le_action_lookup(&hdev->pend_le_conns, &conn->dst,
					   conn->dst_type);
	if (params && params->conn) {
		hci_conn_drop(params->conn);
		hci_conn_put(params->conn);
		params->conn = NULL;
	}

	/* If the status indicates successful cancellation of
	 * the attempt (i.e. Unknown Connection Id) there's no point of
	 * notifying failure since we'll go back to keep trying to
	 * connect. The only exception is explicit connect requests
	 * where a timeout + cancel does indicate an actual failure.
	 */
	if (status != HCI_ERROR_UNKNOWN_CONN_ID ||
	    (params && params->explicit_connect))
		mgmt_connect_failed(hdev, &conn->dst, conn->type,
				    conn->dst_type, status);

	/* Since we may have temporarily stopped the background scanning in
	 * favor of connection establishment, we should restart it.
	 */
	hci_update_passive_scan(hdev);
	hci_connect_le_scan_cleanup(conn, status);

	/* Enable advertising in case this was a failed connection
	 * attempt as a peripheral.
@@ -1237,15 +1245,15 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
{
	struct hci_conn *conn = data;

	bt_dev_dbg(hdev, "err %d", err);

	hci_dev_lock(hdev);

	if (!err) {
		hci_connect_le_scan_cleanup(conn);
		hci_connect_le_scan_cleanup(conn, 0x00);
		goto done;
	}

	bt_dev_err(hdev, "request failed to create LE connection: err %d", err);

	/* Check if connection is still pending */
	if (conn != hci_lookup_le_connect(hdev))
		goto done;
@@ -2438,6 +2446,12 @@ void hci_conn_hash_flush(struct hci_dev *hdev)
		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.
		 */
		hci_conn_unlink(c);
		hci_conn_del(c);
	}
}
@@ -2775,6 +2789,9 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
{
	int r = 0;

	if (test_and_set_bit(HCI_CONN_CANCEL, &conn->flags))
		return 0;

	switch (conn->state) {
	case BT_CONNECTED:
	case BT_CONFIG:
+7 −11
Original line number Diff line number Diff line
@@ -2881,16 +2881,6 @@ static void cs_le_create_conn(struct hci_dev *hdev, bdaddr_t *peer_addr,

	conn->resp_addr_type = peer_addr_type;
	bacpy(&conn->resp_addr, peer_addr);

	/* We don't want the connection attempt to stick around
	 * indefinitely since LE doesn't have a page timeout concept
	 * like BR/EDR. Set a timer for any connection that doesn't use
	 * the accept list for connecting.
	 */
	if (filter_policy == HCI_LE_USE_PEER_ADDR)
		queue_delayed_work(conn->hdev->workqueue,
				   &conn->le_conn_timeout,
				   conn->conn_timeout);
}

static void hci_cs_le_create_conn(struct hci_dev *hdev, u8 status)
@@ -5902,6 +5892,12 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
	if (status)
		goto unlock;

	/* Drop the connection if it has been aborted */
	if (test_bit(HCI_CONN_CANCEL, &conn->flags)) {
		hci_conn_drop(conn);
		goto unlock;
	}

	if (conn->dst_type == ADDR_LE_DEV_PUBLIC)
		addr_type = BDADDR_LE_PUBLIC;
	else
@@ -6995,7 +6991,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
		bis->iso_qos.in.latency = le16_to_cpu(ev->interval) * 125 / 100;
		bis->iso_qos.in.sdu = le16_to_cpu(ev->max_pdu);

		hci_connect_cfm(bis, ev->status);
		hci_iso_setup_path(bis);
	}

	hci_dev_unlock(hdev);
Loading