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

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

 - hci_conn: Fix not cleaning up PA_LINK connections
 - hci_event: Fix not handling PA Sync Lost event
 - MGMT: cancel mesh send timer when hdev removed
 - 6lowpan: reset link-local header on ipv6 recv path
 - 6lowpan: fix BDADDR_LE vs ADDR_LE_DEV address type confusion
 - L2CAP: export l2cap_chan_hold for modules
 - 6lowpan: Don't hold spin lock over sleeping functions
 - 6lowpan: add missing l2cap_chan_lock()
 - btusb: reorder cleanup in btusb_disconnect to avoid UAF
 - btrtl: Avoid loading the config file on security chips

* tag 'for-net-2025-11-11' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth:
  Bluetooth: btrtl: Avoid loading the config file on security chips
  Bluetooth: hci_event: Fix not handling PA Sync Lost event
  Bluetooth: hci_conn: Fix not cleaning up PA_LINK connections
  Bluetooth: 6lowpan: add missing l2cap_chan_lock()
  Bluetooth: 6lowpan: Don't hold spin lock over sleeping functions
  Bluetooth: L2CAP: export l2cap_chan_hold for modules
  Bluetooth: 6lowpan: fix BDADDR_LE vs ADDR_LE_DEV address type confusion
  Bluetooth: 6lowpan: reset link-local header on ipv6 recv path
  Bluetooth: btusb: reorder cleanup in btusb_disconnect to avoid UAF
  Bluetooth: MGMT: cancel mesh send timer when hdev removed
====================

Link: https://patch.msgid.link/20251111141357.1983153-1-luiz.dentz@gmail.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents bb8336a5 cd8dbd9e
Loading
Loading
Loading
Loading
+13 −11
Original line number Diff line number Diff line
@@ -50,7 +50,7 @@

#define	RTL_CHIP_SUBVER (&(struct rtl_vendor_cmd) {{0x10, 0x38, 0x04, 0x28, 0x80}})
#define	RTL_CHIP_REV    (&(struct rtl_vendor_cmd) {{0x10, 0x3A, 0x04, 0x28, 0x80}})
#define	RTL_SEC_PROJ    (&(struct rtl_vendor_cmd) {{0x10, 0xA4, 0x0D, 0x00, 0xb0}})
#define	RTL_SEC_PROJ    (&(struct rtl_vendor_cmd) {{0x10, 0xA4, 0xAD, 0x00, 0xb0}})

#define RTL_PATCH_SNIPPETS		0x01
#define RTL_PATCH_DUMMY_HEADER		0x02
@@ -534,7 +534,6 @@ static int rtlbt_parse_firmware_v2(struct hci_dev *hdev,
{
	struct rtl_epatch_header_v2 *hdr;
	int rc;
	u8 reg_val[2];
	u8 key_id;
	u32 num_sections;
	struct rtl_section *section;
@@ -549,14 +548,7 @@ static int rtlbt_parse_firmware_v2(struct hci_dev *hdev,
		.len  = btrtl_dev->fw_len - 7, /* Cut the tail */
	};

	rc = btrtl_vendor_read_reg16(hdev, RTL_SEC_PROJ, reg_val);
	if (rc < 0)
		return -EIO;
	key_id = reg_val[0];

	rtl_dev_dbg(hdev, "%s: key id %u", __func__, key_id);

	btrtl_dev->key_id = key_id;
	key_id = btrtl_dev->key_id;

	hdr = rtl_iov_pull_data(&iov, sizeof(*hdr));
	if (!hdr)
@@ -1070,6 +1062,8 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
	u16 hci_rev, lmp_subver;
	u8 hci_ver, lmp_ver, chip_type = 0;
	int ret;
	int rc;
	u8 key_id;
	u8 reg_val[2];

	btrtl_dev = kzalloc(sizeof(*btrtl_dev), GFP_KERNEL);
@@ -1180,6 +1174,14 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
		goto err_free;
	}

	rc = btrtl_vendor_read_reg16(hdev, RTL_SEC_PROJ, reg_val);
	if (rc < 0)
		goto err_free;

	key_id = reg_val[0];
	btrtl_dev->key_id = key_id;
	rtl_dev_info(hdev, "%s: key id %u", __func__, key_id);

	btrtl_dev->fw_len = -EIO;
	if (lmp_subver == RTL_ROM_LMP_8852A && hci_rev == 0x000c) {
		snprintf(fw_name, sizeof(fw_name), "%s_v2.bin",
@@ -1202,7 +1204,7 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
		goto err_free;
	}

	if (btrtl_dev->ic_info->cfg_name) {
	if (btrtl_dev->ic_info->cfg_name && !btrtl_dev->key_id) {
		if (postfix) {
			snprintf(cfg_name, sizeof(cfg_name), "%s-%s.bin",
				 btrtl_dev->ic_info->cfg_name, postfix);
+6 −7
Original line number Diff line number Diff line
@@ -4361,6 +4361,11 @@ static void btusb_disconnect(struct usb_interface *intf)

	hci_unregister_dev(hdev);

	if (data->oob_wake_irq)
		device_init_wakeup(&data->udev->dev, false);
	if (data->reset_gpio)
		gpiod_put(data->reset_gpio);

	if (intf == data->intf) {
		if (data->isoc)
			usb_driver_release_interface(&btusb_driver, data->isoc);
@@ -4371,17 +4376,11 @@ static void btusb_disconnect(struct usb_interface *intf)
			usb_driver_release_interface(&btusb_driver, data->diag);
		usb_driver_release_interface(&btusb_driver, data->intf);
	} else if (intf == data->diag) {
		usb_driver_release_interface(&btusb_driver, data->intf);
		if (data->isoc)
			usb_driver_release_interface(&btusb_driver, data->isoc);
		usb_driver_release_interface(&btusb_driver, data->intf);
	}

	if (data->oob_wake_irq)
		device_init_wakeup(&data->udev->dev, false);

	if (data->reset_gpio)
		gpiod_put(data->reset_gpio);

	hci_free_dev(hdev);
}

+5 −0
Original line number Diff line number Diff line
@@ -2783,6 +2783,11 @@ struct hci_ev_le_per_adv_report {
	__u8     data[];
} __packed;

#define HCI_EV_LE_PA_SYNC_LOST		0x10
struct hci_ev_le_pa_sync_lost {
	__le16 handle;
} __packed;

#define LE_PA_DATA_COMPLETE	0x00
#define LE_PA_DATA_MORE_TO_COME	0x01
#define LE_PA_DATA_TRUNCATED	0x02
+76 −29
Original line number Diff line number Diff line
@@ -53,6 +53,11 @@ static bool enable_6lowpan;
static struct l2cap_chan *listen_chan;
static DEFINE_MUTEX(set_lock);

enum {
	LOWPAN_PEER_CLOSING,
	LOWPAN_PEER_MAXBITS
};

struct lowpan_peer {
	struct list_head list;
	struct rcu_head rcu;
@@ -61,6 +66,8 @@ struct lowpan_peer {
	/* peer addresses in various formats */
	unsigned char lladdr[ETH_ALEN];
	struct in6_addr peer_addr;

	DECLARE_BITMAP(flags, LOWPAN_PEER_MAXBITS);
};

struct lowpan_btle_dev {
@@ -289,6 +296,7 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
		local_skb->pkt_type = PACKET_HOST;
		local_skb->dev = dev;

		skb_reset_mac_header(local_skb);
		skb_set_transport_header(local_skb, sizeof(struct ipv6hdr));

		if (give_skb_to_upper(local_skb, dev) != NET_RX_SUCCESS) {
@@ -919,7 +927,9 @@ static int bt_6lowpan_disconnect(struct l2cap_conn *conn, u8 dst_type)

	BT_DBG("peer %p chan %p", peer, peer->chan);

	l2cap_chan_lock(peer->chan);
	l2cap_chan_close(peer->chan, ENOENT);
	l2cap_chan_unlock(peer->chan);

	return 0;
}
@@ -956,10 +966,11 @@ static struct l2cap_chan *bt_6lowpan_listen(void)
}

static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
			  struct l2cap_conn **conn)
			  struct l2cap_conn **conn, bool disconnect)
{
	struct hci_conn *hcon;
	struct hci_dev *hdev;
	int le_addr_type;
	int n;

	n = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu",
@@ -970,13 +981,32 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
	if (n < 7)
		return -EINVAL;

	if (disconnect) {
		/* The "disconnect" debugfs command has used different address
		 * type constants than "connect" since 2015. Let's retain that
		 * for now even though it's obviously buggy...
		 */
		*addr_type += 1;
	}

	switch (*addr_type) {
	case BDADDR_LE_PUBLIC:
		le_addr_type = ADDR_LE_DEV_PUBLIC;
		break;
	case BDADDR_LE_RANDOM:
		le_addr_type = ADDR_LE_DEV_RANDOM;
		break;
	default:
		return -EINVAL;
	}

	/* The LE_PUBLIC address type is ignored because of BDADDR_ANY */
	hdev = hci_get_route(addr, BDADDR_ANY, BDADDR_LE_PUBLIC);
	if (!hdev)
		return -ENOENT;

	hci_dev_lock(hdev);
	hcon = hci_conn_hash_lookup_le(hdev, addr, *addr_type);
	hcon = hci_conn_hash_lookup_le(hdev, addr, le_addr_type);
	hci_dev_unlock(hdev);
	hci_dev_put(hdev);

@@ -993,41 +1023,52 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
static void disconnect_all_peers(void)
{
	struct lowpan_btle_dev *entry;
	struct lowpan_peer *peer, *tmp_peer, *new_peer;
	struct list_head peers;

	INIT_LIST_HEAD(&peers);
	struct lowpan_peer *peer;
	int nchans;

	/* We make a separate list of peers as the close_cb() will
	 * modify the device peers list so it is better not to mess
	 * with the same list at the same time.
	/* l2cap_chan_close() cannot be called from RCU, and lock ordering
	 * chan->lock > devices_lock prevents taking write side lock, so copy
	 * then close.
	 */

	rcu_read_lock();
	list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list)
		list_for_each_entry_rcu(peer, &entry->peers, list)
			clear_bit(LOWPAN_PEER_CLOSING, peer->flags);
	rcu_read_unlock();

	do {
		struct l2cap_chan *chans[32];
		int i;

		nchans = 0;

		spin_lock(&devices_lock);

		list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
			list_for_each_entry_rcu(peer, &entry->peers, list) {
			new_peer = kmalloc(sizeof(*new_peer), GFP_ATOMIC);
			if (!new_peer)
				break;
				if (test_and_set_bit(LOWPAN_PEER_CLOSING,
						     peer->flags))
					continue;

			new_peer->chan = peer->chan;
			INIT_LIST_HEAD(&new_peer->list);
				l2cap_chan_hold(peer->chan);
				chans[nchans++] = peer->chan;

			list_add(&new_peer->list, &peers);
				if (nchans >= ARRAY_SIZE(chans))
					goto done;
			}
		}

	rcu_read_unlock();

	spin_lock(&devices_lock);
	list_for_each_entry_safe(peer, tmp_peer, &peers, list) {
		l2cap_chan_close(peer->chan, ENOENT);
done:
		spin_unlock(&devices_lock);

		list_del_rcu(&peer->list);
		kfree_rcu(peer, rcu);
		for (i = 0; i < nchans; ++i) {
			l2cap_chan_lock(chans[i]);
			l2cap_chan_close(chans[i], ENOENT);
			l2cap_chan_unlock(chans[i]);
			l2cap_chan_put(chans[i]);
		}
	spin_unlock(&devices_lock);
	} while (nchans);
}

struct set_enable {
@@ -1050,7 +1091,9 @@ static void do_enable_set(struct work_struct *work)

	mutex_lock(&set_lock);
	if (listen_chan) {
		l2cap_chan_lock(listen_chan);
		l2cap_chan_close(listen_chan, 0);
		l2cap_chan_unlock(listen_chan);
		l2cap_chan_put(listen_chan);
	}

@@ -1103,13 +1146,15 @@ static ssize_t lowpan_control_write(struct file *fp,
	buf[buf_size] = '\0';

	if (memcmp(buf, "connect ", 8) == 0) {
		ret = get_l2cap_conn(&buf[8], &addr, &addr_type, &conn);
		ret = get_l2cap_conn(&buf[8], &addr, &addr_type, &conn, false);
		if (ret == -EINVAL)
			return ret;

		mutex_lock(&set_lock);
		if (listen_chan) {
			l2cap_chan_lock(listen_chan);
			l2cap_chan_close(listen_chan, 0);
			l2cap_chan_unlock(listen_chan);
			l2cap_chan_put(listen_chan);
			listen_chan = NULL;
		}
@@ -1140,7 +1185,7 @@ static ssize_t lowpan_control_write(struct file *fp,
	}

	if (memcmp(buf, "disconnect ", 11) == 0) {
		ret = get_l2cap_conn(&buf[11], &addr, &addr_type, &conn);
		ret = get_l2cap_conn(&buf[11], &addr, &addr_type, &conn, true);
		if (ret < 0)
			return ret;

@@ -1271,7 +1316,9 @@ static void __exit bt_6lowpan_exit(void)
	debugfs_remove(lowpan_control_debugfs);

	if (listen_chan) {
		l2cap_chan_lock(listen_chan);
		l2cap_chan_close(listen_chan, 0);
		l2cap_chan_unlock(listen_chan);
		l2cap_chan_put(listen_chan);
	}

+19 −14
Original line number Diff line number Diff line
@@ -769,21 +769,23 @@ static void find_bis(struct hci_conn *conn, void *data)
	d->count++;
}

static int hci_le_big_terminate(struct hci_dev *hdev, u8 big, struct hci_conn *conn)
static int hci_le_big_terminate(struct hci_dev *hdev, struct hci_conn *conn)
{
	struct iso_list_data *d;
	int ret;

	bt_dev_dbg(hdev, "big 0x%2.2x sync_handle 0x%4.4x", big, conn->sync_handle);
	bt_dev_dbg(hdev, "hcon %p big 0x%2.2x sync_handle 0x%4.4x", conn,
		   conn->iso_qos.bcast.big, conn->sync_handle);

	d = kzalloc(sizeof(*d), GFP_KERNEL);
	if (!d)
		return -ENOMEM;

	d->big = big;
	d->big = conn->iso_qos.bcast.big;
	d->sync_handle = conn->sync_handle;

	if (test_and_clear_bit(HCI_CONN_PA_SYNC, &conn->flags)) {
	if (conn->type == PA_LINK &&
	    test_and_clear_bit(HCI_CONN_PA_SYNC, &conn->flags)) {
		hci_conn_hash_list_flag(hdev, find_bis, PA_LINK,
					HCI_CONN_PA_SYNC, d);

@@ -801,6 +803,9 @@ static int hci_le_big_terminate(struct hci_dev *hdev, u8 big, struct hci_conn *c
			d->big_sync_term = true;
	}

	if (!d->pa_sync_term && !d->big_sync_term)
		return 0;

	ret = hci_cmd_sync_queue(hdev, big_terminate_sync, d,
				 terminate_big_destroy);
	if (ret)
@@ -852,8 +857,7 @@ static void bis_cleanup(struct hci_conn *conn)

		hci_le_terminate_big(hdev, conn);
	} else {
		hci_le_big_terminate(hdev, conn->iso_qos.bcast.big,
				     conn);
		hci_le_big_terminate(hdev, conn);
	}
}

@@ -994,19 +998,20 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t
		conn->mtu = hdev->le_mtu ? hdev->le_mtu : hdev->acl_mtu;
		break;
	case CIS_LINK:
	case BIS_LINK:
	case PA_LINK:
		/* conn->src should reflect the local identity address */
		hci_copy_identity_address(hdev, &conn->src, &conn->src_type);

		/* set proper cleanup function */
		if (!bacmp(dst, BDADDR_ANY))
			conn->cleanup = bis_cleanup;
		else if (conn->role == HCI_ROLE_MASTER)
		if (conn->role == HCI_ROLE_MASTER)
			conn->cleanup = cis_cleanup;

		conn->mtu = hdev->iso_mtu ? hdev->iso_mtu :
			    hdev->le_mtu ? hdev->le_mtu : hdev->acl_mtu;
		conn->mtu = hdev->iso_mtu;
		break;
	case PA_LINK:
	case BIS_LINK:
		/* conn->src should reflect the local identity address */
		hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
		conn->cleanup = bis_cleanup;
		conn->mtu = hdev->iso_mtu;
		break;
	case SCO_LINK:
		if (lmp_esco_capable(hdev))
Loading