From ea2ee8b222306208d2b094d1a11894da6c106d42 Mon Sep 17 00:00:00 2001 From: Kiran K Date: Tue, 25 Aug 2026 22:53:00 +0530 Subject: [PATCH 1/9] Bluetooth: btintel_pcie: Clear automask on spurious interrupts On spurious interrupt where the TX and RX causes are not set, driver was not clearing the auto mask which can block all the interrupts. Driver needs to clear the automask even if no causes are set. 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 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index 005c77a4f5eb..eec95e5f3dbb 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -1696,6 +1696,9 @@ static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id) if (unlikely(!(intr_fh | intr_hw))) { /* Ignore interrupt, inta == 0 */ + bt_warn_ratelimited("Bluetooth: btintel_pcie: Received spurious interrupt\n"); + btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_AUTOMASK_ST, + BIT(entry->entry)); return IRQ_NONE; } From a086c0892969bf8a0151b0f12bd14a68827c88b2 Mon Sep 17 00:00:00 2001 From: Laxman Acharya Padhya Date: Mon, 31 Aug 2026 15:44:21 +0545 Subject: [PATCH 2/9] Bluetooth: btintel: validate version TLV value lengths btintel_parse_version_tlv() verifies that a complete TLV is present in the response, but it does not ensure that the value is long enough for the specific TLV type. A short value can therefore cause an out-of-bounds read through get_unaligned_le16(), get_unaligned_le32(), or memcpy(). Reject values shorter than the minimum required by each known TLV type. Also reject responses that do not contain the Command Complete Status field. Fixes: 57375beef71a ("Bluetooth: btintel: Add infrastructure to read controller information") Reviewed-by: Ali Ahmet Memis Signed-off-by: Laxman Acharya Padhya Tested-by: Kiran K Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btintel.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index bcb2514b7bc0..352a1c83cd08 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -571,12 +571,44 @@ int btintel_version_info_tlv(struct hci_dev *hdev, } EXPORT_SYMBOL_GPL(btintel_version_info_tlv); +static u8 btintel_version_tlv_min_len(u8 type) +{ + switch (type) { + case INTEL_TLV_CNVI_TOP: + case INTEL_TLV_CNVR_TOP: + case INTEL_TLV_CNVI_BT: + case INTEL_TLV_CNVR_BT: + case INTEL_TLV_BUILD_NUM: + case INTEL_TLV_GIT_SHA1: + return sizeof(u32); + case INTEL_TLV_DEV_REV_ID: + case INTEL_TLV_TIME_STAMP: + return sizeof(u16); + case INTEL_TLV_IMAGE_TYPE: + case INTEL_TLV_BUILD_TYPE: + case INTEL_TLV_SECURE_BOOT: + case INTEL_TLV_OTP_LOCK: + case INTEL_TLV_API_LOCK: + case INTEL_TLV_DEBUG_LOCK: + case INTEL_TLV_LIMITED_CCE: + case INTEL_TLV_SBE_TYPE: + return sizeof(u8); + case INTEL_TLV_MIN_FW: + return 3; + case INTEL_TLV_OTP_BDADDR: + return sizeof(bdaddr_t); + default: + return 0; + } +} + int btintel_parse_version_tlv(struct hci_dev *hdev, struct intel_version_tlv *version, struct sk_buff *skb) { /* Consume Command Complete Status field */ - skb_pull(skb, 1); + if (!skb_pull(skb, 1)) + return -EINVAL; /* Event parameters contain multiple TLVs. Read each of them * and only keep the required data. Also, it use existing legacy @@ -596,6 +628,9 @@ int btintel_parse_version_tlv(struct hci_dev *hdev, if (skb->len < tlv->len + sizeof(*tlv)) return -EINVAL; + if (tlv->len < btintel_version_tlv_min_len(tlv->type)) + return -EINVAL; + switch (tlv->type) { case INTEL_TLV_CNVI_TOP: version->cnvi_top = get_unaligned_le32(tlv->val); From ac8aa9e0ec93a12a60230066f199f49c3b9aac3d Mon Sep 17 00:00:00 2001 From: Laxman Acharya Padhya Date: Mon, 31 Aug 2026 15:44:22 +0545 Subject: [PATCH 3/9] Bluetooth: btintel: bound firmware ID by TLV length The firmware ID is treated as a NUL-terminated string even though the TLV length is its only boundary. If the value does not contain a NUL terminator, snprintf() can read beyond the received response. Limit the conversion to the advertised TLV value length. Fixes: 164c62f958f8 ("Bluetooth: btintel: Add firmware ID to firmware name") Reviewed-by: Ali Ahmet Memis Signed-off-by: Laxman Acharya Padhya Tested-by: Kiran K Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btintel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index 352a1c83cd08..2f87047168d7 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -702,7 +702,7 @@ int btintel_parse_version_tlv(struct hci_dev *hdev, break; case INTEL_TLV_FW_ID: snprintf(version->fw_id, sizeof(version->fw_id), - "%s", tlv->val); + "%.*s", tlv->len, tlv->val); break; default: /* Ignore rest of information */ From 3a74624b5deae7f5e2b98e638687fbf9594a9781 Mon Sep 17 00:00:00 2001 From: Laxman Acharya Padhya Date: Mon, 31 Aug 2026 15:44:23 +0545 Subject: [PATCH 4/9] Bluetooth: btintel: propagate version TLV parsing errors btintel_read_version_tlv() ignores the parser return value, so setup continues with partially initialized version data after a malformed TLV causes parsing to stop. Return the parser error to the caller so an invalid response fails setup instead of being treated as successful. Keep this behavioral change separate from the bounds checks so it can be reverted independently if an existing controller sends malformed data. Signed-off-by: Laxman Acharya Padhya Reviewed-by: Ali Ahmet Memis Tested-by: Kiran K Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btintel.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index 2f87047168d7..909a265fd906 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -721,6 +721,7 @@ static int btintel_read_version_tlv(struct hci_dev *hdev, { struct sk_buff *skb; const u8 param[1] = { 0xFF }; + int err; if (!version) return -EINVAL; @@ -739,10 +740,10 @@ static int btintel_read_version_tlv(struct hci_dev *hdev, return -EIO; } - btintel_parse_version_tlv(hdev, version, skb); + err = btintel_parse_version_tlv(hdev, version, skb); kfree_skb(skb); - return 0; + return err; } /* ------- REGMAP IBT SUPPORT ------- */ From 57938bbdb9bf7fd41cbd5cd509ec10c4b22bec18 Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Fri, 28 Aug 2026 08:55:09 +0000 Subject: [PATCH 5/9] Bluetooth: hci_core: Fix race condition during device registration In hci_register_dev(), the power_on work item is queued to hdev->req_workqueue before initializing hdev->adv_monitors_idr and registering the MSFT extension via msft_register(). For devices marked with quirks such as HCI_QUIRK_RAW_DEVICE, the HCI_UNCONFIGURED flag is set on the device. When the power_on work item runs concurrently on another CPU, hci_power_on() detects that the device is unconfigured and immediately invokes hci_dev_do_close(), which calls msft_do_close(). Concurrently, msft_register() allocates the msft structure and exposes it to hdev->msft_data prior to calling mutex_init(&msft->filter_lock). If msft_do_close() executes while hdev->msft_data is already assigned but the mutex has not yet been initialized, mutex_lock(&msft->filter_lock) operates on an uninitialized mutex, triggering a DEBUG_LOCKS warning: DEBUG_LOCKS_WARN_ON(lock->magic != lock) WARNING: kernel/locking/mutex.c:625 at __mutex_lock_common kernel/locking/mutex.c:625 [inline] WARNING: kernel/locking/mutex.c:625 at __mutex_lock+0x12d8/0x1550 kernel/locking/mutex.c:821 ... Call Trace: msft_do_close+0x308/0x7b0 net/bluetooth/msft.c:693 hci_dev_close_sync+0x86b/0x10a0 net/bluetooth/hci_sync.c:5522 hci_dev_do_close net/bluetooth/hci_core.c:499 [inline] hci_power_on+0x32c/0x750 net/bluetooth/hci_core.c:937 process_one_work kernel/workqueue.c:3322 [inline] process_scheduled_works+0xa8e/0x14e0 kernel/workqueue.c:3405 worker_thread+0x92d/0xe10 kernel/workqueue.c:3486 kthread+0x388/0x470 kernel/kthread.c:436 ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 Fix this by moving the queue_work() call in hci_register_dev() to after idr_init(&hdev->adv_monitors_idr) and msft_register(hdev) so that device structures and extensions are fully initialized before asynchronous tasks can access them. Additionally, assign hdev->msft_data in msft_register() only after mutex_init(&msft->filter_lock) has completed. Fixes: 9e14606d8f38 ("Bluetooth: msft: Extended monitor tracking by address filter") Assisted-by: Gemini:gemini-3.7-flash Gemini:gemini-3.1-pro-preview syzbot Reported-by: syzbot+14ce1b05b7d5a989abbe@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=14ce1b05b7d5a989abbe Link: https://syzkaller.appspot.com/ai_job?id=2bc9e8aa-ca6d-43e2-be2c-fd5d9f649d7e Signed-off-by: Aleksandr Nogikh Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_core.c | 4 ++-- net/bluetooth/msft.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 35a1be57e386..d7355c73f93e 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2632,11 +2632,11 @@ int hci_register_dev(struct hci_dev *hdev) if (error) BT_WARN("register suspend notifier failed error:%d\n", error); - queue_work(hdev->req_workqueue, &hdev->power_on); - idr_init(&hdev->adv_monitors_idr); msft_register(hdev); + queue_work(hdev->req_workqueue, &hdev->power_on); + return id; err_wqueue: diff --git a/net/bluetooth/msft.c b/net/bluetooth/msft.c index ded68568e6c9..d9dd722db3eb 100644 --- a/net/bluetooth/msft.c +++ b/net/bluetooth/msft.c @@ -769,8 +769,8 @@ void msft_register(struct hci_dev *hdev) INIT_LIST_HEAD(&msft->handle_map); INIT_LIST_HEAD(&msft->address_filters); - hdev->msft_data = msft; mutex_init(&msft->filter_lock); + hdev->msft_data = msft; } void msft_release(struct hci_dev *hdev) From 4ef05db5b08b176a551b4a6287372045998806b0 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 30 Aug 2026 20:11:36 +0300 Subject: [PATCH 6/9] Bluetooth: L2CAP: fix chan mode for LE_CONN_REQ + EXT_FLOWCTL pchan l2cap_new_connection() sets default value of channel mode to match the parent channel. l2cap_le_connect_req() left this at the default, and created L2CAP_MODE_EXT_FLOWCTL channels if listening pchan has that mode. This causes FLAG_DEFER_SETUP channels to reply to L2CAP_LE_CONN_REQ with L2CAP_ECRED_CONN_RSP, which is incorrect. It can also result to stack OOB write (of l2cap_alloc_cid determined values) in l2cap_ecred_rsp_defer(), as l2cap_le_connect_req() does not limit maximum number of deferred channels or check for duplicate ident. Fix by setting chan->mode correctly in l2cap_le_connect_req(). Also check channel mode in l2cap_ecred_rsp_defer(), and do WARN_ON_ONCE instead of OOB write to make it less brittle. Fixes: 15f02b910562 ("Bluetooth: L2CAP: Add initial code for Enhanced Credit Based Mode") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/l2cap_core.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index ee459dd411f5..1c0b7884dc27 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3894,6 +3894,9 @@ static void l2cap_ecred_rsp_defer(struct l2cap_chan *chan, void *data) struct l2cap_ecred_conn_rsp *rsp_flex = container_of(&rsp->pdu.rsp, struct l2cap_ecred_conn_rsp, hdr); + if (chan->mode != L2CAP_MODE_EXT_FLOWCTL) + return; + /* Check if channel for outgoing connection or if it wasn't deferred * since in those cases it must be skipped. */ @@ -3904,6 +3907,10 @@ static void l2cap_ecred_rsp_defer(struct l2cap_chan *chan, void *data) /* Reset ident so only one response is sent */ chan->ident = 0; + /* Unreachable, check in l2cap_ecred_conn_req. If reached, drop rest */ + if (WARN_ON_ONCE(rsp->count >= ARRAY_SIZE(rsp->pdu.scid))) + rsp->pdu.rsp.result = cpu_to_le16(L2CAP_CR_LE_NO_MEM); + /* Include all channels pending with the same ident */ if (!rsp->pdu.rsp.result) rsp_flex->dcid[rsp->count++] = cpu_to_le16(chan->scid); @@ -5063,6 +5070,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn, __set_chan_timer(chan, chan->ops->get_sndtimeo(chan)); chan->ident = cmd->ident; + chan->mode = L2CAP_MODE_LE_FLOWCTL; if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) { l2cap_state_change(chan, BT_CONNECT2); From 56c2b5831d39dc84aad2573dc3e197af1a872a05 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 30 Aug 2026 15:04:01 +0300 Subject: [PATCH 7/9] Bluetooth: L2CAP: fix out-of-bounds write in l2cap_ecred_connect l2cap_chan_connect() tries to ensure there are no more than L2CAP_ECRED_CONN_SCID_MAX pending ECRED channels, so they fit in the same L2CAP_ECRED_CONN_REQ that l2cap_ecred_connect() constructs. However, the check only counts deferred channels. If 6 L2CAP sockets are connected at the same time in order DDDDND (D=deferred, N=non-deferred), the last can bump the total to max+1. It results to one __le16 written out of bounds of the scid array, and an invalid ECRED_CONN_REQ being sent. Fix by leaving room for the non-deferred pending ECRED channels in the counting in l2cap_chan_connect(), so the limit can't be exceeded. Move counting under same critical section where the channel is added. Although race conditions involving this appear unreachable, it's easier to see. Also add WARN_ON_ONCE check in l2cap_ecred_defer_connect() to make this less brittle. Fixes: da49b602f7f7 ("Bluetooth: L2CAP: Use DEFER_SETUP to group ECRED connections") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/l2cap_core.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 1c0b7884dc27..60833fa2835b 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1337,7 +1337,7 @@ static void l2cap_le_connect(struct l2cap_chan *chan) struct l2cap_ecred_conn_data { struct { struct l2cap_ecred_conn_req_hdr req; - __le16 scid[5]; + __le16 scid[L2CAP_ECRED_CONN_SCID_MAX]; } __packed pdu; struct l2cap_chan *chan; struct pid *pid; @@ -1365,6 +1365,10 @@ static void l2cap_ecred_defer_connect(struct l2cap_chan *chan, void *data) if (test_and_set_bit(FLAG_ECRED_CONN_REQ_SENT, &chan->flags)) return; + /* Unreachable, checked in l2cap_connect (+timer drops it if reached) */ + if (WARN_ON_ONCE(conn->count >= ARRAY_SIZE(conn->pdu.scid))) + return; + l2cap_ecred_init(chan, 0); /* Set the same ident so we can match on the rsp */ @@ -7377,6 +7381,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, goto done; } + mutex_lock(&conn->lock); + l2cap_chan_lock(chan); + if (chan->mode == L2CAP_MODE_EXT_FLOWCTL) { struct l2cap_chan_data data; @@ -7384,19 +7391,20 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, data.pid = chan->ops->get_peer_pid(chan); data.count = 1; - l2cap_chan_list(conn, l2cap_chan_by_pid, &data); + __l2cap_chan_list(conn, l2cap_chan_by_pid, &data); + + /* Leave room for non-deferred channel that ends the group. */ + if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) + data.count += 1; /* Check if there isn't too many channels being connected */ if (data.count > L2CAP_ECRED_CONN_SCID_MAX) { hci_conn_drop(hcon); err = -EPROTO; - goto done; + goto chan_unlock; } } - mutex_lock(&conn->lock); - l2cap_chan_lock(chan); - if (cid && __l2cap_get_chan_by_dcid(conn, cid)) { hci_conn_drop(hcon); err = -EBUSY; From 0d77683237270702fa93489ca759c89b4e970554 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 30 Aug 2026 15:04:02 +0300 Subject: [PATCH 8/9] Bluetooth: L2CAP: clear FLAG_DEFER_SETUP only for same PID/PSM l2cap_ecred_defer_connect() clears FLAG_DEFER_SETUP also for channels with different PID/PSM, which will not be added to the same ECRED_CONN_REQ in any case. Consequently, only one ECRED connection group can work at a time although it appears intended they would be separate for each PID/PSM combination. Fix by clearing FLAG_DEFER_SETUP only for the connections that could be added in the request. Retain test_bit(FLAG_DEFER_SETUP) before calling get_peer_pid as it may be NULL otherwise. Fixes: da49b602f7f7 ("Bluetooth: L2CAP: Use DEFER_SETUP to group ECRED connections") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/l2cap_core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 60833fa2835b..644e31160d55 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1352,7 +1352,7 @@ static void l2cap_ecred_defer_connect(struct l2cap_chan *chan, void *data) if (chan == conn->chan) return; - if (!test_and_clear_bit(FLAG_DEFER_SETUP, &chan->flags)) + if (!test_bit(FLAG_DEFER_SETUP, &chan->flags)) return; pid = chan->ops->get_peer_pid(chan); @@ -1362,6 +1362,9 @@ static void l2cap_ecred_defer_connect(struct l2cap_chan *chan, void *data) chan->mode != L2CAP_MODE_EXT_FLOWCTL || chan->state != BT_CONNECT) return; + if (!test_and_clear_bit(FLAG_DEFER_SETUP, &chan->flags)) + return; + if (test_and_set_bit(FLAG_ECRED_CONN_REQ_SENT, &chan->flags)) return; From 2deb76c21b81e42b3282224f7dd2046fe73fd1e0 Mon Sep 17 00:00:00 2001 From: Gongwei Li Date: Tue, 25 Aug 2026 10:01:45 +0800 Subject: [PATCH 9/9] Bluetooth: hci_mrvl: Fix wrong return value check of wait_on_bit_timeout() wait_on_bit_timeout() returns 0 if the bit was cleared, -EINTR if the process received a signal and the mode permitted wake up on that signal, or -EAGAIN if the timeout elapsed. It never returns 1. Hence the check "err == 1" in mrvl_load_firmware() is dead code: when the waiting task is interrupted by a signal (-EINTR), the code falls into the "else if (err)" branch and misreports it as "Firmware request timeout" with -ETIMEDOUT instead of propagating -EINTR. Fix this by testing for -EINTR so that an interrupted firmware load is properly detected and reported. Fixes: 162f812f23ba ("Bluetooth: hci_uart: Add Marvell support") Signed-off-by: Gongwei Li Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_mrvl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/bluetooth/hci_mrvl.c b/drivers/bluetooth/hci_mrvl.c index 516b8f74c434..5798a8db016e 100644 --- a/drivers/bluetooth/hci_mrvl.c +++ b/drivers/bluetooth/hci_mrvl.c @@ -307,9 +307,8 @@ static int mrvl_load_firmware(struct hci_dev *hdev, const char *name) err = wait_on_bit_timeout(&mrvl->flags, STATE_FW_REQ_PENDING, TASK_INTERRUPTIBLE, msecs_to_jiffies(2000)); - if (err == 1) { + if (err == -EINTR) { bt_dev_err(hdev, "Firmware load interrupted"); - err = -EINTR; break; } else if (err) { bt_dev_err(hdev, "Firmware request timeout");