From dcaf83ead130d3067862599089b0999b3da140a4 Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Wed, 2 Sep 2026 02:19:18 +0800 Subject: [PATCH 01/11] Bluetooth: Properly disable remote wakeup for MT7922/MT7925 on Ryzen platform It is reported that a remote wakeup could cause MT7922/MT7925's btusb interface completely unresponsive. Resetting the xHCI root hub doesn't help at all, and recovering from such a state needs a power cycle. 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... Meanwhile, though device_set_wakeup_capable(false) is the correct fix for other NICs with fake remote wakeup capabilities, doing so for MT7922/MT7925 effectively prevents it from being used as wakeup sources as per userspace requests. Hence, return -EBUSY on runtime suspend to prevent the interface from being autosuspended while it's still opened, which has the same effect as device_set_wakeup_capable(false), since disabling remote wakeup simply causes the USB core to gate runtime autosuspend as well due to needs_remote_wakeup == 1. The interface can be safely autosuspended as long as remote wakeup is disabled, i.e., after closing the HCI device. Specifically, the interface 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. Fixes: e31d761628ad ("Bluetooth: btmtk: Disable remote wakeup for MT7922/MT7925") Tested-by: Rafael Passos Signed-off-by: Rong Zhang Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btmtk.c | 10 ------ drivers/bluetooth/btusb.c | 73 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 69 insertions(+), 14 deletions(-) diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c index c0ed51567ed4..9589caff925d 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: diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index d70a3e7a13f5..95f4640c60e4 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -6,6 +6,7 @@ * Copyright (C) 2005-2008 Marcel Holtmann */ +#include #include #include #include @@ -980,6 +981,7 @@ 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 struct btusb_data { struct hci_dev *hdev; @@ -2969,10 +2971,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 +3006,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) @@ -4565,11 +4615,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++) From 1c12c3117639e78940959d956519c758c57d0849 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 31 Aug 2026 12:13:10 -0400 Subject: [PATCH 02/11] Bluetooth: btusb: Fix UAF of btusb_data by rx_work btusb_close() and btusb_flush() cancel data->rx_work with the asynchronous cancel_delayed_work(), so if btusb_rx_work() is already running on another CPU it keeps running after the cancel returns. btusb_disconnect() calls hci_unregister_dev(), which invokes btusb_close(), and then frees the btusb_data. A still running btusb_rx_work() then dereferences the freed data: while ((skb = skb_dequeue(&data->acl_q))) data->recv_acl(data->hdev, skb); Use cancel_delayed_work_sync() instead. In btusb_close() the cancel also has to happen after btusb_stop_traffic(), otherwise an URB completion racing with the cancel can requeue the work right after it has been waited for. Fixes: 800fe5ec302e ("Bluetooth: btusb: Add support for queuing during polling interval") Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 95f4640c60e4..ddc44ca28722 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -2094,18 +2094,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); @@ -2131,7 +2137,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); From 83e3e515fd261600ed8491fb0a8bcdfb115c904e Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Thu, 3 Sep 2026 03:18:59 +0800 Subject: [PATCH 03/11] Bluetooth: btrtl: Don't leak return code when parsing firmware format v2 When key_id from chip is zero, rtlbt_parse_firmware_v2() intentionally ignores all security headers. However, the implementation simply breaks from a switch statement and leaks uninitialized return code `rc' (if the first section is a security one) or the previous section's `rc'. Fix it by really skipping a loop with `continue'. For consistency and readability, also do the same for the default case. Fixes: 9a24ce5e29b1 ("Bluetooth: btrtl: Firmware format v2 support") Cc: stable@vger.kernel.org Signed-off-by: Rong Zhang Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btrtl.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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", From 6436e1b5331b1aebf905c13e0880a37032719b75 Mon Sep 17 00:00:00 2001 From: Kiran K Date: Thu, 3 Sep 2026 20:21:01 +0530 Subject: [PATCH 04/11] Bluetooth: btintel_pcie: validate packet_len before skb_put_data btintel_pcie_submit_rx_work() reads packet_len from rfh_hdr without checking if it exceeds the RX buffer size. An oversized packet_len can lead to an out-of-bounds read in skb_put_data(). Validate packet_len to ensure it is non-zero and does not exceed BTINTEL_PCIE_BUFFER_SIZE - sizeof(*rfh_hdr), logging an error when invalid. This issue was reported by Claude Mythos. It can be simulated either by using customized firmware configured to return an invalid packet_len or by modifying rfh_hdr->packet_len in the driver before calling btintel_pcie_submit_rx_work(). Fixes: c2b636b3f788 ("Bluetooth: btintel_pcie: Add support for PCIe transport") Signed-off-by: Kiran K Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btintel_pcie.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index eec95e5f3dbb..968608932fb7 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -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; } From 3dd1b41f96aad08444be1b7626c89de2b9f2abd4 Mon Sep 17 00:00:00 2001 From: Kiran K Date: Thu, 3 Sep 2026 20:21:02 +0530 Subject: [PATCH 05/11] Bluetooth: btintel_pcie: fix tx_handle bounds off-by-one Valid indices into txq->urbd0s/tfds/bufs are 0..txq->count-1, so tfd_index == txq->count is already out of range. Change the guard in btintel_pcie_msix_tx_handle() from '> txq->count' to '>= txq->count'. This issue was reported by Claude Mythos. Fixes: c2b636b3f788 ("Bluetooth: btintel_pcie: Add support for PCIe transport") Signed-off-by: Kiran K Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btintel_pcie.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index 968608932fb7..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; From 3621f78d43b0a9563d5ade68370434c532eea259 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 2 Sep 2026 13:16:26 -0400 Subject: [PATCH 06/11] Bluetooth: hci_sync: Fix not setting CE length properly Both hci_le_set_def_rate_sync() and hci_le_conn_rate_request_sync() were leaving Min_CE_Length and Max_CE_Length set to 0x0000, but the connection event length recommended in requests by a Peripheral has a valid range of 0x0001 to 0x7CFF (Time = N * 125 us, Time Range: 0.125 ms to 3.999875 s), so 0x0000 cannot be used. Set both to the minimum valid value, which is safe since 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 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. Fixes: 2f8784cfe8a9 ("Bluetooth: Add support for Shorter Connection Interval (SCI) feature") Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) 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); From 3d8a8e81ea8ad8813d4c82a12ba53ecb597b217d Mon Sep 17 00:00:00 2001 From: Ivan Hu Date: Fri, 4 Sep 2026 13:30:07 +0800 Subject: [PATCH 07/11] Bluetooth: btmtk: Declare MT7920 (MT7961 1a) Bluetooth firmware btmtk_fw_get_filename() constructs the firmware name at runtime, so for the MT7920 variant (dev_id 0x7961 with fw_flavor set) it requests "mediatek/BT_RAM_CODE_MT7961_1a_2_hdr.bin" without ever declaring it via MODULE_FIRMWARE(). Tools that select firmware from module metadata (e.g. "modinfo -F firmware") therefore omit this blob, so request_firmware() fails and Bluetooth does not initialise on MT7920, even though the file is present in linux-firmware. Declare it with MODULE_FIRMWARE(), as the mt76 driver already does for the corresponding MT7920 wifi firmware. Fixes: 1cb63d80fff6 ("Bluetooth: btusb: Add support Mediatek MT7920") Signed-off-by: Ivan Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btmtk.c | 1 + drivers/bluetooth/btmtk.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c index 9589caff925d..26d525acd659 100644 --- a/drivers/bluetooth/btmtk.c +++ b/drivers/bluetooth/btmtk.c @@ -1577,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" From 9b851b09b392da68bd715601f10a5adb2d8d19b8 Mon Sep 17 00:00:00 2001 From: Krystian Kaniewski Date: Fri, 4 Sep 2026 12:24:22 +0000 Subject: [PATCH 08/11] Bluetooth: hci_sysfs: Fix NULL pointer dereference in device_del() A NULL pointer dereference in klist_put() occurs when a child device (such as a BNEP network device in bnep_session) is concurrently being unregistered while hci_conn_del_sysfs() reparents child devices. This is caused by a race condition between hci_conn_del_sysfs() and concurrent child device unregistration (e.g. bnep_session calling unregister_netdev()). During device unregistration, device_del() snapshots a non-NULL parent pointer. Concurrently, hci_conn_del_sysfs() finds the child device using device_find_any_child() and calls device_move() to reparent it to NULL, which removes the node from its parent's klist and clears knode_parent. Subsequently, device_del() calls klist_del(&dev->p->knode_parent) using the stale parent snapshot, causing klist_put() to dereference knode_klist(n)->put on an already removed node, resulting in a NULL pointer dereference. This race was introduced by commit 27aabf27fd01 ("Bluetooth: fix use-after-free in device_for_each_child()"), which replaced device_find_child(..., __match_tty) with device_find_any_child() in hci_conn_del_sysfs(). That change was intended to avoid a use-after-free where conn->dev outlived its parent hdev->dev when child devices held references to conn->dev, because conn->dev only held a reference to hdev->dev while registered in sysfs. Fix the issue properly by taking an explicit reference to the parent device with get_device(&hdev->dev) in hci_conn_init_sysfs() and dropping it with put_device(parent) in bt_link_release() when the conn device is freed. This ensures that hdev->dev remains valid for the entire lifecycle of conn->dev, resolving the underlying use-after-free. With the parent reference held properly, restore the __match_tty filter in hci_conn_del_sysfs() so that device_move() is only invoked on persistent RFCOMM TTY devices as originally intended, eliminating the race condition with unregistering network devices. Fixes: 27aabf27fd01 ("Bluetooth: fix use-after-free in device_for_each_child()") Assisted-by: Gemini:gemini-3.7-flash syzbot Reported-by: syzbot+6df45dd3d03e1a9aca96@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=6df45dd3d03e1a9aca96 Link: https://syzkaller.appspot.com/ai_job?id=f1c0e740-db21-40af-a9ff-84db0fd8b8bd Signed-off-by: Krystian Kaniewski Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sysfs.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) 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); From f5a427b16e45210dee656b0860728f3d496dee85 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Fri, 4 Sep 2026 10:54:57 +0800 Subject: [PATCH 09/11] Bluetooth: btqcomsmd: destroy RPMsg endpoints before freeing hci_dev The command and ACL RPMsg endpoints store struct btqcomsmd as their callback private data. The receive callbacks dereference btq->hdev without taking an hci_dev reference. The current teardown order frees the hci_dev before destroying the RPMsg endpoints in both the hci_register_dev() error path and the driver remove path. If WCNSS delivers data in that window, the endpoint callback can run with an already freed hci_dev and pass it to the Bluetooth core. For qcom_smd endpoints, rpmsg_destroy_ept() closes the channel and clears the callback under the channel recv_lock. The receive path holds the same lock while invoking the callback, so destroying the endpoints first both prevents new callbacks and serializes with any callback already running. Destroy the command and ACL endpoints before hci_free_dev(). Keep hci_unregister_dev() first during remove so the HCI core stops issuing operations before the transport endpoints are shut down. In the full registration-error cleanup path, return directly after freeing the hci_dev to avoid falling through to the partial-construction labels and destroying the endpoints twice. Fixes: 5052de8deff5 ("soc: qcom: smd: Transition client drivers from smd to rpmsg") Fixes: 9a39a927be01 ("Bluetooth: btqcomsmd: Fix a resource leak in error handling paths in the probe function") Cc: stable@vger.kernel.org Acked-by: Bartosz Golaszewski Reviewed-by: Dmitry Baryshkov Signed-off-by: Xu Rao Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btqcomsmd.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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[] = { From e486a891c412d9d82ee865987f4eead6196e1f96 Mon Sep 17 00:00:00 2001 From: Jiajia Liu Date: Fri, 4 Sep 2026 16:03:50 +0800 Subject: [PATCH 10/11] Bluetooth: btusb: mediatek: Fix leaked runtime PM reference in reset MT7925 on HP Pro Mini 260 sometimes timed out during reloading driver and reset usb device. btusb_suspend is not called again after closing bluetooth interface. usbcore: registered new interface driver btusb Bluetooth: hci0: HW/SW Version: 0x00000000, Build Time: 20260605184935 Bluetooth: hci0: Execution of wmt command timed out Bluetooth: hci0: Failed to send wmt patch dwnld (-110) Bluetooth: hci0: Failed to set up firmware (-110) usb 3-10: reset high-speed USB device number 4 using xhci_hcd Bluetooth: hci0: HW/SW Version: 0x00000000, Build Time: 20260605184935 Bluetooth: hci0: Device setup in 1856545 usecs Bluetooth: hci0: AOSP extensions version v1.00 Bluetooth: hci0: AOSP quality report is supported Bluetooth: MGMT ver 1.23 btusb_mtk_reset calls usb_autopm_get_interface to resume the device before driving the hardware reset, but never calls the matching usb_autopm_put_interface. Every hardware reset therefore leaks a PM usage reference of the interface, preventing the device from being runtime suspended again until it is unbound. Add the BTUSB_RESET flag. It is set before usb_queue_reset_device and is cleared in btusb_disconnect, which drops the reference as well. If the flag is already set when a new reset is requested, drop one reference. Also clear BTMTK_HW_RESET_ACTIVE if usb_autopm_get_interface fails, otherwise no further reset could ever be attempted. Fixes: 25b6d7593a3a ("Bluetooth: btmtk: introduce btmtk reset work") Assisted-by: Claude:qwen3.8-max Signed-off-by: Jiajia Liu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index ddc44ca28722..9372fb521575 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -982,6 +982,7 @@ struct btqca_data { #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; @@ -2931,8 +2932,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); @@ -2954,6 +2958,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); @@ -4596,6 +4605,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); From c93922dd316b7273a8667d29084632066fa8a2d3 Mon Sep 17 00:00:00 2001 From: Jiajia Liu Date: Fri, 4 Sep 2026 16:03:51 +0800 Subject: [PATCH 11/11] Bluetooth: btusb: Fix leaked runtime PM reference in btusb_reset btusb_reset calls usb_autopm_get_interface to resume the device before queuing a reset of it, but never calls the matching usb_autopm_put_interface. usb_queue_reset_device ends up in usb_reset_device(), and since btusb provides no pre_reset/post_reset callbacks the interface is merely unbound and rebound: the interface device object survives this cycle, and so does its PM usage count, which is not cleared when the driver is unbound. As a result every reset permanently leaks a PM usage reference, preventing the interface from being runtime suspended again until it is unbound. Set BTUSB_RESET flag before usb_queue_reset_device so that btusb_disconnect drops the reference. If the flag is already set, drop one reference. Fixes: c9209b269afd ("Bluetooth: btusb: Introduce generic USB reset") Assisted-by: Claude:qwen3.8-max Signed-off-by: Jiajia Liu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 9372fb521575..002b9f975710 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1057,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); }