summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2026-09-09 12:51:52 -0700
committerJakub Kicinski <kuba@kernel.org>2026-09-09 12:51:52 -0700
commitbbee0759d363d1297d08b285acfec999570158a6 (patch)
tree33f238e4891ecaed91dfdc8e66c83cb577ba843f
parenta401a9d547c50ef34db1088cc1fb9a201a7af657 (diff)
parentc93922dd316b7273a8667d29084632066fa8a2d3 (diff)
downloadlinux-next-bbee0759d363d1297d08b285acfec999570158a6.tar.gz
linux-next-bbee0759d363d1297d08b285acfec999570158a6.zip
Merge tag 'for-net-2026-09-08' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth
Luiz Augusto von Dentz says: ==================== bluetooth pull request for net: Core: - hci_sysfs: Fix NULL pointer dereference in device_del() - hci_sync: Fix not setting CE length properly - btqcomsmd: destroy RPMsg endpoints before freeing hci_dev Drivers: - btmtk: Declare MT7920 (MT7961 1a) Bluetooth firmware - btusb: mediatek: Fix leaked runtime PM reference in reset - btusb: Fix leaked runtime PM reference in btusb_reset - btusb: Fix UAF of btusb_data by rx_work - btusb: Properly disable remote wakeup for MT7922/MT7925 on Ryzen platform - btintel_pcie: validate packet_len before skb_put_data - btintel_pcie: fix tx_handle bounds off-by-one - btrtl: Don't leak return code when parsing firmware format v2 * tag 'for-net-2026-09-08' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth: Bluetooth: btusb: Fix leaked runtime PM reference in btusb_reset Bluetooth: btusb: mediatek: Fix leaked runtime PM reference in reset Bluetooth: btqcomsmd: destroy RPMsg endpoints before freeing hci_dev Bluetooth: hci_sysfs: Fix NULL pointer dereference in device_del() Bluetooth: btmtk: Declare MT7920 (MT7961 1a) Bluetooth firmware Bluetooth: hci_sync: Fix not setting CE length properly Bluetooth: btintel_pcie: fix tx_handle bounds off-by-one Bluetooth: btintel_pcie: validate packet_len before skb_put_data Bluetooth: btrtl: Don't leak return code when parsing firmware format v2 Bluetooth: btusb: Fix UAF of btusb_data by rx_work Bluetooth: Properly disable remote wakeup for MT7922/MT7925 on Ryzen platform ==================== Link: https://patch.msgid.link/20260908212127.1022197-1-luiz.dentz@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-rw-r--r--drivers/bluetooth/btintel_pcie.c6
-rw-r--r--drivers/bluetooth/btmtk.c11
-rw-r--r--drivers/bluetooth/btmtk.h1
-rw-r--r--drivers/bluetooth/btqcomsmd.c6
-rw-r--r--drivers/bluetooth/btrtl.c5
-rw-r--r--drivers/bluetooth/btusb.c105
-rw-r--r--net/bluetooth/hci_sync.c38
-rw-r--r--net/bluetooth/hci_sysfs.c17
8 files changed, 159 insertions, 30 deletions
diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index eec95e5f3dbb..6d9649776ae7 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -1099,7 +1099,7 @@ static void btintel_pcie_msix_tx_handle(struct btintel_pcie_data *data)
urbd0 = &txq->urbd0s[cr_tia];
- if (urbd0->tfd_index > txq->count)
+ if (urbd0->tfd_index >= txq->count)
return;
cr_tia = (cr_tia + 1) % txq->count;
@@ -1599,7 +1599,9 @@ static int btintel_pcie_submit_rx_work(struct btintel_pcie_data *data, u8 status
rfh_hdr = buf;
len = rfh_hdr->packet_len;
- if (len <= 0) {
+ if (len == 0 || len > BTINTEL_PCIE_BUFFER_SIZE - sizeof(*rfh_hdr)) {
+ bt_dev_err(data->hdev, "Invalid packet_len %d (max %zu)", len,
+ BTINTEL_PCIE_BUFFER_SIZE - sizeof(*rfh_hdr));
ret = -EINVAL;
goto resubmit;
}
diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
index c0ed51567ed4..26d525acd659 100644
--- a/drivers/bluetooth/btmtk.c
+++ b/drivers/bluetooth/btmtk.c
@@ -1374,16 +1374,6 @@ int btmtk_usb_setup(struct hci_dev *hdev)
break;
case 0x7922:
case 0x7925:
- /*
- * A remote wakeup could cause the device completely unresponsive, and
- * recovering from such a state needs a power cycle.
- *
- * Since the remote wakeup capability is super broken, just disable it
- * to get rid of the troubles. The device can still be autosuspended
- * when the bluetooth interface is closed.
- */
- device_set_wakeup_capable(&btmtk_data->udev->dev, false);
- fallthrough;
case 0x7961:
case 0x7902:
case 0x6639:
@@ -1587,5 +1577,6 @@ MODULE_FIRMWARE(FIRMWARE_MT7663);
MODULE_FIRMWARE(FIRMWARE_MT7668);
MODULE_FIRMWARE(FIRMWARE_MT7922);
MODULE_FIRMWARE(FIRMWARE_MT7961);
+MODULE_FIRMWARE(FIRMWARE_MT7920);
MODULE_FIRMWARE(FIRMWARE_MT7925);
MODULE_FIRMWARE(FIRMWARE_MT7927);
diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h
index c83c24897c95..bc26148ec544 100644
--- a/drivers/bluetooth/btmtk.h
+++ b/drivers/bluetooth/btmtk.h
@@ -7,6 +7,7 @@
#define FIRMWARE_MT7922 "mediatek/BT_RAM_CODE_MT7922_1_1_hdr.bin"
#define FIRMWARE_MT7902 "mediatek/BT_RAM_CODE_MT7902_1_1_hdr.bin"
#define FIRMWARE_MT7961 "mediatek/BT_RAM_CODE_MT7961_1_2_hdr.bin"
+#define FIRMWARE_MT7920 "mediatek/BT_RAM_CODE_MT7961_1a_2_hdr.bin"
#define FIRMWARE_MT7925 "mediatek/mt7925/BT_RAM_CODE_MT7925_1_1_hdr.bin"
#define FIRMWARE_MT7927 "mediatek/mt7927/BT_RAM_CODE_MT6639_2_1_hdr.bin"
diff --git a/drivers/bluetooth/btqcomsmd.c b/drivers/bluetooth/btqcomsmd.c
index d2e13fcb6bab..d669ea4eb3eb 100644
--- a/drivers/bluetooth/btqcomsmd.c
+++ b/drivers/bluetooth/btqcomsmd.c
@@ -188,7 +188,10 @@ static int btqcomsmd_probe(struct platform_device *pdev)
return 0;
hci_free_dev:
+ rpmsg_destroy_ept(btq->cmd_channel);
+ rpmsg_destroy_ept(btq->acl_channel);
hci_free_dev(hdev);
+ return ret;
destroy_cmd_channel:
rpmsg_destroy_ept(btq->cmd_channel);
destroy_acl_channel:
@@ -202,10 +205,11 @@ static void btqcomsmd_remove(struct platform_device *pdev)
struct btqcomsmd *btq = platform_get_drvdata(pdev);
hci_unregister_dev(btq->hdev);
- hci_free_dev(btq->hdev);
rpmsg_destroy_ept(btq->cmd_channel);
rpmsg_destroy_ept(btq->acl_channel);
+
+ hci_free_dev(btq->hdev);
}
static const struct of_device_id btqcomsmd_of_match[] = {
diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index 03fa9409e3ee..d29813331603 100644
--- a/drivers/bluetooth/btrtl.c
+++ b/drivers/bluetooth/btrtl.c
@@ -591,7 +591,7 @@ static int rtlbt_parse_firmware_v2(struct hci_dev *hdev,
* headers.
*/
if (!key_id)
- break;
+ continue;
rc = btrtl_parse_section(hdev, btrtl_dev, opcode,
ptr, section_len);
break;
@@ -600,8 +600,7 @@ static int rtlbt_parse_firmware_v2(struct hci_dev *hdev,
ptr, section_len);
break;
default:
- rc = 0;
- break;
+ continue;
}
if (rc < 0) {
rtl_dev_err(hdev, "RTL: Parse section (%u) err %d",
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index d70a3e7a13f5..002b9f975710 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -6,6 +6,7 @@
* Copyright (C) 2005-2008 Marcel Holtmann <marcel@holtmann.org>
*/
+#include <linux/cpufeature.h>
#include <linux/dmi.h>
#include <linux/module.h>
#include <linux/usb.h>
@@ -980,6 +981,8 @@ struct btqca_data {
#define BTUSB_USE_ALT3_FOR_WBS 15
#define BTUSB_ALT6_CONTINUOUS_TX 16
#define BTUSB_HW_SSR_ACTIVE 17
+#define BTUSB_WAKEUP_BROKEN 18
+#define BTUSB_RESET 19
struct btusb_data {
struct hci_dev *hdev;
@@ -1054,13 +1057,15 @@ static void btusb_reset(struct hci_dev *hdev)
int err;
data = hci_get_drvdata(hdev);
- /* This is not an unbalanced PM reference since the device will reset */
err = usb_autopm_get_interface(data->intf);
if (err) {
bt_dev_err(hdev, "Failed usb_autopm_get_interface: %d", err);
return;
}
+ if (test_and_set_bit(BTUSB_RESET, &data->flags))
+ usb_autopm_put_interface_no_suspend(data->intf);
+
bt_dev_err(hdev, "Resetting usb device.");
usb_queue_reset_device(data->intf);
}
@@ -2092,18 +2097,24 @@ static int btusb_close(struct hci_dev *hdev)
BT_DBG("%s", hdev->name);
- cancel_delayed_work(&data->rx_work);
cancel_work_sync(&data->work);
cancel_work_sync(&data->waker);
- skb_queue_purge(&data->acl_q);
-
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
clear_bit(BTUSB_BULK_RUNNING, &data->flags);
clear_bit(BTUSB_INTR_RUNNING, &data->flags);
clear_bit(BTUSB_DIAG_RUNNING, &data->flags);
btusb_stop_traffic(data);
+
+ /* rx_work must only be canceled once the URBs that can rearm it are
+ * gone, and it must be canceled synchronously since btusb_disconnect()
+ * frees the btusb_data it dereferences right after hci_unregister_dev().
+ */
+ cancel_delayed_work_sync(&data->rx_work);
+
+ skb_queue_purge(&data->acl_q);
+
btusb_free_frags(data);
err = usb_autopm_get_interface(data->intf);
@@ -2129,7 +2140,7 @@ static int btusb_flush(struct hci_dev *hdev)
BT_DBG("%s", hdev->name);
- cancel_delayed_work(&data->rx_work);
+ cancel_delayed_work_sync(&data->rx_work);
skb_queue_purge(&data->acl_q);
@@ -2923,8 +2934,11 @@ static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data)
}
err = usb_autopm_get_interface(data->intf);
- if (err < 0)
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed usb_autopm_get_interface: %d", err);
+ clear_bit(BTMTK_HW_RESET_ACTIVE, &btmtk_data->flags);
return err;
+ }
/* Release MediaTek ISO data interface */
btusb_mtk_release_iso_intf(hdev);
@@ -2946,6 +2960,11 @@ static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data)
err = btmtk_usb_subsys_reset(hdev, btmtk_data->dev_id);
+ if (test_and_set_bit(BTUSB_RESET, &data->flags)) {
+ bt_dev_err(hdev, "last usb reset failed? Resetting again");
+ usb_autopm_put_interface_no_suspend(data->intf);
+ }
+
usb_queue_reset_device(data->intf);
clear_bit(BTMTK_HW_RESET_ACTIVE, &btmtk_data->flags);
@@ -2969,10 +2988,25 @@ static int btusb_send_frame_mtk(struct hci_dev *hdev, struct sk_buff *skb)
}
}
+static inline bool platform_is_ryzen(void)
+{
+#ifdef CONFIG_X86
+ return boot_cpu_has(X86_FEATURE_ZEN);
+#else
+ return false;
+#endif
+}
+
+static inline bool is_direct_child_of_root_hub(struct usb_device *udev)
+{
+ return udev->parent == udev->bus->root_hub;
+}
+
static int btusb_mtk_setup(struct hci_dev *hdev)
{
struct btusb_data *data = hci_get_drvdata(hdev);
struct btmtk_data *btmtk_data = hci_get_priv(hdev);
+ int err;
/* MediaTek WMT vendor cmd requiring below USB resources to
* complete the handshake.
@@ -2989,7 +3023,40 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
btusb_mtk_claim_iso_intf(data);
}
- return btmtk_usb_setup(hdev);
+ err = btmtk_usb_setup(hdev);
+ if (err)
+ return err;
+
+ switch (btmtk_data->dev_id) {
+ case 0x7922:
+ case 0x7925:
+ /*
+ * All reports seen to be relevant to Ryzen-based laptops. These
+ * NICs are usually used as OEM components thanks to some sort
+ * of reference designs.
+ *
+ * Their popularity on other platforms is unclear. While there
+ * is still a chance that the quirk may exist on other
+ * platforms, be cautious and only apply the quirk to direct
+ * children of Ryzen platforms's root hubs for the time being.
+ *
+ * In most cases the root hub is on the SoC or PCH, which needs
+ * the quirk. Unfortunately, this can't distinguish root hubs on
+ * PCIe add-in cards. Such roughness should be acceptable, as
+ * PCIe USB controller add-in cards are less commonly used
+ * nowadays. On the other hand, applying the quirk doesn't hurt
+ * any functionalities either, as the device can still be used
+ * as a wakeup source if desired.
+ *
+ * Theoretically, we could retrieve the root hub's PCI vendor ID
+ * with some hierarchy magic, but that's too intrusive...
+ */
+ if (platform_is_ryzen() && is_direct_child_of_root_hub(data->udev))
+ set_bit(BTUSB_WAKEUP_BROKEN, &data->flags);
+ break;
+ }
+
+ return 0;
}
static int btusb_mtk_shutdown(struct hci_dev *hdev)
@@ -4540,6 +4607,9 @@ static void btusb_disconnect(struct usb_interface *intf)
if (data->reset_gpio)
gpiod_put(data->reset_gpio);
+ if (test_and_clear_bit(BTUSB_RESET, &data->flags))
+ usb_autopm_put_interface_no_suspend(data->intf);
+
if (intf == data->intf) {
if (data->isoc)
usb_driver_release_interface(&btusb_driver, data->isoc);
@@ -4565,11 +4635,26 @@ static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
BT_DBG("intf %p", intf);
- /* Don't auto-suspend if there are connections or discovery in
- * progress; external suspend calls shall never fail.
+ /*
+ * It is reported that remote wakeup events could sometimes cause some
+ * adapters completely unresponsive. Resetting the xHCI root hub doesn't
+ * help at all, and recovering from such a state needs a power cycle.
+ * Since disabling remote wakeup simply causes the USB core to gate
+ * runtime autosuspend as well due to needs_remote_wakeup == 1, let's do
+ * this ourselves to make our life easier. The interface can be safely
+ * autosuspended as long as remote wakeup is disabled, i.e., after
+ * closing the HCI device.
+ *
+ * Don't auto-suspend if there are connections or discovery in progress.
+ *
+ * External suspend calls shall never fail. Specifically, a device with
+ * broken remote wakeup may still take the advantage of remote wakeup in
+ * order to wake up the system from sleep if userspace has enabled it as
+ * a wakeup source.
*/
if (PMSG_IS_AUTO(message) &&
- (hci_conn_count(data->hdev) || hci_discovery_active(data->hdev)))
+ ((test_bit(BTUSB_WAKEUP_BROKEN, &data->flags) && data->intf->needs_remote_wakeup) ||
+ hci_conn_count(data->hdev) || hci_discovery_active(data->hdev)))
return -EBUSY;
if (data->suspend_count++)
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index ffd7b37e7401..3ab5fa3dce96 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -4797,6 +4797,24 @@ static int hci_le_set_def_rate_sync(struct hci_dev *hdev)
cp.cont_num = cpu_to_le16(0x0001);
cp.supv_timeout = cpu_to_le16(0x000c); /* 120 ms */
+ /* The connection event length recommended in requests by a Peripheral
+ * uses units of 125 us with a valid range of 0x0001 to 0x7CFF
+ * (0.125 ms to 3.999875 s), so 0x0000 cannot be used. Also note that
+ * the Controller is not required to use these values:
+ *
+ * BLUETOOTH CORE SPECIFICATION Version 6.2 | Vol 4, Part E
+ * 7.8.158. LE Set Default Rate Parameters command
+ *
+ * The Min_CE_Length and Max_CE_Length parameters provide the
+ * Controller with the expected minimum and maximum length of the
+ * connection events. The Controller is not required to use these
+ * values.
+ *
+ * So it is safe to just use the minimum.
+ */
+ cp.min_ce_len = cpu_to_le16(0x0001);
+ cp.max_ce_len = cpu_to_le16(0x0001);
+
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_DEF_RATE,
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}
@@ -7467,8 +7485,24 @@ static int hci_le_conn_rate_request_sync(struct hci_dev *hdev, void *data)
cp.max_latency = cpu_to_le16(params->max_latency);
cp.cont_num = cpu_to_le16(params->cont_num);
cp.supv_timeout = cpu_to_le16(params->rate_supv_timeout);
- cp.min_ce_len = cpu_to_le16(0x0000);
- cp.max_ce_len = cpu_to_le16(0x0000);
+
+ /* The connection event length recommended in requests by a Peripheral
+ * uses units of 125 us with a valid range of 0x0001 to 0x7CFF
+ * (0.125 ms to 3.999875 s), so 0x0000 cannot be used. Also note that
+ * the Controller is not required to use these values:
+ *
+ * BLUETOOTH CORE SPECIFICATION Version 6.2 | Vol 4, Part E
+ * 7.8.157. LE Connection Rate Request command
+ *
+ * The Min_CE_Length and Max_CE_Length parameters provide the
+ * Controller with the expected minimum and maximum length of the
+ * connection events. The Controller is not required to use these
+ * values.
+ *
+ * So it is safe to just use the minimum.
+ */
+ cp.min_ce_len = cpu_to_le16(0x0001);
+ cp.max_ce_len = cpu_to_le16(0x0001);
hci_dev_unlock(hdev);
diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c
index 8957ce7c21b7..c2065abf753e 100644
--- a/net/bluetooth/hci_sysfs.c
+++ b/net/bluetooth/hci_sysfs.c
@@ -13,7 +13,10 @@ static const struct class bt_class = {
static void bt_link_release(struct device *dev)
{
struct hci_conn *conn = to_hci_conn(dev);
+ struct device *parent = dev->parent;
+
kfree(conn);
+ put_device(parent);
}
static const struct device_type bt_link = {
@@ -21,6 +24,16 @@ static const struct device_type bt_link = {
.release = bt_link_release,
};
+/*
+ * The rfcomm tty device will possibly retain even when conn
+ * is down, and sysfs doesn't support move zombie device,
+ * so we should move the device before conn device is destroyed.
+ */
+static int __match_tty(struct device *dev, const void *data)
+{
+ return !strncmp(dev_name(dev), "rfcomm", 6);
+}
+
void hci_conn_init_sysfs(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
@@ -29,7 +42,7 @@ void hci_conn_init_sysfs(struct hci_conn *conn)
conn->dev.type = &bt_link;
conn->dev.class = &bt_class;
- conn->dev.parent = &hdev->dev;
+ conn->dev.parent = get_device(&hdev->dev);
device_initialize(&conn->dev);
}
@@ -69,7 +82,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn)
while (1) {
struct device *dev;
- dev = device_find_any_child(&conn->dev);
+ dev = device_find_child(&conn->dev, NULL, __match_tty);
if (!dev)
break;
device_move(dev, NULL, DPM_ORDER_DEV_LAST);