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

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

 - hci_sock: Prevent race in socket write iter and sock bind
 - hci_core: Fix triggering cmd_timer for HCI_OP_NOP
 - hci_core: lookup hci_conn on RX path on protocol side
 - SMP: Fix not generating mackey and ltk when repairing
 - btusb: mediatek: Fix kernel crash when releasing mtk iso interface
 - btusb: mediatek: Avoid btusb_mtk_claim_iso_intf() NULL deref

* tag 'for-net-2025-11-21' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth:
  Bluetooth: SMP: Fix not generating mackey and ltk when repairing
  Bluetooth: btusb: mediatek: Avoid btusb_mtk_claim_iso_intf() NULL deref
  Bluetooth: hci_core: lookup hci_conn on RX path on protocol side
  Bluetooth: hci_sock: Prevent race in socket write iter and sock bind
  Bluetooth: hci_core: Fix triggering cmd_timer for HCI_OP_NOP
  Bluetooth: btusb: mediatek: Fix kernel crash when releasing mtk iso interface
====================

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


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents ec3803b5 545d7827
Loading
Loading
Loading
Loading
+32 −7
Original line number Diff line number Diff line
@@ -2711,9 +2711,21 @@ static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb)

static void btusb_mtk_claim_iso_intf(struct btusb_data *data)
{
	struct btmtk_data *btmtk_data = hci_get_priv(data->hdev);
	struct btmtk_data *btmtk_data;
	int err;

	if (!data->hdev)
		return;

	btmtk_data = hci_get_priv(data->hdev);
	if (!btmtk_data)
		return;

	if (!btmtk_data->isopkt_intf) {
		bt_dev_err(data->hdev, "Can't claim NULL iso interface");
		return;
	}

	/*
	 * The function usb_driver_claim_interface() is documented to need
	 * locks held if it's not called from a probe routine. The code here
@@ -2735,17 +2747,30 @@ static void btusb_mtk_claim_iso_intf(struct btusb_data *data)

static void btusb_mtk_release_iso_intf(struct hci_dev *hdev)
{
	struct btmtk_data *btmtk_data = hci_get_priv(hdev);
	struct btmtk_data *btmtk_data;

	if (!hdev)
		return;

	btmtk_data = hci_get_priv(hdev);
	if (!btmtk_data)
		return;

	if (test_bit(BTMTK_ISOPKT_OVER_INTR, &btmtk_data->flags)) {
		usb_kill_anchored_urbs(&btmtk_data->isopkt_anchor);
		clear_bit(BTMTK_ISOPKT_RUNNING, &btmtk_data->flags);

		if (btmtk_data->isopkt_skb) {
			dev_kfree_skb_irq(btmtk_data->isopkt_skb);
			btmtk_data->isopkt_skb = NULL;
		}

		if (btmtk_data->isopkt_intf) {
			usb_set_intfdata(btmtk_data->isopkt_intf, NULL);
			usb_driver_release_interface(&btusb_driver,
						     btmtk_data->isopkt_intf);
			btmtk_data->isopkt_intf = NULL;
		}
	}

	clear_bit(BTMTK_ISOPKT_OVER_INTR, &btmtk_data->flags);
+14 −7
Original line number Diff line number Diff line
@@ -749,7 +749,6 @@ struct hci_conn {

	__u8		remote_cap;
	__u8		remote_auth;
	__u8		remote_id;

	unsigned int	sent;

@@ -857,11 +856,12 @@ extern struct mutex hci_cb_list_lock;
/* ----- HCI interface to upper protocols ----- */
int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr);
int l2cap_disconn_ind(struct hci_conn *hcon);
void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags);
int l2cap_recv_acldata(struct hci_dev *hdev, u16 handle, struct sk_buff *skb,
		       u16 flags);

#if IS_ENABLED(CONFIG_BT_BREDR)
int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags);
void sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb);
int sco_recv_scodata(struct hci_dev *hdev, u16 handle, struct sk_buff *skb);
#else
static inline int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
				  __u8 *flags)
@@ -869,23 +869,30 @@ static inline int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
	return 0;
}

static inline void sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb)
static inline int sco_recv_scodata(struct hci_dev *hdev, u16 handle,
				   struct sk_buff *skb)
{
	kfree_skb(skb);
	return -ENOENT;
}
#endif

#if IS_ENABLED(CONFIG_BT_LE)
int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags);
void iso_recv(struct hci_conn *hcon, struct sk_buff *skb, u16 flags);
int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb,
	     u16 flags);
#else
static inline int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
				  __u8 *flags)
{
	return 0;
}
static inline void iso_recv(struct hci_conn *hcon, struct sk_buff *skb,
			    u16 flags)

static inline int iso_recv(struct hci_dev *hdev, u16 handle,
			   struct sk_buff *skb, u16 flags)
{
	kfree_skb(skb);
	return -ENOENT;
}
#endif

+37 −52
Original line number Diff line number Diff line
@@ -3832,13 +3832,14 @@ static void hci_tx_work(struct work_struct *work)
static void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb)
{
	struct hci_acl_hdr *hdr;
	struct hci_conn *conn;
	__u16 handle, flags;
	int err;

	hdr = skb_pull_data(skb, sizeof(*hdr));
	if (!hdr) {
		bt_dev_err(hdev, "ACL packet too small");
		goto drop;
		kfree_skb(skb);
		return;
	}

	handle = __le16_to_cpu(hdr->handle);
@@ -3850,36 +3851,27 @@ static void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb)

	hdev->stat.acl_rx++;

	hci_dev_lock(hdev);
	conn = hci_conn_hash_lookup_handle(hdev, handle);
	hci_dev_unlock(hdev);

	if (conn) {
		hci_conn_enter_active_mode(conn, BT_POWER_FORCE_ACTIVE_OFF);

		/* Send to upper protocol */
		l2cap_recv_acldata(conn, skb, flags);
		return;
	} else {
	err = l2cap_recv_acldata(hdev, handle, skb, flags);
	if (err == -ENOENT)
		bt_dev_err(hdev, "ACL packet for unknown connection handle %d",
			   handle);
	}

drop:
	kfree_skb(skb);
	else if (err)
		bt_dev_dbg(hdev, "ACL packet recv for handle %d failed: %d",
			   handle, err);
}

/* SCO data packet */
static void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb)
{
	struct hci_sco_hdr *hdr;
	struct hci_conn *conn;
	__u16 handle, flags;
	int err;

	hdr = skb_pull_data(skb, sizeof(*hdr));
	if (!hdr) {
		bt_dev_err(hdev, "SCO packet too small");
		goto drop;
		kfree_skb(skb);
		return;
	}

	handle = __le16_to_cpu(hdr->handle);
@@ -3891,34 +3883,28 @@ static void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb)

	hdev->stat.sco_rx++;

	hci_dev_lock(hdev);
	conn = hci_conn_hash_lookup_handle(hdev, handle);
	hci_dev_unlock(hdev);

	if (conn) {
		/* Send to upper protocol */
	hci_skb_pkt_status(skb) = flags & 0x03;
		sco_recv_scodata(conn, skb);
		return;
	} else {

	err = sco_recv_scodata(hdev, handle, skb);
	if (err == -ENOENT)
		bt_dev_err_ratelimited(hdev, "SCO packet for unknown connection handle %d",
				       handle);
	}

drop:
	kfree_skb(skb);
	else if (err)
		bt_dev_dbg(hdev, "SCO packet recv for handle %d failed: %d",
			   handle, err);
}

static void hci_isodata_packet(struct hci_dev *hdev, struct sk_buff *skb)
{
	struct hci_iso_hdr *hdr;
	struct hci_conn *conn;
	__u16 handle, flags;
	int err;

	hdr = skb_pull_data(skb, sizeof(*hdr));
	if (!hdr) {
		bt_dev_err(hdev, "ISO packet too small");
		goto drop;
		kfree_skb(skb);
		return;
	}

	handle = __le16_to_cpu(hdr->handle);
@@ -3928,22 +3914,13 @@ static void hci_isodata_packet(struct hci_dev *hdev, struct sk_buff *skb)
	bt_dev_dbg(hdev, "len %d handle 0x%4.4x flags 0x%4.4x", skb->len,
		   handle, flags);

	hci_dev_lock(hdev);
	conn = hci_conn_hash_lookup_handle(hdev, handle);
	hci_dev_unlock(hdev);

	if (!conn) {
	err = iso_recv(hdev, handle, skb, flags);
	if (err == -ENOENT)
		bt_dev_err(hdev, "ISO packet for unknown connection handle %d",
			   handle);
		goto drop;
	}

	/* Send to upper protocol */
	iso_recv(conn, skb, flags);
	return;

drop:
	kfree_skb(skb);
	else if (err)
		bt_dev_dbg(hdev, "ISO packet recv for handle %d failed: %d",
			   handle, err);
}

static bool hci_req_is_complete(struct hci_dev *hdev)
@@ -4121,7 +4098,7 @@ static void hci_rx_work(struct work_struct *work)
	}
}

static void hci_send_cmd_sync(struct hci_dev *hdev, struct sk_buff *skb)
static int hci_send_cmd_sync(struct hci_dev *hdev, struct sk_buff *skb)
{
	int err;

@@ -4133,16 +4110,19 @@ static void hci_send_cmd_sync(struct hci_dev *hdev, struct sk_buff *skb)
	if (!hdev->sent_cmd) {
		skb_queue_head(&hdev->cmd_q, skb);
		queue_work(hdev->workqueue, &hdev->cmd_work);
		return;
		return -EINVAL;
	}

	if (hci_skb_opcode(skb) != HCI_OP_NOP) {
		err = hci_send_frame(hdev, skb);
		if (err < 0) {
			hci_cmd_sync_cancel_sync(hdev, -err);
			return;
			return err;
		}
		atomic_dec(&hdev->cmd_cnt);
	} else {
		err = -ENODATA;
		kfree_skb(skb);
	}

	if (hdev->req_status == HCI_REQ_PEND &&
@@ -4150,12 +4130,15 @@ static void hci_send_cmd_sync(struct hci_dev *hdev, struct sk_buff *skb)
		kfree_skb(hdev->req_skb);
		hdev->req_skb = skb_clone(hdev->sent_cmd, GFP_KERNEL);
	}

	return err;
}

static void hci_cmd_work(struct work_struct *work)
{
	struct hci_dev *hdev = container_of(work, struct hci_dev, cmd_work);
	struct sk_buff *skb;
	int err;

	BT_DBG("%s cmd_cnt %d cmd queued %d", hdev->name,
	       atomic_read(&hdev->cmd_cnt), skb_queue_len(&hdev->cmd_q));
@@ -4166,7 +4149,9 @@ static void hci_cmd_work(struct work_struct *work)
		if (!skb)
			return;

		hci_send_cmd_sync(hdev, skb);
		err = hci_send_cmd_sync(hdev, skb);
		if (err)
			return;

		rcu_read_lock();
		if (test_bit(HCI_RESET, &hdev->flags) ||
+2 −0
Original line number Diff line number Diff line
@@ -1311,7 +1311,9 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
			goto done;
		}

		hci_dev_lock(hdev);
		mgmt_index_removed(hdev);
		hci_dev_unlock(hdev);

		err = hci_dev_open(hdev->id);
		if (err) {
+25 −5
Original line number Diff line number Diff line
@@ -2314,14 +2314,31 @@ static void iso_disconn_cfm(struct hci_conn *hcon, __u8 reason)
	iso_conn_del(hcon, bt_to_errno(reason));
}

void iso_recv(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags)
{
	struct iso_conn *conn = hcon->iso_data;
	struct hci_conn *hcon;
	struct iso_conn *conn;
	struct skb_shared_hwtstamps *hwts;
	__u16 pb, ts, len, sn;

	if (!conn)
		goto drop;
	hci_dev_lock(hdev);

	hcon = hci_conn_hash_lookup_handle(hdev, handle);
	if (!hcon) {
		hci_dev_unlock(hdev);
		kfree_skb(skb);
		return -ENOENT;
	}

	conn = iso_conn_hold_unless_zero(hcon->iso_data);
	hcon = NULL;

	hci_dev_unlock(hdev);

	if (!conn) {
		kfree_skb(skb);
		return -EINVAL;
	}

	pb     = hci_iso_flags_pb(flags);
	ts     = hci_iso_flags_ts(flags);
@@ -2377,7 +2394,7 @@ void iso_recv(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
			hci_skb_pkt_status(skb) = flags & 0x03;
			hci_skb_pkt_seqnum(skb) = sn;
			iso_recv_frame(conn, skb);
			return;
			goto done;
		}

		if (pb == ISO_SINGLE) {
@@ -2455,6 +2472,9 @@ void iso_recv(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)

drop:
	kfree_skb(skb);
done:
	iso_conn_put(conn);
	return 0;
}

static struct hci_cb iso_cb = {
Loading