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

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

 - Fix building with coredump disabled
 - Fix use-after-free in hci_remove_adv_monitor
 - Use RCU for hci_conn_params and iterate safely in hci_sync
 - Fix locking issues on ISO and SCO
 - Fix bluetooth on Intel Macbook 2014

* tag 'for-net-2023-07-20' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth:
  Bluetooth: MGMT: Use correct address for memcpy()
  Bluetooth: btusb: Fix bluetooth on Intel Macbook 2014
  Bluetooth: SCO: fix sco_conn related locking and validity issues
  Bluetooth: hci_conn: return ERR_PTR instead of NULL when there is no link
  Bluetooth: hci_sync: Avoid use-after-free in dbg for hci_remove_adv_monitor()
  Bluetooth: coredump: fix building with coredump disabled
  Bluetooth: ISO: fix iso_conn related locking and validity issues
  Bluetooth: hci_event: call disconnect callback before deleting conn
  Bluetooth: use RCU for hci_conn_params and iterate safely in hci_sync
====================

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


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 9b39f758 d1f0a981
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -4104,6 +4104,7 @@ static int btusb_probe(struct usb_interface *intf,
	BT_DBG("intf %p id %p", intf, id);

	if ((id->driver_info & BTUSB_IFNUM_2) &&
	    (intf->cur_altsetting->desc.bInterfaceNumber != 0) &&
	    (intf->cur_altsetting->desc.bInterfaceNumber != 2))
		return -ENODEV;

+5 −2
Original line number Diff line number Diff line
@@ -593,9 +593,7 @@ struct hci_dev {
	const char		*fw_info;
	struct dentry		*debugfs;

#ifdef CONFIG_DEV_COREDUMP
	struct hci_devcoredump	dump;
#endif

	struct device		dev;

@@ -822,6 +820,7 @@ struct hci_conn_params {

	struct hci_conn *conn;
	bool explicit_connect;
	/* Accessed without hdev->lock: */
	hci_conn_flags_t flags;
	u8  privacy_mode;
};
@@ -1573,7 +1572,11 @@ struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev,
					    bdaddr_t *addr, u8 addr_type);
void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type);
void hci_conn_params_clear_disabled(struct hci_dev *hdev);
void hci_conn_params_free(struct hci_conn_params *param);

void hci_pend_le_list_del_init(struct hci_conn_params *param);
void hci_pend_le_list_add(struct hci_conn_params *param,
			  struct list_head *list);
struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list,
						  bdaddr_t *addr,
						  u8 addr_type);
+7 −7
Original line number Diff line number Diff line
@@ -118,7 +118,7 @@ static void hci_connect_le_scan_cleanup(struct hci_conn *conn, u8 status)
	 */
	params->explicit_connect = false;

	list_del_init(&params->action);
	hci_pend_le_list_del_init(params);

	switch (params->auto_connect) {
	case HCI_AUTO_CONN_EXPLICIT:
@@ -127,10 +127,10 @@ static void hci_connect_le_scan_cleanup(struct hci_conn *conn, u8 status)
		return;
	case HCI_AUTO_CONN_DIRECT:
	case HCI_AUTO_CONN_ALWAYS:
		list_add(&params->action, &hdev->pend_le_conns);
		hci_pend_le_list_add(params, &hdev->pend_le_conns);
		break;
	case HCI_AUTO_CONN_REPORT:
		list_add(&params->action, &hdev->pend_le_reports);
		hci_pend_le_list_add(params, &hdev->pend_le_reports);
		break;
	default:
		break;
@@ -1426,8 +1426,8 @@ static int hci_explicit_conn_params_set(struct hci_dev *hdev,
	if (params->auto_connect == HCI_AUTO_CONN_DISABLED ||
	    params->auto_connect == HCI_AUTO_CONN_REPORT ||
	    params->auto_connect == HCI_AUTO_CONN_EXPLICIT) {
		list_del_init(&params->action);
		list_add(&params->action, &hdev->pend_le_conns);
		hci_pend_le_list_del_init(params);
		hci_pend_le_list_add(params, &hdev->pend_le_conns);
	}

	params->explicit_connect = true;
@@ -1684,7 +1684,7 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
	if (!link) {
		hci_conn_drop(acl);
		hci_conn_drop(sco);
		return NULL;
		return ERR_PTR(-ENOLINK);
	}

	sco->setting = setting;
@@ -2254,7 +2254,7 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
	if (!link) {
		hci_conn_drop(le);
		hci_conn_drop(cis);
		return NULL;
		return ERR_PTR(-ENOLINK);
	}

	/* If LE is already connected and CIS handle is already set proceed to
+34 −8
Original line number Diff line number Diff line
@@ -1972,6 +1972,7 @@ static int hci_remove_adv_monitor(struct hci_dev *hdev,
				  struct adv_monitor *monitor)
{
	int status = 0;
	int handle;

	switch (hci_get_adv_monitor_offload_ext(hdev)) {
	case HCI_ADV_MONITOR_EXT_NONE: /* also goes here when powered off */
@@ -1980,9 +1981,10 @@ static int hci_remove_adv_monitor(struct hci_dev *hdev,
		goto free_monitor;

	case HCI_ADV_MONITOR_EXT_MSFT:
		handle = monitor->handle;
		status = msft_remove_monitor(hdev, monitor);
		bt_dev_dbg(hdev, "%s remove monitor %d msft status %d",
			   hdev->name, monitor->handle, status);
			   hdev->name, handle, status);
		break;
	}

@@ -2249,21 +2251,45 @@ struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev,
	return NULL;
}

/* This function requires the caller holds hdev->lock */
/* This function requires the caller holds hdev->lock or rcu_read_lock */
struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list,
						  bdaddr_t *addr, u8 addr_type)
{
	struct hci_conn_params *param;

	list_for_each_entry(param, list, action) {
	rcu_read_lock();

	list_for_each_entry_rcu(param, list, action) {
		if (bacmp(&param->addr, addr) == 0 &&
		    param->addr_type == addr_type)
		    param->addr_type == addr_type) {
			rcu_read_unlock();
			return param;
		}
	}

	rcu_read_unlock();

	return NULL;
}

/* This function requires the caller holds hdev->lock */
void hci_pend_le_list_del_init(struct hci_conn_params *param)
{
	if (list_empty(&param->action))
		return;

	list_del_rcu(&param->action);
	synchronize_rcu();
	INIT_LIST_HEAD(&param->action);
}

/* This function requires the caller holds hdev->lock */
void hci_pend_le_list_add(struct hci_conn_params *param,
			  struct list_head *list)
{
	list_add_rcu(&param->action, list);
}

/* This function requires the caller holds hdev->lock */
struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev,
					    bdaddr_t *addr, u8 addr_type)
@@ -2297,14 +2323,15 @@ struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev,
	return params;
}

static void hci_conn_params_free(struct hci_conn_params *params)
void hci_conn_params_free(struct hci_conn_params *params)
{
	hci_pend_le_list_del_init(params);

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

	list_del(&params->action);
	list_del(&params->list);
	kfree(params);
}
@@ -2342,8 +2369,7 @@ void hci_conn_params_clear_disabled(struct hci_dev *hdev)
			continue;
		}

		list_del(&params->list);
		kfree(params);
		hci_conn_params_free(params);
	}

	BT_DBG("All LE disabled connection parameters were removed");
+9 −6
Original line number Diff line number Diff line
@@ -1564,7 +1564,7 @@ static u8 hci_cc_le_set_privacy_mode(struct hci_dev *hdev, void *data,

	params = hci_conn_params_lookup(hdev, &cp->bdaddr, cp->bdaddr_type);
	if (params)
		params->privacy_mode = cp->mode;
		WRITE_ONCE(params->privacy_mode, cp->mode);

	hci_dev_unlock(hdev);

@@ -2784,6 +2784,9 @@ static void hci_cs_disconnect(struct hci_dev *hdev, u8 status)
			hci_enable_advertising(hdev);
		}

		/* Inform sockets conn is gone before we delete it */
		hci_disconn_cfm(conn, HCI_ERROR_UNSPECIFIED);

		goto done;
	}

@@ -2804,8 +2807,8 @@ static void hci_cs_disconnect(struct hci_dev *hdev, u8 status)

		case HCI_AUTO_CONN_DIRECT:
		case HCI_AUTO_CONN_ALWAYS:
			list_del_init(&params->action);
			list_add(&params->action, &hdev->pend_le_conns);
			hci_pend_le_list_del_init(params);
			hci_pend_le_list_add(params, &hdev->pend_le_conns);
			break;

		default:
@@ -3423,8 +3426,8 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, void *data,

		case HCI_AUTO_CONN_DIRECT:
		case HCI_AUTO_CONN_ALWAYS:
			list_del_init(&params->action);
			list_add(&params->action, &hdev->pend_le_conns);
			hci_pend_le_list_del_init(params);
			hci_pend_le_list_add(params, &hdev->pend_le_conns);
			hci_update_passive_scan(hdev);
			break;

@@ -5962,7 +5965,7 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
	params = hci_pend_le_action_lookup(&hdev->pend_le_conns, &conn->dst,
					   conn->dst_type);
	if (params) {
		list_del_init(&params->action);
		hci_pend_le_list_del_init(params);
		if (params->conn) {
			hci_conn_drop(params->conn);
			hci_conn_put(params->conn);
Loading