diff options
| author | Thierry Reding <treding@nvidia.com> | 2026-04-30 10:17:35 +0200 |
|---|---|---|
| committer | Thierry Reding <treding@nvidia.com> | 2026-04-30 10:17:35 +0200 |
| commit | 51fc52244bc8a0ab573eef2fe5282dfccaecf431 (patch) | |
| tree | ca447ea9f0840b7788ed484a795c85835179cf5b | |
| parent | 356a194f49f6c9e19f08f6435c23ed18eb828585 (diff) | |
| parent | 516a3ab44299afd2ffd58279cf355c87c0d35754 (diff) | |
| download | linux-next-51fc52244bc8a0ab573eef2fe5282dfccaecf431.tar.gz | |
Merge branch 'master' of https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git
| -rw-r--r-- | drivers/bluetooth/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/bluetooth/btintel.c | 10 | ||||
| -rw-r--r-- | drivers/bluetooth/btintel.h | 7 | ||||
| -rw-r--r-- | drivers/bluetooth/btintel_pcie.c | 216 | ||||
| -rw-r--r-- | drivers/bluetooth/btintel_pcie.h | 9 | ||||
| -rw-r--r-- | drivers/bluetooth/btmtk.c | 38 | ||||
| -rw-r--r-- | drivers/bluetooth/btmtk.h | 7 | ||||
| -rw-r--r-- | drivers/bluetooth/btusb.c | 11 | ||||
| -rw-r--r-- | drivers/bluetooth/hci_ath.c | 3 | ||||
| -rw-r--r-- | drivers/bluetooth/hci_bcsp.c | 3 | ||||
| -rw-r--r-- | drivers/bluetooth/hci_h4.c | 3 | ||||
| -rw-r--r-- | drivers/bluetooth/hci_h5.c | 3 | ||||
| -rw-r--r-- | drivers/bluetooth/virtio_bt.c | 39 | ||||
| -rw-r--r-- | include/net/bluetooth/hci_core.h | 2 | ||||
| -rw-r--r-- | net/bluetooth/6lowpan.c | 25 | ||||
| -rw-r--r-- | net/bluetooth/bnep/core.c | 13 | ||||
| -rw-r--r-- | net/bluetooth/hci_conn.c | 124 | ||||
| -rw-r--r-- | net/bluetooth/hci_event.c | 27 | ||||
| -rw-r--r-- | net/bluetooth/iso.c | 56 | ||||
| -rw-r--r-- | net/bluetooth/l2cap_core.c | 14 | ||||
| -rw-r--r-- | net/bluetooth/l2cap_sock.c | 9 | ||||
| -rw-r--r-- | net/bluetooth/rfcomm/core.c | 7 | ||||
| -rw-r--r-- | net/bluetooth/sco.c | 60 |
23 files changed, 552 insertions, 136 deletions
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index c5d45cf91f882..fc1b37044a9b0 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -502,7 +502,7 @@ config BT_NXPUART config BT_INTEL_PCIE tristate "Intel HCI PCIe driver" - depends on PCI + depends on PCI && ACPI select BT_INTEL select FW_LOADER help diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index dcaaa4ca02b99..5e9cac090bd8f 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -67,9 +67,10 @@ static struct { u32 fw_build_num; } coredump_info; -static const guid_t btintel_guid_dsm = +const guid_t btintel_guid_dsm = GUID_INIT(0xaa10f4e0, 0x81ac, 0x4233, 0xab, 0xf6, 0x3b, 0x2a, 0xc5, 0x0e, 0x28, 0xd9); +EXPORT_SYMBOL_GPL(btintel_guid_dsm); int btintel_check_bdaddr(struct hci_dev *hdev) { @@ -2624,7 +2625,7 @@ static void btintel_set_ppag(struct hci_dev *hdev, struct intel_version_tlv *ver kfree_skb(skb); } -static int btintel_acpi_reset_method(struct hci_dev *hdev) +int btintel_acpi_reset_method(struct hci_dev *hdev) { int ret = 0; acpi_status status; @@ -2632,14 +2633,14 @@ static int btintel_acpi_reset_method(struct hci_dev *hdev) struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; status = acpi_evaluate_object(ACPI_HANDLE(GET_HCIDEV_DEV(hdev)), "_PRR", NULL, &buffer); - if (ACPI_FAILURE(status)) { + if (ACPI_FAILURE(status) || !buffer.pointer) { bt_dev_err(hdev, "Failed to run _PRR method"); ret = -ENODEV; return ret; } p = buffer.pointer; - if (p->package.count != 1 || p->type != ACPI_TYPE_PACKAGE) { + if (p->type != ACPI_TYPE_PACKAGE || p->package.count != 1) { bt_dev_err(hdev, "Invalid arguments"); ret = -EINVAL; goto exit_on_error; @@ -2663,6 +2664,7 @@ exit_on_error: kfree(buffer.pointer); return ret; } +EXPORT_SYMBOL_GPL(btintel_acpi_reset_method); static void btintel_set_dsm_reset_method(struct hci_dev *hdev, struct intel_version_tlv *ver_tlv) diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h index 0e9ca99aaaaea..70d812ad36a22 100644 --- a/drivers/bluetooth/btintel.h +++ b/drivers/bluetooth/btintel.h @@ -79,6 +79,8 @@ struct intel_tlv { #define BTINTEL_HWID_SCP2 0x20 /* Scorpius Peak2 - Nova Lake */ #define BTINTEL_HWID_BZRIW 0x22 /* BlazarIW - Wildcat Lake */ +extern const guid_t btintel_guid_dsm; + struct intel_version_tlv { u32 cnvi_top; u32 cnvr_top; @@ -289,6 +291,7 @@ int btintel_bootloader_setup_tlv(struct hci_dev *hdev, int btintel_shutdown_combined(struct hci_dev *hdev); void btintel_hw_error(struct hci_dev *hdev, u8 code); void btintel_print_fseq_info(struct hci_dev *hdev); +int btintel_acpi_reset_method(struct hci_dev *hdev); #else static inline int btintel_check_bdaddr(struct hci_dev *hdev) @@ -422,4 +425,8 @@ static inline void btintel_hw_error(struct hci_dev *hdev, u8 code) static inline void btintel_print_fseq_info(struct hci_dev *hdev) { } +static inline int btintel_acpi_reset_method(struct hci_dev *hdev) +{ + return -ENODEV; +} #endif diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index 2f59c0d6f9ec4..fda474406003f 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -15,6 +15,7 @@ #include <linux/wait.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/acpi.h> #include <linux/unaligned.h> #include <linux/devcoredump.h> @@ -102,6 +103,22 @@ enum { BTINTEL_PCIE_D3 }; +enum { + BTINTEL_PCIE_DSM_SET_RESET_TIMING = 1, + BTINTEL_PCIE_DSM_GET_RESET_TIMING = 2, + BTINTEL_PCIE_DSM_BT_PLDR_CONFIG = 3, + BTINTEL_PCIE_DSM_GET_RESET_TYPE = 4, + BTINTEL_PCIE_DSM_DYNAMIC_PLDR = 5, + BTINTEL_PCIE_DSM_GET_RESET_METHOD = 6, + BTINTEL_PCIE_DSM_SET_PLDR_DELAY = 7, +}; + +enum btintel_dsm_internal_product_reset_mode { + BTINTEL_PCIE_DSM_PLDR_MODE_EN_PROD_RESET = BIT(0), + BTINTEL_PCIE_DSM_PLDR_MODE_EN_WIFI_FLR = BIT(1), + BTINTEL_PCIE_DSM_PLDR_MODE_EN_BT_OFF_ON = BIT(2), +}; + /* Structure for dbgc fragment buffer * @buf_addr_lsb: LSB of the buffer's physical address * @buf_addr_msb: MSB of the buffer's physical address @@ -128,11 +145,6 @@ struct btintel_pcie_dbgc_ctxt { struct btintel_pcie_dbgc_ctxt_buf bufs[BTINTEL_PCIE_DBGC_BUFFER_COUNT]; }; -struct btintel_pcie_removal { - struct pci_dev *pdev; - struct work_struct work; -}; - static LIST_HEAD(btintel_pcie_recovery_list); static DEFINE_SPINLOCK(btintel_pcie_recovery_lock); @@ -289,6 +301,9 @@ static inline void btintel_pcie_dump_debug_registers(struct hci_dev *hdev) skb_put_data(skb, buf, strlen(buf)); data->boot_stage_cache = reg; + if (reg & BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_WARNING) + bt_dev_warn(hdev, "Controller device warning (boot_stage: 0x%8.8x)", reg); + reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_IPC_STATUS_REG); snprintf(buf, sizeof(buf), "ipc status: 0x%8.8x", reg); skb_put_data(skb, buf, strlen(buf)); @@ -880,8 +895,11 @@ static inline bool btintel_pcie_in_lockdown(struct btintel_pcie_data *data) static inline bool btintel_pcie_in_error(struct btintel_pcie_data *data) { - return (data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_ERR) || - (data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_ABORT_HANDLER); + if (data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_WARNING) + bt_dev_warn(data->hdev, "Controller device warning (boot_stage: 0x%8.8x)", + data->boot_stage_cache); + + return data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_ABORT_HANDLER; } static void btintel_pcie_msix_gp1_handler(struct btintel_pcie_data *data) @@ -914,7 +932,8 @@ static void btintel_pcie_msix_gp0_handler(struct btintel_pcie_data *data) data->img_resp_cache = reg; if (btintel_pcie_in_error(data)) { - bt_dev_err(data->hdev, "Controller in error state"); + bt_dev_err(data->hdev, "Controller in error state (boot_stage: 0x%8.8x)", + data->boot_stage_cache); btintel_pcie_dump_debug_registers(data->hdev); return; } @@ -2266,21 +2285,132 @@ static void btintel_pcie_inc_recovery_count(struct pci_dev *pdev, } static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data); +static void btintel_pcie_reset(struct hci_dev *hdev); -static void btintel_pcie_removal_work(struct work_struct *wk) +static int btintel_pcie_acpi_reset_method(struct btintel_pcie_data *data) { - struct btintel_pcie_removal *removal = - container_of(wk, struct btintel_pcie_removal, work); - struct pci_dev *pdev = removal->pdev; - struct btintel_pcie_data *data; + union acpi_object *obj, argv4; + acpi_handle handle; + int ret; + struct pldr_mode { + __le16 cmd_type; + __le16 cmd_payload; + } __packed; + + /* set 1 for _PRR mode + * Product Reset (PLDR Abort flow) + */ + static const struct pldr_mode mode = { + .cmd_type = cpu_to_le16(1), + .cmd_payload = cpu_to_le16(BTINTEL_PCIE_DSM_PLDR_MODE_EN_PROD_RESET | + BTINTEL_PCIE_DSM_PLDR_MODE_EN_WIFI_FLR), + }; + struct hci_dev *hdev = data->hdev; + + handle = ACPI_HANDLE(GET_HCIDEV_DEV(data->hdev)); + if (!handle) { + bt_dev_err(data->hdev, "No support for bluetooth device in ACPI firmware"); + return -EACCES; + } + + if (!acpi_has_method(handle, "_PRR")) { + bt_dev_err(data->hdev, "No support for _PRR ACPI method, cold boot"); + return -ENODEV; + } + + argv4.buffer.type = ACPI_TYPE_BUFFER; + argv4.buffer.length = sizeof(mode); + argv4.buffer.pointer = (void *)&mode; + + obj = acpi_evaluate_dsm(handle, &btintel_guid_dsm, 0, + BTINTEL_PCIE_DSM_DYNAMIC_PLDR, &argv4); + if (!obj) { + bt_dev_err(data->hdev, "Failed to call dsm to set reset method"); + return -EIO; + } + ACPI_FREE(obj); + + pci_dev_lock(data->pdev); + pci_save_state(data->pdev); + ret = btintel_acpi_reset_method(hdev); + if (ret) + bt_dev_err(data->hdev, "ACPI _PRR reset failed (%d), PLDR incomplete", + ret); + pci_restore_state(data->pdev); + pci_dev_unlock(data->pdev); + return ret; +} + +static void btintel_pcie_perform_pldr(struct btintel_pcie_data *data) +{ + struct pci_dev *pdev = data->pdev; + struct pci_dev *wifi = NULL; + struct pci_bus *bus; + int ret; + /* on integrated we have to look up by ID (same bus) */ + static const struct pci_device_id wifi_device_ids[] = { + #define WIFI_DEV(_id) { PCI_DEVICE(PCI_VENDOR_ID_INTEL, _id) } + WIFI_DEV(0xA840), /* LNL */ + WIFI_DEV(0xE440), /* PTL-P */ + WIFI_DEV(0xE340), /* PTL-H */ + WIFI_DEV(0xD340), /* NVL-H */ + WIFI_DEV(0x6E70), /* NVL-S */ + WIFI_DEV(0x4D40), /* WCL */ + WIFI_DEV(0xD240), /* RZL-H */ + WIFI_DEV(0x6C40), /* RZL-M */ + {} + }; + struct pci_dev *tmp = NULL; + + bus = pdev->bus; + if (!bus) + return; + + list_for_each_entry(tmp, &bus->devices, bus_list) { + if (pci_match_id(wifi_device_ids, tmp)) { + wifi = pci_dev_get(tmp); + break; + } + } + + if (wifi) + device_release_driver(&wifi->dev); + + /* Wi-Fi is fully unbound before the reset and fully reprobed after + * the normal PCI probe path handles all state setup from scratch. + * BT needs pci_save_state()/pci_restore_state() because the BT driver + * is still partially attached when the _PRR runs (it hasn't been unbound yet). + * The PCI device needs to remain minimally functional so that + * device_reprobe(&pdev->dev) can work afterward + */ + ret = btintel_pcie_acpi_reset_method(data); + + if (wifi) { + if (device_reprobe(&wifi->dev)) + BT_ERR("WiFi reprobe failed for BDF:%s", pci_name(wifi)); + pci_dev_put(wifi); + } + + if (!ret) { + if (device_reprobe(&pdev->dev)) + BT_ERR("BT reprobe failed for BDF:%s", pci_name(pdev)); + } +} + +static void btintel_pcie_reset_work(struct work_struct *wk) +{ + struct btintel_pcie_data *data = + container_of(wk, struct btintel_pcie_data, reset_work); + struct pci_dev *pdev = data->pdev; int err; pci_lock_rescan_remove(); if (!pdev->bus) - goto error; + goto out; - data = pci_get_drvdata(pdev); + if (!data) + goto out; btintel_pcie_disable_interrupts(data); btintel_pcie_synchronize_irqs(data); @@ -2288,12 +2418,21 @@ static void btintel_pcie_removal_work(struct work_struct *wk) flush_work(&data->rx_work); bt_dev_dbg(data->hdev, "Release bluetooth interface"); + if (data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR) { + /* This function holds pci_lock_rescan_remove(), which acquires + * pci_rescan_remove_lock. This mutex serializes against PCI device + * addition/removal (hotplug), so no device can be added to or + * removed from the bus list while this code runs. + */ + btintel_pcie_perform_pldr(data); + goto out; + } btintel_pcie_release_hdev(data); err = pci_reset_function(pdev); if (err) { BT_ERR("Failed resetting the pcie device (%d)", err); - goto error; + goto out; } btintel_pcie_enable_interrupts(data); @@ -2303,7 +2442,7 @@ static void btintel_pcie_removal_work(struct work_struct *wk) if (err) { BT_ERR("Failed to enable bluetooth hardware after reset (%d)", err); - goto error; + goto out; } btintel_pcie_reset_ia(data); @@ -2313,17 +2452,15 @@ static void btintel_pcie_removal_work(struct work_struct *wk) err = btintel_pcie_setup_hdev(data); if (err) { BT_ERR("Failed registering hdev (%d)", err); - goto error; + goto out; } -error: +out: pci_dev_put(pdev); pci_unlock_rescan_remove(); - kfree(removal); } static void btintel_pcie_reset(struct hci_dev *hdev) { - struct btintel_pcie_removal *removal; struct btintel_pcie_data *data; data = hci_get_drvdata(hdev); @@ -2334,14 +2471,8 @@ static void btintel_pcie_reset(struct hci_dev *hdev) if (test_and_set_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags)) return; - removal = kzalloc_obj(*removal, GFP_ATOMIC); - if (!removal) - return; - - removal->pdev = data->pdev; - INIT_WORK(&removal->work, btintel_pcie_removal_work); - pci_dev_get(removal->pdev); - schedule_work(&removal->work); + pci_dev_get(data->pdev); + schedule_work(&data->reset_work); } static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code) @@ -2351,15 +2482,19 @@ static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code) struct pci_dev *pdev = dev_data->pdev; time64_t retry_window; - if (code == 0x13) { - bt_dev_err(hdev, "Encountered top exception"); - return; - } + btintel_pcie_dump_debug_registers(hdev); data = btintel_pcie_get_recovery(pdev, &hdev->dev); if (!data) return; + if (code == 0x13) + dev_data->reset_type = BTINTEL_PCIE_IOSF_PRR_PLDR; + else + dev_data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR; + + bt_dev_err(hdev, "Encountered exception err:0x%x triggering: %s", code, + dev_data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR ? "PLDR" : "FLR"); retry_window = ktime_get_boottime_seconds() - data->last_error; if (retry_window < BTINTEL_PCIE_RESET_WINDOW_SECS && @@ -2512,10 +2647,14 @@ static int btintel_pcie_probe(struct pci_dev *pdev, skb_queue_head_init(&data->rx_skb_q); INIT_WORK(&data->rx_work, btintel_pcie_rx_work); + INIT_WORK(&data->reset_work, btintel_pcie_reset_work); data->boot_stage_cache = 0x00; data->img_resp_cache = 0x00; - + /* FLR can be invoked by echoing to debugfs path, so explicitly + * initialized + */ + data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR; err = btintel_pcie_config_pcie(pdev, data); if (err) goto exit_error; @@ -2564,6 +2703,14 @@ static void btintel_pcie_remove(struct pci_dev *pdev) data = pci_get_drvdata(pdev); + /* Cancel pending reset work. Skip only when remove() is called from + * within the reset work itself (PLDR device_reprobe path) to avoid + * deadlock. current_work() returns the work_struct of the caller if + * we are in a workqueue context. + */ + if (current_work() != &data->reset_work) + cancel_work_sync(&data->reset_work); + btintel_pcie_disable_interrupts(data); btintel_pcie_synchronize_irqs(data); @@ -2713,6 +2860,7 @@ static int btintel_pcie_resume(struct device *dev) if (data->pm_sx_event == PM_EVENT_FREEZE || data->pm_sx_event == PM_EVENT_HIBERNATE) { set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags); + data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR; btintel_pcie_reset(data->hdev); return 0; } diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h index 3c7bb708362de..2db85f71b2f8c 100644 --- a/drivers/bluetooth/btintel_pcie.h +++ b/drivers/bluetooth/btintel_pcie.h @@ -48,7 +48,7 @@ #define BTINTEL_PCIE_CSR_BOOT_STAGE_OPFW (BIT(2)) #define BTINTEL_PCIE_CSR_BOOT_STAGE_ROM_LOCKDOWN (BIT(10)) #define BTINTEL_PCIE_CSR_BOOT_STAGE_IML_LOCKDOWN (BIT(11)) -#define BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_ERR (BIT(12)) +#define BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_WARNING (BIT(12)) #define BTINTEL_PCIE_CSR_BOOT_STAGE_ABORT_HANDLER (BIT(13)) #define BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_HALTED (BIT(14)) #define BTINTEL_PCIE_CSR_BOOT_STAGE_MAC_ACCESS_ON (BIT(16)) @@ -144,6 +144,11 @@ enum msix_mbox_int_causes { BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX4 = BIT(3), /* cause MBOX4 */ }; +enum btintel_pcie_reset_type { + BTINTEL_PCIE_IOSF_PRR_FLR = 0, + BTINTEL_PCIE_IOSF_PRR_PLDR = 1, +}; + #define BTINTEL_PCIE_MSIX_NON_AUTO_CLEAR_CAUSE BIT(7) /* Minimum and Maximum number of MSI-X Vector @@ -500,6 +505,7 @@ struct btintel_pcie_data { struct workqueue_struct *workqueue; struct sk_buff_head rx_skb_q; struct work_struct rx_work; + struct work_struct reset_work; struct dma_pool *dma_pool; dma_addr_t dma_p_addr; @@ -511,6 +517,7 @@ struct btintel_pcie_data { struct txq txq; struct rxq rxq; u32 alive_intr_ctxt; + enum btintel_pcie_reset_type reset_type; struct btintel_pcie_dbgc dbgc; struct btintel_pcie_dump_header dmp_hdr; u8 pm_sx_event; diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c index 6fb6ca2748086..ab34f1dd42bcf 100644 --- a/drivers/bluetooth/btmtk.c +++ b/drivers/bluetooth/btmtk.c @@ -695,8 +695,13 @@ static int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev, if (data->evt_skb == NULL) goto err_free_wc; - /* Parse and handle the return WMT event */ - wmt_evt = (struct btmtk_hci_wmt_evt *)data->evt_skb->data; + wmt_evt = skb_pull_data(data->evt_skb, sizeof(*wmt_evt)); + if (!wmt_evt) { + bt_dev_err(hdev, "WMT event too short (%u bytes)", + data->evt_skb->len); + err = -EINVAL; + goto err_free_skb; + } if (wmt_evt->whdr.op != hdr->op) { bt_dev_err(hdev, "Wrong op received %d expected %d", wmt_evt->whdr.op, hdr->op); @@ -712,6 +717,12 @@ static int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev, status = BTMTK_WMT_PATCH_DONE; break; case BTMTK_WMT_FUNC_CTRL: + if (!skb_pull_data(data->evt_skb, + sizeof(wmt_evt_funcc->status))) { + err = -EINVAL; + goto err_free_skb; + } + wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt; if (be16_to_cpu(wmt_evt_funcc->status) == 0x404) status = BTMTK_WMT_ON_DONE; @@ -1534,6 +1545,29 @@ int btmtk_usb_shutdown(struct hci_dev *hdev) return 0; } EXPORT_SYMBOL_GPL(btmtk_usb_shutdown); + +int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_event_hdr *hdr = (void *)skb->data; + struct hci_ev_cmd_complete *ec; + + if (hdr->evt == HCI_EV_CMD_COMPLETE && + skb->len >= HCI_EVENT_HDR_SIZE + sizeof(*ec)) { + u16 opcode; + + ec = (void *)(skb->data + HCI_EVENT_HDR_SIZE); + opcode = __le16_to_cpu(ec->opcode); + + /* Filter vendor opcode */ + if (opcode == 0xfc5d) { + kfree_skb(skb); + return 0; + } + } + + return hci_recv_frame(hdev, skb); +} +EXPORT_SYMBOL_GPL(btmtk_recv_event); #endif MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h index c564aedc0ce0f..c83c24897c954 100644 --- a/drivers/bluetooth/btmtk.h +++ b/drivers/bluetooth/btmtk.h @@ -220,6 +220,8 @@ int btmtk_usb_suspend(struct hci_dev *hdev); int btmtk_usb_setup(struct hci_dev *hdev); int btmtk_usb_shutdown(struct hci_dev *hdev); + +int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb); #else static inline int btmtk_set_bdaddr(struct hci_dev *hdev, @@ -299,4 +301,9 @@ static inline int btmtk_usb_shutdown(struct hci_dev *hdev) { return -EOPNOTSUPP; } + +static inline int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb) +{ + return hci_recv_frame(hdev, skb); +} #endif diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 7f5fce93d9848..572091e601f9e 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -601,6 +601,10 @@ static const struct usb_device_id quirks_table[] = { /* Realtek 8922AE Bluetooth devices */ { USB_DEVICE(0x0bda, 0x8922), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0bda, 0xd922), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0bda, 0xd923), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3617), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3616), .driver_info = BTUSB_REALTEK | @@ -723,6 +727,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x04ca, 0x38e4), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0e8d, 0x223c), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3568), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3584), .driver_info = BTUSB_MEDIATEK | @@ -773,6 +779,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0489, 0xe151), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0e8d, 0x8c38), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3602), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3603), .driver_info = BTUSB_MEDIATEK | @@ -814,6 +822,8 @@ static const struct usb_device_id quirks_table[] = { { USB_DEVICE(0x2ff8, 0xb011), .driver_info = BTUSB_REALTEK }, /* Additional Realtek 8761BUV Bluetooth devices */ + { USB_DEVICE(0x2c4e, 0x0115), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x2357, 0x0604), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0b05, 0x190e), .driver_info = BTUSB_REALTEK | @@ -4242,6 +4252,7 @@ static int btusb_probe(struct usb_interface *intf, hci_set_quirk(hdev, HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN); hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP); data->recv_acl = btmtk_usb_recv_acl; + data->recv_event = btmtk_recv_event; data->suspend = btmtk_usb_suspend; data->resume = btmtk_usb_resume; data->disconnect = btusb_mtk_disconnect; diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c index fa679ad0acdfa..8201fa7f61e84 100644 --- a/drivers/bluetooth/hci_ath.c +++ b/drivers/bluetooth/hci_ath.c @@ -191,6 +191,9 @@ static int ath_recv(struct hci_uart *hu, const void *data, int count) { struct ath_struct *ath = hu->priv; + if (!ath) + return -ENODEV; + ath->rx_skb = h4_recv_buf(hu, ath->rx_skb, data, count, ath_recv_pkts, ARRAY_SIZE(ath_recv_pkts)); if (IS_ERR(ath->rx_skb)) { diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c index b386f91d8b46d..db56eead27ceb 100644 --- a/drivers/bluetooth/hci_bcsp.c +++ b/drivers/bluetooth/hci_bcsp.c @@ -585,6 +585,9 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count) if (!test_bit(HCI_UART_REGISTERED, &hu->flags)) return -EUNATCH; + if (!bcsp) + return -ENODEV; + BT_DBG("hu %p count %d rx_state %d rx_count %ld", hu, count, bcsp->rx_state, bcsp->rx_count); diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index a889a66a326f7..7673727074985 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -109,6 +109,9 @@ static int h4_recv(struct hci_uart *hu, const void *data, int count) { struct h4_struct *h4 = hu->priv; + if (!h4) + return -ENODEV; + h4->rx_skb = h4_recv_buf(hu, h4->rx_skb, data, count, h4_recv_pkts, ARRAY_SIZE(h4_recv_pkts)); if (IS_ERR(h4->rx_skb)) { diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index cfdf75dc28475..d353837182125 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -587,6 +587,9 @@ static int h5_recv(struct hci_uart *hu, const void *data, int count) struct h5 *h5 = hu->priv; const unsigned char *ptr = data; + if (!h5) + return -ENODEV; + BT_DBG("%s pending %zu count %d", hu->hdev->name, h5->rx_pending, count); diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c index 76d61af8a275e..140ab55c9fc5a 100644 --- a/drivers/bluetooth/virtio_bt.c +++ b/drivers/bluetooth/virtio_bt.c @@ -12,6 +12,7 @@ #include <net/bluetooth/hci_core.h> #define VERSION "0.1" +#define VIRTBT_RX_BUF_SIZE 1000 enum { VIRTBT_VQ_TX, @@ -33,11 +34,11 @@ static int virtbt_add_inbuf(struct virtio_bluetooth *vbt) struct sk_buff *skb; int err; - skb = alloc_skb(1000, GFP_KERNEL); + skb = alloc_skb(VIRTBT_RX_BUF_SIZE, GFP_KERNEL); if (!skb) return -ENOMEM; - sg_init_one(sg, skb->data, 1000); + sg_init_one(sg, skb->data, VIRTBT_RX_BUF_SIZE); err = virtqueue_add_inbuf(vq, sg, 1, skb, GFP_KERNEL); if (err < 0) { @@ -197,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev) static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb) { + size_t min_hdr; __u8 pkt_type; pkt_type = *((__u8 *) skb->data); @@ -204,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb) switch (pkt_type) { case HCI_EVENT_PKT: + min_hdr = sizeof(struct hci_event_hdr); + break; case HCI_ACLDATA_PKT: + min_hdr = sizeof(struct hci_acl_hdr); + break; case HCI_SCODATA_PKT: + min_hdr = sizeof(struct hci_sco_hdr); + break; case HCI_ISODATA_PKT: - hci_skb_pkt_type(skb) = pkt_type; - hci_recv_frame(vbt->hdev, skb); + min_hdr = sizeof(struct hci_iso_hdr); break; default: kfree_skb(skb); - break; + return; + } + + if (skb->len < min_hdr) { + bt_dev_err_ratelimited(vbt->hdev, + "rx pkt_type 0x%02x payload %u < hdr %zu\n", + pkt_type, skb->len, min_hdr); + kfree_skb(skb); + return; } + + hci_skb_pkt_type(skb) = pkt_type; + hci_recv_frame(vbt->hdev, skb); } static void virtbt_rx_work(struct work_struct *work) @@ -227,8 +245,15 @@ static void virtbt_rx_work(struct work_struct *work) if (!skb) return; - skb_put(skb, len); - virtbt_rx_handle(vbt, skb); + if (!len || len > VIRTBT_RX_BUF_SIZE) { + bt_dev_err_ratelimited(vbt->hdev, + "rx reply len %u outside [1, %u]\n", + len, VIRTBT_RX_BUF_SIZE); + kfree_skb(skb); + } else { + skb_put(skb, len); + virtbt_rx_handle(vbt, skb); + } if (virtbt_add_inbuf(vbt) < 0) return; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index a7bffb908c1ec..aa600fbf9a535 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -2495,7 +2495,7 @@ void mgmt_adv_monitor_device_lost(struct hci_dev *hdev, u16 handle, bdaddr_t *bdaddr, u8 addr_type); int hci_abort_conn(struct hci_conn *conn, u8 reason); -u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, +void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, u16 to_multiplier); void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand, __u8 ltk[16], __u8 key_size); diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 2f03b780b40d8..23a229ab6a33d 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -758,13 +758,33 @@ static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan) return chan; } +static void unregister_dev(struct lowpan_btle_dev *dev) +{ + struct hci_dev *hdev = READ_ONCE(dev->hdev); + + /* If netdev holds last reference to hci_dev (its parent device), this + * leads to theoretical cyclic locking on lowpan_unregister_netdev: + * + * rtnl_lock -> put_device(parent) -> hci_release_dev -> + * destroy_workqueue -> hci_rx_work -> l2cap_recv_acldata -> + * chan_ready_cb -> ifup -> rtnl_lock + * + * However, hci_rx_work is disabled in hci_unregister_dev, so this + * should not occur. Make lockdep happy by postponing hdev release after + * netdev put. + */ + hci_dev_hold(hdev); + lowpan_unregister_netdev(dev->netdev); + hci_dev_put(hdev); +} + static void delete_netdev(struct work_struct *work) { struct lowpan_btle_dev *entry = container_of(work, struct lowpan_btle_dev, delete_netdev); - lowpan_unregister_netdev(entry->netdev); + unregister_dev(entry); /* The entry pointer is deleted by the netdev destructor. */ } @@ -1250,6 +1270,7 @@ static void disconnect_devices(void) break; new_dev->netdev = entry->netdev; + new_dev->hdev = entry->hdev; INIT_LIST_HEAD(&new_dev->list); list_add_rcu(&new_dev->list, &devices); @@ -1261,7 +1282,7 @@ static void disconnect_devices(void) ifdown(entry->netdev); BT_DBG("Unregistering netdev %s %p", entry->netdev->name, entry->netdev); - lowpan_unregister_netdev(entry->netdev); + unregister_dev(entry); kfree(entry); } } diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index d44987d4515c0..853c8d7644b55 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -330,11 +330,18 @@ static int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb) goto badframe; break; case BNEP_FILTER_MULTI_ADDR_SET: - case BNEP_FILTER_NET_TYPE_SET: - /* Pull: ctrl type (1 b), len (2 b), data (len bytes) */ - if (!skb_pull(skb, 3 + *(u16 *)(skb->data + 1) * 2)) + case BNEP_FILTER_NET_TYPE_SET: { + u8 *hdr; + + /* Pull ctrl type (1 b) + len (2 b) */ + hdr = skb_pull_data(skb, 3); + if (!hdr) + goto badframe; + /* Pull data (len bytes); length is big-endian */ + if (!skb_pull(skb, get_unaligned_be16(&hdr[1]))) goto badframe; break; + } default: kfree_skb(skb); return 0; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 3a05925990861..17b46ad6a3496 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -480,40 +480,107 @@ bool hci_setup_sync(struct hci_conn *conn, __u16 handle) return hci_setup_sync_conn(conn, handle); } -u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, - u16 to_multiplier) +struct le_conn_update_data { + struct hci_conn *conn; + u16 min; + u16 max; + u16 latency; + u16 to_multiplier; +}; + +static int le_conn_update_sync(struct hci_dev *hdev, void *data) { - struct hci_dev *hdev = conn->hdev; + struct le_conn_update_data *d = data; + struct hci_conn *conn = d->conn; struct hci_conn_params *params; struct hci_cp_le_conn_update cp; + u16 timeout; + u8 store_hint; + int err; + /* Verify connection is still alive and read conn fields under + * the same lock to prevent a concurrent disconnect from freeing + * or reusing the connection while we build the HCI command. + */ hci_dev_lock(hdev); - params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); - if (params) { - params->conn_min_interval = min; - params->conn_max_interval = max; - params->conn_latency = latency; - params->supervision_timeout = to_multiplier; + if (!hci_conn_valid(hdev, conn)) { + hci_dev_unlock(hdev); + return -ECANCELED; } - hci_dev_unlock(hdev); - memset(&cp, 0, sizeof(cp)); cp.handle = cpu_to_le16(conn->handle); - cp.conn_interval_min = cpu_to_le16(min); - cp.conn_interval_max = cpu_to_le16(max); - cp.conn_latency = cpu_to_le16(latency); - cp.supervision_timeout = cpu_to_le16(to_multiplier); + cp.conn_interval_min = cpu_to_le16(d->min); + cp.conn_interval_max = cpu_to_le16(d->max); + cp.conn_latency = cpu_to_le16(d->latency); + cp.supervision_timeout = cpu_to_le16(d->to_multiplier); cp.min_ce_len = cpu_to_le16(0x0000); cp.max_ce_len = cpu_to_le16(0x0000); + timeout = conn->conn_timeout; + + hci_dev_unlock(hdev); - hci_send_cmd(hdev, HCI_OP_LE_CONN_UPDATE, sizeof(cp), &cp); + err = __hci_cmd_sync_status_sk(hdev, HCI_OP_LE_CONN_UPDATE, + sizeof(cp), &cp, + HCI_EV_LE_CONN_UPDATE_COMPLETE, + timeout, NULL); + if (err) + return err; + + /* Update stored connection parameters after the controller has + * confirmed the update via the LE Connection Update Complete event. + */ + hci_dev_lock(hdev); + + params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); + if (params) { + params->conn_min_interval = d->min; + params->conn_max_interval = d->max; + params->conn_latency = d->latency; + params->supervision_timeout = d->to_multiplier; + store_hint = 0x01; + } else { + store_hint = 0x00; + } - if (params) - return 0x01; + hci_dev_unlock(hdev); - return 0x00; + mgmt_new_conn_param(hdev, &conn->dst, conn->dst_type, store_hint, + d->min, d->max, d->latency, d->to_multiplier); + + return 0; +} + +static void le_conn_update_complete(struct hci_dev *hdev, void *data, int err) +{ + struct le_conn_update_data *d = data; + + hci_conn_put(d->conn); + kfree(d); +} + +void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, + u16 to_multiplier) +{ + struct le_conn_update_data *d; + + d = kzalloc_obj(*d); + if (!d) + return; + + hci_conn_get(conn); + d->conn = conn; + d->min = min; + d->max = max; + d->latency = latency; + d->to_multiplier = to_multiplier; + + if (hci_cmd_sync_queue(conn->hdev, le_conn_update_sync, d, + le_conn_update_complete) < 0) { + hci_conn_put(conn); + kfree(d); + } } void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand, @@ -2130,6 +2197,9 @@ static int create_big_sync(struct hci_dev *hdev, void *data) u32 flags = 0; int err; + if (!hci_conn_valid(hdev, conn)) + return -ECANCELED; + if (qos->bcast.out.phys == BIT(1)) flags |= MGMT_ADV_FLAG_SEC_2M; @@ -2204,11 +2274,24 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err) bt_dev_dbg(hdev, "conn %p", conn); + if (err == -ECANCELED) + goto done; + + hci_dev_lock(hdev); + + if (!hci_conn_valid(hdev, conn)) + goto unlock; + if (err) { bt_dev_err(hdev, "Unable to create BIG: %d", err); hci_connect_cfm(conn, err); hci_conn_del(conn); } + +unlock: + hci_dev_unlock(hdev); +done: + hci_conn_put(conn); } struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid, @@ -2336,10 +2419,11 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst, BT_BOUND, &data); /* Queue start periodic advertising and create BIG */ - err = hci_cmd_sync_queue(hdev, create_big_sync, conn, + err = hci_cmd_sync_queue(hdev, create_big_sync, hci_conn_get(conn), create_big_complete); if (err < 0) { hci_conn_drop(conn); + hci_conn_put(conn); return ERR_PTR(err); } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index b2ee6b6a0f565..1b3b9131affaa 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -7118,9 +7118,29 @@ static void hci_le_create_big_complete_evt(struct hci_dev *hdev, void *data, continue; } + if (ev->num_bis <= i) { + bt_dev_err(hdev, + "Not enough BIS handles for BIG 0x%2.2x", + ev->handle); + ev->status = HCI_ERROR_UNSPECIFIED; + hci_connect_cfm(conn, ev->status); + hci_conn_del(conn); + continue; + } + if (hci_conn_set_handle(conn, - __le16_to_cpu(ev->bis_handle[i++]))) + __le16_to_cpu(ev->bis_handle[i++]))) { + bt_dev_err(hdev, + "Failed to set BIS handle for BIG 0x%2.2x", + ev->handle); + /* Force error so BIG gets terminated as not all BIS + * could be connected. + */ + ev->status = HCI_ERROR_UNSPECIFIED; + hci_connect_cfm(conn, ev->status); + hci_conn_del(conn); continue; + } conn->state = BT_CONNECTED; set_bit(HCI_CONN_BIG_CREATED, &conn->flags); @@ -7129,7 +7149,10 @@ static void hci_le_create_big_complete_evt(struct hci_dev *hdev, void *data, hci_iso_setup_path(conn); } - if (!ev->status && !i) + /* If there is an unexpected error or if no BISes have been connected + * for the BIG, terminate it. + */ + if (ev->status == HCI_ERROR_UNSPECIFIED || (!ev->status && !i)) /* If no BISes have been connected for the BIG, * terminate. This is in case all bound connections * have been closed before the BIG creation diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index be145e2736b78..7cb2864fe8724 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -347,6 +347,7 @@ static int iso_connect_bis(struct sock *sk) return -EHOSTUNREACH; hci_dev_lock(hdev); + lock_sock(sk); if (!bis_capable(hdev)) { err = -EOPNOTSUPP; @@ -399,13 +400,9 @@ static int iso_connect_bis(struct sock *sk) goto unlock; } - lock_sock(sk); - err = iso_chan_add(conn, sk, NULL); - if (err) { - release_sock(sk); + if (err) goto unlock; - } /* Update source addr of the socket */ bacpy(&iso_pi(sk)->src, &hcon->src); @@ -421,9 +418,8 @@ static int iso_connect_bis(struct sock *sk) iso_sock_set_timer(sk, READ_ONCE(sk->sk_sndtimeo)); } - release_sock(sk); - unlock: + release_sock(sk); hci_dev_unlock(hdev); hci_dev_put(hdev); return err; @@ -444,6 +440,7 @@ static int iso_connect_cis(struct sock *sk) return -EHOSTUNREACH; hci_dev_lock(hdev); + lock_sock(sk); if (!cis_central_capable(hdev)) { err = -EOPNOTSUPP; @@ -498,13 +495,9 @@ static int iso_connect_cis(struct sock *sk) goto unlock; } - lock_sock(sk); - err = iso_chan_add(conn, sk, NULL); - if (err) { - release_sock(sk); + if (err) goto unlock; - } /* Update source addr of the socket */ bacpy(&iso_pi(sk)->src, &hcon->src); @@ -520,9 +513,8 @@ static int iso_connect_cis(struct sock *sk) iso_sock_set_timer(sk, READ_ONCE(sk->sk_sndtimeo)); } - release_sock(sk); - unlock: + release_sock(sk); hci_dev_unlock(hdev); hci_dev_put(hdev); return err; @@ -1193,7 +1185,7 @@ static int iso_sock_connect(struct socket *sock, struct sockaddr_unsized *addr, release_sock(sk); - if (bacmp(&iso_pi(sk)->dst, BDADDR_ANY)) + if (bacmp(&sa->iso_bdaddr, BDADDR_ANY)) err = iso_connect_cis(sk); else err = iso_connect_bis(sk); @@ -2256,8 +2248,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) sk = iso_get_sock(hdev, &hdev->bdaddr, bdaddr, BT_LISTEN, iso_match_sid, ev1); if (sk && !ev1->status) { + lock_sock(sk); iso_pi(sk)->sync_handle = le16_to_cpu(ev1->handle); iso_pi(sk)->bc_sid = ev1->sid; + release_sock(sk); } goto done; @@ -2268,8 +2262,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) sk = iso_get_sock(hdev, &hdev->bdaddr, bdaddr, BT_LISTEN, iso_match_sid_past, ev1a); if (sk && !ev1a->status) { + lock_sock(sk); iso_pi(sk)->sync_handle = le16_to_cpu(ev1a->sync_handle); iso_pi(sk)->bc_sid = ev1a->sid; + release_sock(sk); } goto done; @@ -2296,27 +2292,35 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) ev2); if (sk) { - int err; - struct hci_conn *hcon = iso_pi(sk)->conn->hcon; + int err = 0; + bool big_sync; + struct hci_conn *hcon; + lock_sock(sk); + + hcon = iso_pi(sk)->conn->hcon; iso_pi(sk)->qos.bcast.encryption = ev2->encryption; if (ev2->num_bis < iso_pi(sk)->bc_num_bis) iso_pi(sk)->bc_num_bis = ev2->num_bis; - if (!test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags) && - !test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) { + big_sync = !test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags) && + !test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags); + + if (big_sync) err = hci_conn_big_create_sync(hdev, hcon, &iso_pi(sk)->qos, iso_pi(sk)->sync_handle, iso_pi(sk)->bc_num_bis, iso_pi(sk)->bc_bis); - if (err) { - bt_dev_err(hdev, "hci_le_big_create_sync: %d", - err); - sock_put(sk); - sk = NULL; - } + + release_sock(sk); + + if (big_sync && err) { + bt_dev_err(hdev, "hci_le_big_create_sync: %d", + err); + sock_put(sk); + sk = NULL; } } @@ -2370,8 +2374,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) if (!base || base_len > BASE_MAX_LENGTH) goto done; + lock_sock(sk); memcpy(iso_pi(sk)->base, base, base_len); iso_pi(sk)->base_len = base_len; + release_sock(sk); } else { /* This is a PA data fragment. Keep pa_data_len set to 0 * until all data has been reassembled. diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 77dec104a9c36..7701528f11677 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4706,16 +4706,8 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_PARAM_UPDATE_RSP, sizeof(rsp), &rsp); - if (!err) { - u8 store_hint; - - store_hint = hci_le_conn_update(hcon, min, max, latency, - to_multiplier); - mgmt_new_conn_param(hcon->hdev, &hcon->dst, hcon->dst_type, - store_hint, min, max, latency, - to_multiplier); - - } + if (!err) + hci_le_conn_update(hcon, min, max, latency, to_multiplier); return 0; } @@ -5428,7 +5420,7 @@ static inline int l2cap_ecred_reconf_req(struct l2cap_conn *conn, * configured, the MPS field may be less than the current MPS * of that channel. */ - if (chan[i]->remote_mps >= mps && i) { + if (chan[i]->remote_mps > mps && num_scid > 1) { BT_ERR("chan %p decreased MPS %u -> %u", chan[i], chan[i]->remote_mps, mps); result = L2CAP_RECONF_INVALID_MPS; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 71e8c1b45bcee..cf590a67d3641 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1498,6 +1498,9 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan) { struct sock *sk, *parent = chan->data; + if (!parent) + return NULL; + lock_sock(parent); /* Check for backlog size */ @@ -1657,6 +1660,9 @@ static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state, { struct sock *sk = chan->data; + if (!sk) + return; + sk->sk_state = state; if (err) @@ -1758,6 +1764,9 @@ static long l2cap_sock_get_sndtimeo_cb(struct l2cap_chan *chan) { struct sock *sk = chan->data; + if (!sk) + return 0; + return READ_ONCE(sk->sk_sndtimeo); } diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 611a9a94151ec..d11bd5337d573 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -1715,9 +1715,12 @@ static int rfcomm_recv_data(struct rfcomm_session *s, u8 dlci, int pf, struct sk } if (pf && d->cfc) { - u8 credits = *(u8 *) skb->data; skb_pull(skb, 1); + u8 *credits = skb_pull_data(skb, 1); - d->tx_credits += credits; + if (!credits) + goto drop; + + d->tx_credits += *credits; if (d->tx_credits) clear_bit(RFCOMM_TX_THROTTLED, &d->flags); } diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 18826d4b9c0bf..eba44525d41d9 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -472,9 +472,13 @@ static struct sock *sco_get_sock_listen(bdaddr_t *src) sk1 = sk; } + sk = sk ? sk : sk1; + if (sk) + sock_hold(sk); + read_unlock(&sco_sk_list.lock); - return sk ? sk : sk1; + return sk; } static void sco_sock_destruct(struct sock *sk) @@ -515,11 +519,13 @@ static void sco_sock_kill(struct sock *sk) BT_DBG("sk %p state %d", sk, sk->sk_state); /* Sock is dead, so set conn->sk to NULL to avoid possible UAF */ + lock_sock(sk); if (sco_pi(sk)->conn) { sco_conn_lock(sco_pi(sk)->conn); sco_pi(sk)->conn->sk = NULL; sco_conn_unlock(sco_pi(sk)->conn); } + release_sock(sk); /* Kill poor orphan */ bt_sock_unlink(&sco_sk_list, sk); @@ -1365,40 +1371,51 @@ static int sco_sock_release(struct socket *sock) static void sco_conn_ready(struct sco_conn *conn) { - struct sock *parent; - struct sock *sk = conn->sk; + struct sock *parent, *sk; + + sco_conn_lock(conn); + sk = sco_sock_hold(conn); + sco_conn_unlock(conn); BT_DBG("conn %p", conn); if (sk) { lock_sock(sk); - sco_sock_clear_timer(sk); - sk->sk_state = BT_CONNECTED; - sk->sk_state_change(sk); + + /* conn->sk may have become NULL if racing with sk close, but + * due to held hdev->lock, it can't become different sk. + */ + if (conn->sk) { + sco_sock_clear_timer(sk); + sk->sk_state = BT_CONNECTED; + sk->sk_state_change(sk); + } + release_sock(sk); + sock_put(sk); } else { - sco_conn_lock(conn); - - if (!conn->hcon) { - sco_conn_unlock(conn); + if (!conn->hcon) return; - } + + lockdep_assert_held(&conn->hcon->hdev->lock); parent = sco_get_sock_listen(&conn->hcon->src); - if (!parent) { - sco_conn_unlock(conn); + if (!parent) return; - } lock_sock(parent); + sco_conn_lock(conn); + + /* hdev->lock guarantees conn->sk == NULL still here */ + + if (parent->sk_state != BT_LISTEN) + goto release; + sk = sco_sock_alloc(sock_net(parent), NULL, BTPROTO_SCO, GFP_ATOMIC, 0); - if (!sk) { - release_sock(parent); - sco_conn_unlock(conn); - return; - } + if (!sk) + goto release; sco_sock_init(sk, parent); @@ -1417,9 +1434,10 @@ static void sco_conn_ready(struct sco_conn *conn) /* Wake up parent */ parent->sk_data_ready(parent); - release_sock(parent); - +release: sco_conn_unlock(conn); + release_sock(parent); + sock_put(parent); } } |
