From: Greg Kroah-Hartman Date: Mon, 13 Jul 2026 17:30:58 +0000 (+0200) Subject: 7.1-stable patches X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=974755b26d5f0ddc81d136ff3453fa8f7b5287f4;p=thirdparty%2Fkernel%2Fstable-queue.git 7.1-stable patches added patches: bluetooth-bnep-pin-l2cap-connection-during-netdev-registration.patch bluetooth-btmtksdio-fix-infinite-loop-in-btmtksdio_txrx_work.patch bluetooth-btnxpuart-fix-out-of-bounds-firmware-read-in-nxp_recv_fw_req_v3.patch bluetooth-fix-uaf-in-bt_accept_dequeue.patch bluetooth-hci_conn-fix-null-ptr-deref-in-hci_abort_conn.patch bluetooth-hci_uart-clear-hci_uart_sending-when-write_work-is-canceled.patch bluetooth-iso-avoid-null-deref-of-conn-in-iso_conn_big_sync.patch bluetooth-l2cap-cancel-pending_rx_work-before-taking-conn-lock.patch bluetooth-l2cap-validate-option-length-before-reading-conf-opt-value.patch --- diff --git a/queue-7.1/bluetooth-bnep-pin-l2cap-connection-during-netdev-registration.patch b/queue-7.1/bluetooth-bnep-pin-l2cap-connection-during-netdev-registration.patch new file mode 100644 index 0000000000..7321bb8b78 --- /dev/null +++ b/queue-7.1/bluetooth-bnep-pin-l2cap-connection-during-netdev-registration.patch @@ -0,0 +1,91 @@ +From bb067a99a0356196c0b89a95721985485ebce5a5 Mon Sep 17 00:00:00 2001 +From: Yousef Alhouseen +Date: Sun, 28 Jun 2026 02:50:58 +0200 +Subject: Bluetooth: bnep: pin L2CAP connection during netdev registration + +From: Yousef Alhouseen + +commit bb067a99a0356196c0b89a95721985485ebce5a5 upstream. + +bnep_add_connection() reads the L2CAP connection without holding the +channel lock, then passes its HCI device to register_netdev(). Controller +teardown can clear and release that connection concurrently, leaving the +network device registration path to dereference a freed parent device. + +Take a reference to the L2CAP connection while holding the channel lock. +Retain it until register_netdev() has taken the parent device reference. + +Fixes: 65f53e9802db ("Bluetooth: Access BNEP session addresses through L2CAP channel") +Reported-by: syzbot+fed5dce4553262f3b35c@syzkaller.appspotmail.com +Closes: https://syzkaller.appspot.com/bug?extid=fed5dce4553262f3b35c +Cc: stable@vger.kernel.org +Signed-off-by: Yousef Alhouseen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Greg Kroah-Hartman +--- + net/bluetooth/bnep/core.c | 27 +++++++++++++++++++++------ + 1 file changed, 21 insertions(+), 6 deletions(-) + +--- a/net/bluetooth/bnep/core.c ++++ b/net/bluetooth/bnep/core.c +@@ -562,14 +562,18 @@ static int bnep_session(void *arg) + return 0; + } + +-static struct device *bnep_get_device(struct bnep_session *session) ++static struct l2cap_conn *bnep_get_conn(struct bnep_session *session) + { +- struct l2cap_conn *conn = l2cap_pi(session->sock->sk)->chan->conn; ++ struct l2cap_chan *chan = l2cap_pi(session->sock->sk)->chan; ++ struct l2cap_conn *conn; + +- if (!conn || !conn->hcon) +- return NULL; ++ l2cap_chan_lock(chan); ++ conn = chan->conn; ++ if (conn) ++ l2cap_conn_get(conn); ++ l2cap_chan_unlock(chan); + +- return &conn->hcon->dev; ++ return conn; + } + + static const struct device_type bnep_type = { +@@ -581,6 +585,7 @@ int bnep_add_connection(struct bnep_conn + u32 valid_flags = BIT(BNEP_SETUP_RESPONSE); + struct net_device *dev; + struct bnep_session *s, *ss; ++ struct l2cap_conn *conn = NULL; + u8 dst[ETH_ALEN], src[ETH_ALEN]; + int err; + +@@ -640,10 +645,18 @@ int bnep_add_connection(struct bnep_conn + bnep_set_default_proto_filter(s); + #endif + +- SET_NETDEV_DEV(dev, bnep_get_device(s)); ++ conn = bnep_get_conn(s); ++ if (!conn) { ++ err = -ENOTCONN; ++ goto failed; ++ } ++ ++ SET_NETDEV_DEV(dev, &conn->hcon->dev); + SET_NETDEV_DEVTYPE(dev, &bnep_type); + + err = register_netdev(dev); ++ l2cap_conn_put(conn); ++ conn = NULL; + if (err) + goto failed; + +@@ -665,6 +678,8 @@ int bnep_add_connection(struct bnep_conn + return 0; + + failed: ++ if (conn) ++ l2cap_conn_put(conn); + up_write(&bnep_session_sem); + free_netdev(dev); + return err; diff --git a/queue-7.1/bluetooth-btmtksdio-fix-infinite-loop-in-btmtksdio_txrx_work.patch b/queue-7.1/bluetooth-btmtksdio-fix-infinite-loop-in-btmtksdio_txrx_work.patch new file mode 100644 index 0000000000..ec9386f90b --- /dev/null +++ b/queue-7.1/bluetooth-btmtksdio-fix-infinite-loop-in-btmtksdio_txrx_work.patch @@ -0,0 +1,56 @@ +From a257407e2bbbb099ed427719a50563f67fa366d8 Mon Sep 17 00:00:00 2001 +From: Sergey Senozhatsky +Date: Tue, 9 Jun 2026 21:10:06 +0900 +Subject: Bluetooth: btmtksdio: fix infinite loop in btmtksdio_txrx_work() + +From: Sergey Senozhatsky + +commit a257407e2bbbb099ed427719a50563f67fa366d8 upstream. + +Every once in a while we see a hung btmtksdio_flush() task: + + INFO: task kworker/u17:0:189 blocked for more than 122 seconds. + __cancel_work_timer+0x3f4/0x460 + cancel_work_sync+0x1c/0x2c + btmtksdio_flush+0x2c/0x40 + hci_dev_open_sync+0x10c4/0x2190 + [..] + +It all boils down to incorrect time_is_before_jiffies() usage in +btmtksdio_txrx_work(). The btmtksdio_txrx_work() loop is expected +to be terminated if running for longer than 5*HZ. However the +timeout check is twisted: time_is_before_jiffies(old_jiffies + 5*HZ) +evaluates to true when old_jiffies + 5*HZ is in the past i.e. when a +timeout has occurred. Using OR with time_is_before_jiffies(txrx_timeout) +means that: +- before the 5-second timeout: the condition is `int_status || false`, + so it loops as long as there are pending interrupts. +- after the 5-second timeout: the condition becomes `int_status || true`, + which is always true. + +When the loop becomes infinite btmtksdio_txrx_work() loop never +terminates and never releases the SDIO host. + +Fix loop termination condition to actually enforce a 5*HZ timeout. + +Fixes: 26270bc189ea4 ("Bluetooth: btmtksdio: move interrupt service to work") +Cc: stable@vger.kernel.org +Signed-off-by: Sergey Senozhatsky +Reviewed-by: Sean Wang +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Greg Kroah-Hartman +--- + drivers/bluetooth/btmtksdio.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/bluetooth/btmtksdio.c ++++ b/drivers/bluetooth/btmtksdio.c +@@ -620,7 +620,7 @@ static void btmtksdio_txrx_work(struct w + if (btmtksdio_rx_packet(bdev, rx_size) < 0) + bdev->hdev->stat.err_rx++; + } +- } while (int_status || time_is_before_jiffies(txrx_timeout)); ++ } while (int_status && time_is_after_jiffies(txrx_timeout)); + + /* Enable interrupt */ + if (bdev->func->irq_handler) diff --git a/queue-7.1/bluetooth-btnxpuart-fix-out-of-bounds-firmware-read-in-nxp_recv_fw_req_v3.patch b/queue-7.1/bluetooth-btnxpuart-fix-out-of-bounds-firmware-read-in-nxp_recv_fw_req_v3.patch new file mode 100644 index 0000000000..fdd833a052 --- /dev/null +++ b/queue-7.1/bluetooth-btnxpuart-fix-out-of-bounds-firmware-read-in-nxp_recv_fw_req_v3.patch @@ -0,0 +1,53 @@ +From badff6c3bed8923a1257a853f137d447976eec30 Mon Sep 17 00:00:00 2001 +From: Maoyi Xie +Date: Wed, 17 Jun 2026 16:36:52 +0800 +Subject: Bluetooth: btnxpuart: Fix out-of-bounds firmware read in nxp_recv_fw_req_v3() + +From: Maoyi Xie + +commit badff6c3bed8923a1257a853f137d447976eec30 upstream. + +During the v3 firmware download the controller sends a v3_data_req with a +32 bit offset and a 16 bit len. nxp_recv_fw_req_v3() checks only the lower +bound of the offset and then sends firmware from that offset. + + nxpdev->fw_dnld_v3_offset = offset - nxpdev->fw_v3_offset_correction; + serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data + + nxpdev->fw_dnld_v3_offset, len); + +Nothing checks that fw_dnld_v3_offset + len stays within nxpdev->fw->size, +so a controller that asks for an offset or length past the firmware image +makes the driver read past the end of nxpdev->fw->data and send that +memory back over UART. + +nxp_recv_fw_req_v1() already bounds the same write. Add the equivalent +check to the v3 path, reject the request when it falls outside the firmware +image, and zero len on the error path so the fw_v3_prev_sent bookkeeping at +free_skb stays consistent. + +Fixes: 689ca16e5232 ("Bluetooth: NXP: Add protocol support for NXP Bluetooth chipsets") +Suggested-by: Neeraj Sanjay Kale +Reviewed-by: Neeraj Sanjay Kale +Cc: stable@vger.kernel.org +Signed-off-by: Maoyi Xie +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Greg Kroah-Hartman +--- + drivers/bluetooth/btnxpuart.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/bluetooth/btnxpuart.c ++++ b/drivers/bluetooth/btnxpuart.c +@@ -1267,6 +1267,12 @@ static int nxp_recv_fw_req_v3(struct hci + } + + nxpdev->fw_dnld_v3_offset = offset - nxpdev->fw_v3_offset_correction; ++ if (nxpdev->fw_dnld_v3_offset >= nxpdev->fw->size || ++ len > nxpdev->fw->size - nxpdev->fw_dnld_v3_offset) { ++ bt_dev_err(hdev, "FW download out of bounds, ignoring request"); ++ len = 0; ++ goto free_skb; ++ } + serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data + + nxpdev->fw_dnld_v3_offset, len); + diff --git a/queue-7.1/bluetooth-fix-uaf-in-bt_accept_dequeue.patch b/queue-7.1/bluetooth-fix-uaf-in-bt_accept_dequeue.patch new file mode 100644 index 0000000000..a775853004 --- /dev/null +++ b/queue-7.1/bluetooth-fix-uaf-in-bt_accept_dequeue.patch @@ -0,0 +1,97 @@ +From 4bd0b274054f2679f28b70222b607bb0afc3ab9a Mon Sep 17 00:00:00 2001 +From: Yousef Alhouseen +Date: Sun, 28 Jun 2026 02:23:05 +0200 +Subject: Bluetooth: fix UAF in bt_accept_dequeue() + +From: Yousef Alhouseen + +commit 4bd0b274054f2679f28b70222b607bb0afc3ab9a upstream. + +bt_accept_get() takes a temporary reference before dropping the accept +queue lock. bt_accept_dequeue() currently drops that reference before +bt_accept_unlink(), leaving only the queue reference. + +bt_accept_unlink() drops the queue reference. The subsequent +sock_hold() therefore accesses freed memory if it was the final +reference, as observed by KASAN during listening L2CAP socket cleanup. + +Retain the temporary queue-walk reference through unlink and hand it to +the caller on success. Drop it explicitly on the closed and +not-yet-connected paths. + +Fixes: ab1513597c6c ("Bluetooth: fix UAF in l2cap_sock_cleanup_listen() vs l2cap_conn_del()") +Reported-by: syzbot+674ff7e4d7fdfd572afc@syzkaller.appspotmail.com +Closes: https://syzkaller.appspot.com/bug?extid=674ff7e4d7fdfd572afc +Cc: stable@vger.kernel.org +Signed-off-by: Yousef Alhouseen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Greg Kroah-Hartman +--- + net/bluetooth/af_bluetooth.c | 17 +++-------------- + net/bluetooth/l2cap_sock.c | 4 ++-- + 2 files changed, 5 insertions(+), 16 deletions(-) + +--- a/net/bluetooth/af_bluetooth.c ++++ b/net/bluetooth/af_bluetooth.c +@@ -308,7 +308,7 @@ struct sock *bt_accept_dequeue(struct so + + restart: + for (sk = bt_accept_get(parent, NULL); sk; sk = next) { +- /* Prevent early freeing of sk due to unlink and sock_kill */ ++ /* The reference from bt_accept_get() keeps sk alive. */ + lock_sock(sk); + + /* Check sk has not already been unlinked via +@@ -324,13 +324,11 @@ restart: + + next = bt_accept_get(parent, sk); + +- /* sk is safely in the parent list so reduce reference count */ +- sock_put(sk); +- + /* FIXME: Is this check still needed */ + if (sk->sk_state == BT_CLOSED) { + bt_accept_unlink(sk); + release_sock(sk); ++ sock_put(sk); + continue; + } + +@@ -340,16 +338,6 @@ restart: + if (newsock) + sock_graft(sk, newsock); + +- /* Hand the caller a reference taken while sk is +- * still locked. bt_accept_unlink() just dropped +- * the accept-queue reference; without this hold a +- * concurrent teardown (e.g. l2cap_conn_del() -> +- * l2cap_sock_kill()) could free sk between +- * release_sock() and the caller using it. Every +- * caller drops this with sock_put() when done. +- */ +- sock_hold(sk); +- + release_sock(sk); + if (next) + sock_put(next); +@@ -357,6 +345,7 @@ restart: + } + + release_sock(sk); ++ sock_put(sk); + } + + return NULL; +--- a/net/bluetooth/l2cap_sock.c ++++ b/net/bluetooth/l2cap_sock.c +@@ -1482,8 +1482,8 @@ static void l2cap_sock_cleanup_listen(st + + /* Close not yet accepted channels. + * +- * bt_accept_dequeue() now returns sk with an extra reference held +- * (taken while sk was still locked) so a concurrent l2cap_conn_del() ++ * bt_accept_dequeue() returns sk with its temporary queue-walk ++ * reference held, so a concurrent l2cap_conn_del() + * -> l2cap_sock_kill() cannot free sk under us. + * + * cleanup_listen() runs under the parent sk lock, so unlike diff --git a/queue-7.1/bluetooth-hci_conn-fix-null-ptr-deref-in-hci_abort_conn.patch b/queue-7.1/bluetooth-hci_conn-fix-null-ptr-deref-in-hci_abort_conn.patch new file mode 100644 index 0000000000..1566768912 --- /dev/null +++ b/queue-7.1/bluetooth-hci_conn-fix-null-ptr-deref-in-hci_abort_conn.patch @@ -0,0 +1,279 @@ +From 12917f591cea1af36087dba5b9ec888652f0b42a Mon Sep 17 00:00:00 2001 +From: Siwei Zhang +Date: Mon, 15 Jun 2026 11:33:05 -0400 +Subject: Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn() + +From: Siwei Zhang + +commit 12917f591cea1af36087dba5b9ec888652f0b42a upstream. + +hci_abort_conn() read hci_skb_event(hdev->sent_cmd) when a connection +was pending, but hdev->sent_cmd can be NULL while req_status is still +HCI_REQ_PEND, leading to a NULL pointer dereference and a general +protection fault from the hci_rx_work() receive path. + +Instead of inspecting hdev->sent_cmd, track the in-flight create +connection command with a new per-connection HCI_CONN_CREATE flag and +route all cancellation through hci_cancel_connect_sync(), which +dispatches to a dedicated per-type cancel function. The create command +is in exactly one of two states: still queued, or in flight. The cancel +function holds cmd_sync_work_lock across the whole decision: the worker +takes this lock to dequeue every entry, so while it is held a queued +command cannot start running and an in-flight command cannot complete +and let the next command become pending. This keeps the flag test and +hci_cmd_sync_cancel() atomic with respect to the worker, so a queued +command is simply dequeued, and an in-flight command owned by this +connection is cancelled without the risk of cancelling an unrelated +command that became pending in the meantime. CIS uses the same flag +mechanism via HCI_CONN_CREATE_CIS but cannot be dequeued per-connection. + +hci_acl_create_conn_sync() and hci_le_create_conn_sync() clear +HCI_CONN_CREATE after the create command completes, but the command +status handler can free conn via hci_conn_del() (for example when the +controller rejects the connection) while the worker is still blocked on +the connection complete event. Hold a reference on conn across the +create command so the flag can be cleared without a use-after-free. + +Fixes: a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for aborting connections") +Cc: stable@vger.kernel.org +Suggested-by: XIAO WU +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Siwei Zhang +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Greg Kroah-Hartman +--- + include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_conn.c | 21 ------ + net/bluetooth/hci_sync.c | 133 ++++++++++++++++++++++++++++++++++----- + 3 files changed, 123 insertions(+), 32 deletions(-) + +--- a/include/net/bluetooth/hci_core.h ++++ b/include/net/bluetooth/hci_core.h +@@ -988,6 +988,7 @@ enum { + HCI_CONN_AUTH_FAILURE, + HCI_CONN_PER_ADV, + HCI_CONN_BIG_CREATED, ++ HCI_CONN_CREATE, + HCI_CONN_CREATE_CIS, + HCI_CONN_CREATE_BIG_SYNC, + HCI_CONN_BIG_SYNC, +--- a/net/bluetooth/hci_conn.c ++++ b/net/bluetooth/hci_conn.c +@@ -3181,26 +3181,11 @@ int hci_abort_conn(struct hci_conn *conn + + conn->abort_reason = reason; + +- /* If the connection is pending check the command opcode since that +- * might be blocking on hci_cmd_sync_work while waiting its respective +- * event so we need to hci_cmd_sync_cancel to cancel it. +- * +- * hci_connect_le serializes the connection attempts so only one +- * connection can be in BT_CONNECT at time. ++ /* Cancel the connect attempt. A return of 0 means the create command ++ * was still queued and got dequeued, so there is nothing to disconnect. + */ +- if (conn->state == BT_CONNECT && READ_ONCE(hdev->req_status) == HCI_REQ_PEND) { +- switch (hci_skb_event(hdev->sent_cmd)) { +- case HCI_EV_CONN_COMPLETE: +- case HCI_EV_LE_CONN_COMPLETE: +- case HCI_EV_LE_ENHANCED_CONN_COMPLETE: +- case HCI_EVT_LE_CIS_ESTABLISHED: +- hci_cmd_sync_cancel(hdev, ECANCELED); +- break; +- } +- /* Cancel connect attempt if still queued/pending */ +- } else if (!hci_cancel_connect_sync(hdev, conn)) { ++ if (!hci_cancel_connect_sync(hdev, conn)) + return 0; +- } + + /* Run immediately if on cmd_sync_work since this may be called + * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does +--- a/net/bluetooth/hci_sync.c ++++ b/net/bluetooth/hci_sync.c +@@ -6611,6 +6611,11 @@ static int hci_le_create_conn_sync(struc + + bt_dev_dbg(hdev, "conn %p", conn); + ++ /* Hold a reference so conn stays valid for the HCI_CONN_CREATE ++ * clear_bit() at done. ++ */ ++ hci_conn_get(conn); ++ + clear_bit(HCI_CONN_SCANNING, &conn->flags); + conn->state = BT_CONNECT; + +@@ -6623,6 +6628,7 @@ static int hci_le_create_conn_sync(struc + hdev->le_scan_type == LE_SCAN_ACTIVE && + !hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) { + hci_conn_del(conn); ++ hci_conn_put(conn); + return -EBUSY; + } + +@@ -6668,6 +6674,12 @@ static int hci_le_create_conn_sync(struc + &own_addr_type); + if (err) + goto done; ++ ++ /* Mark create connection in flight so hci_cancel_connect_sync() can ++ * cancel it while blocking on the connection complete event. ++ */ ++ set_bit(HCI_CONN_CREATE, &conn->flags); ++ + /* Send command LE Extended Create Connection if supported */ + if (use_ext_conn(hdev)) { + err = hci_le_ext_create_conn_sync(hdev, conn, own_addr_type); +@@ -6703,11 +6715,14 @@ static int hci_le_create_conn_sync(struc + conn->conn_timeout, NULL); + + done: ++ clear_bit(HCI_CONN_CREATE, &conn->flags); ++ + if (err == -ETIMEDOUT) + hci_le_connect_cancel_sync(hdev, conn, 0x00); + + /* Re-enable advertising after the connection attempt is finished. */ + hci_resume_advertising_sync(hdev); ++ hci_conn_put(conn); + return err; + } + +@@ -6982,10 +6997,25 @@ static int hci_acl_create_conn_sync(stru + else + cp.role_switch = 0x00; + +- return __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN, +- sizeof(cp), &cp, +- HCI_EV_CONN_COMPLETE, +- conn->conn_timeout, NULL); ++ /* Hold a reference so conn stays valid for the HCI_CONN_CREATE ++ * clear_bit() below. ++ */ ++ hci_conn_get(conn); ++ ++ /* Mark create connection in flight so hci_cancel_connect_sync() can ++ * cancel it while blocking on the connection complete event. ++ */ ++ set_bit(HCI_CONN_CREATE, &conn->flags); ++ ++ err = __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN, ++ sizeof(cp), &cp, ++ HCI_EV_CONN_COMPLETE, ++ conn->conn_timeout, NULL); ++ ++ clear_bit(HCI_CONN_CREATE, &conn->flags); ++ hci_conn_put(conn); ++ ++ return err; + } + + int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn) +@@ -7037,22 +7067,97 @@ int hci_connect_le_sync(struct hci_dev * + return (err == -EEXIST) ? 0 : err; + } + +-int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn) ++static int hci_acl_cancel_create_conn_sync(struct hci_dev *hdev, ++ struct hci_conn *conn) ++{ ++ struct hci_cmd_sync_work_entry *entry; ++ int err = -EBUSY; ++ ++ /* cmd_sync_work_lock makes the HCI_CONN_CREATE test and the cancel ++ * atomic against the worker, which takes this lock to dequeue every ++ * entry: while it is held no other command can become pending, so ++ * hci_cmd_sync_cancel() cannot cancel an unrelated command. ++ */ ++ mutex_lock(&hdev->cmd_sync_work_lock); ++ ++ /* In flight: this connection owns the pending request, cancel it. */ ++ if (test_bit(HCI_CONN_CREATE, &conn->flags)) { ++ hci_cmd_sync_cancel(hdev, ECANCELED); ++ goto unlock; ++ } ++ ++ /* Still queued: a successful dequeue means it never started, so there ++ * is nothing to disconnect. ++ */ ++ entry = _hci_cmd_sync_lookup_entry(hdev, hci_acl_create_conn_sync, conn, ++ NULL); ++ if (entry) { ++ _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED); ++ err = 0; ++ } ++ ++unlock: ++ mutex_unlock(&hdev->cmd_sync_work_lock); ++ return err; ++} ++ ++static int hci_le_cancel_create_conn_sync(struct hci_dev *hdev, ++ struct hci_conn *conn) + { +- if (conn->state != BT_OPEN) +- return -EINVAL; ++ struct hci_cmd_sync_work_entry *entry; ++ int err = -EBUSY; ++ ++ /* cmd_sync_work_lock keeps the HCI_CONN_CREATE test and the cancel ++ * atomic against the cmd_sync worker. ++ */ ++ mutex_lock(&hdev->cmd_sync_work_lock); ++ ++ if (test_bit(HCI_CONN_CREATE, &conn->flags)) { ++ hci_cmd_sync_cancel(hdev, ECANCELED); ++ goto unlock; ++ } + ++ entry = _hci_cmd_sync_lookup_entry(hdev, hci_le_create_conn_sync, conn, ++ create_le_conn_complete); ++ if (entry) { ++ _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED); ++ err = 0; ++ } ++ ++unlock: ++ mutex_unlock(&hdev->cmd_sync_work_lock); ++ return err; ++} ++ ++static int hci_cis_cancel_create_conn_sync(struct hci_dev *hdev, ++ struct hci_conn *conn) ++{ ++ /* LE Create CIS is shared by the whole CIG and cannot be dequeued ++ * per-connection, so only an in-flight command can be cancelled. ++ * cmd_sync_work_lock keeps the test and the cancel atomic against the ++ * cmd_sync worker. ++ */ ++ mutex_lock(&hdev->cmd_sync_work_lock); ++ ++ if (test_bit(HCI_CONN_CREATE_CIS, &conn->flags)) ++ hci_cmd_sync_cancel(hdev, ECANCELED); ++ ++ mutex_unlock(&hdev->cmd_sync_work_lock); ++ return -EBUSY; ++} ++ ++int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn) ++{ + switch (conn->type) { + case ACL_LINK: +- return !hci_cmd_sync_dequeue_once(hdev, +- hci_acl_create_conn_sync, +- conn, NULL); ++ return hci_acl_cancel_create_conn_sync(hdev, conn); + case LE_LINK: +- return !hci_cmd_sync_dequeue_once(hdev, hci_le_create_conn_sync, +- conn, create_le_conn_complete); ++ return hci_le_cancel_create_conn_sync(hdev, conn); ++ case CIS_LINK: ++ return hci_cis_cancel_create_conn_sync(hdev, conn); ++ default: ++ return -ENOENT; + } +- +- return -ENOENT; + } + + int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn, diff --git a/queue-7.1/bluetooth-hci_uart-clear-hci_uart_sending-when-write_work-is-canceled.patch b/queue-7.1/bluetooth-hci_uart-clear-hci_uart_sending-when-write_work-is-canceled.patch new file mode 100644 index 0000000000..ceba6e7794 --- /dev/null +++ b/queue-7.1/bluetooth-hci_uart-clear-hci_uart_sending-when-write_work-is-canceled.patch @@ -0,0 +1,71 @@ +From 1b0d946d6f08bd39211385bc703a440911b41e46 Mon Sep 17 00:00:00 2001 +From: Pauli Virtanen +Date: Sat, 13 Jun 2026 21:43:37 +0300 +Subject: Bluetooth: hci_uart: clear HCI_UART_SENDING when write_work is canceled + +From: Pauli Virtanen + +commit 1b0d946d6f08bd39211385bc703a440911b41e46 upstream. + +HCI_UART_SENDING bit in tx_state means write_work is pending and blocks +queueing it again. Currently this bit is not cleared when canceling the +work in hci_uart_close(), which blocks future writes when device is +reopened later if write_work was pending. + +Fix by clearing HCI_UART_SENDING when canceling the work. + +Also make clearing of tx_skb safe by using disable_work_sync + +enable_work instead of just cancel_work_sync. hci_uart_flush() purges +the proto tx queue so we can cancel the pending write_work there, +instead of doing it just in hci_uart_close(). Re-enable and possibly +requeue the work after queue flush. + +Fixes: c1bb9336ae6b ("Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths") +Link: https://lore.kernel.org/linux-bluetooth/07e0a28650773abec711ee492fdb1bf5d21a6c98.camel@iki.fi/ +Cc: stable@vger.kernel.org +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Greg Kroah-Hartman +--- + drivers/bluetooth/hci_ldisc.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +--- a/drivers/bluetooth/hci_ldisc.c ++++ b/drivers/bluetooth/hci_ldisc.c +@@ -239,6 +239,8 @@ static int hci_uart_flush(struct hci_dev + + BT_DBG("hdev %p tty %p", hdev, tty); + ++ disable_work_sync(&hu->write_work); ++ + if (hu->tx_skb) { + kfree_skb(hu->tx_skb); hu->tx_skb = NULL; + } +@@ -254,6 +256,14 @@ static int hci_uart_flush(struct hci_dev + + percpu_up_read(&hu->proto_lock); + ++ /* Resume TX. Also reschedule in case work was queued concurrently; ++ * this may schedule write_work although there's nothing to do. ++ */ ++ enable_work(&hu->write_work); ++ clear_bit(HCI_UART_SENDING, &hu->tx_state); ++ if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state)) ++ hci_uart_tx_wakeup(hu); ++ + return 0; + } + +@@ -271,12 +281,8 @@ static int hci_uart_open(struct hci_dev + /* Close device */ + static int hci_uart_close(struct hci_dev *hdev) + { +- struct hci_uart *hu = hci_get_drvdata(hdev); +- + BT_DBG("hdev %p", hdev); + +- cancel_work_sync(&hu->write_work); +- + hci_uart_flush(hdev); + hdev->flush = NULL; + return 0; diff --git a/queue-7.1/bluetooth-iso-avoid-null-deref-of-conn-in-iso_conn_big_sync.patch b/queue-7.1/bluetooth-iso-avoid-null-deref-of-conn-in-iso_conn_big_sync.patch new file mode 100644 index 0000000000..f0fb757433 --- /dev/null +++ b/queue-7.1/bluetooth-iso-avoid-null-deref-of-conn-in-iso_conn_big_sync.patch @@ -0,0 +1,74 @@ +From d5541eb148da72d5e0a1bca8ecd171f9fc8b366f Mon Sep 17 00:00:00 2001 +From: Muhammad Bilal +Date: Sun, 21 Jun 2026 21:23:05 +0500 +Subject: Bluetooth: ISO: avoid NULL deref of conn in iso_conn_big_sync() + +From: Muhammad Bilal + +commit d5541eb148da72d5e0a1bca8ecd171f9fc8b366f upstream. + +iso_conn_big_sync() drops the socket lock to call hci_get_route() and +then re-acquires it, but dereferences iso_pi(sk)->conn->hcon afterwards +without re-checking that conn is still valid. + +While the lock is dropped, the connection can be torn down under the +same socket lock: iso_disconn_cfm() -> iso_conn_del() -> iso_chan_del() +sets iso_pi(sk)->conn to NULL (and the broadcast teardown path can also +clear conn->hcon on its own). When iso_conn_big_sync() re-acquires the +lock and reads conn->hcon, conn may be NULL, causing a NULL pointer +dereference (hcon is the first member of struct iso_conn). + +This path is reached from iso_sock_recvmsg() for a PA-sync broadcast +sink socket (BT_SK_DEFER_SETUP | BT_SK_PA_SYNC), so the dropped-lock +window can race with connection teardown driven by controller events. + +Re-validate iso_pi(sk)->conn and its hcon after re-acquiring the socket +lock and bail out if the connection went away, as already done in the +sibling iso_sock_rebind_bc(). + +Fixes: 7a17308c17880d ("Bluetooth: iso: Fix circular lock in iso_conn_big_sync") +Cc: stable@vger.kernel.org +Signed-off-by: Muhammad Bilal +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Greg Kroah-Hartman +--- + net/bluetooth/iso.c | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -1589,6 +1589,7 @@ static void iso_conn_big_sync(struct soc + { + int err; + struct hci_dev *hdev; ++ struct iso_conn *conn; + bdaddr_t src, dst; + u8 src_type; + +@@ -1611,8 +1612,17 @@ static void iso_conn_big_sync(struct soc + hci_dev_lock(hdev); + lock_sock(sk); + ++ /* The socket lock was dropped for hci_get_route(), so the connection ++ * may have been torn down meanwhile: iso_chan_del() clears conn and ++ * the broadcast teardown path can clear conn->hcon on its own. Check ++ * both before dereferencing conn->hcon. ++ */ ++ conn = iso_pi(sk)->conn; ++ if (!conn || !conn->hcon) ++ goto unlock; ++ + if (!test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) { +- err = hci_conn_big_create_sync(hdev, iso_pi(sk)->conn->hcon, ++ err = hci_conn_big_create_sync(hdev, conn->hcon, + &iso_pi(sk)->qos, + iso_pi(sk)->sync_handle, + iso_pi(sk)->bc_num_bis, +@@ -1621,6 +1631,7 @@ static void iso_conn_big_sync(struct soc + bt_dev_err(hdev, "hci_big_create_sync: %d", err); + } + ++unlock: + release_sock(sk); + hci_dev_unlock(hdev); + hci_dev_put(hdev); diff --git a/queue-7.1/bluetooth-l2cap-cancel-pending_rx_work-before-taking-conn-lock.patch b/queue-7.1/bluetooth-l2cap-cancel-pending_rx_work-before-taking-conn-lock.patch new file mode 100644 index 0000000000..260bfa2ddd --- /dev/null +++ b/queue-7.1/bluetooth-l2cap-cancel-pending_rx_work-before-taking-conn-lock.patch @@ -0,0 +1,65 @@ +From 2641a9e0a1dd4af2e21995470a21d55dd35e5203 Mon Sep 17 00:00:00 2001 +From: Runyu Xiao +Date: Wed, 17 Jun 2026 23:36:13 +0800 +Subject: Bluetooth: L2CAP: cancel pending_rx_work before taking conn->lock + +From: Runyu Xiao + +commit 2641a9e0a1dd4af2e21995470a21d55dd35e5203 upstream. + +l2cap_conn_del() takes conn->lock and then calls cancel_work_sync() for +pending_rx_work. process_pending_rx() takes the same mutex, so teardown +can deadlock against the worker it is flushing. + +This issue was found by our static analysis tool and then manually +reviewed against the current tree. + +The grounded PoC kept the l2cap_conn_ready() -> queue_work(..., +&conn->pending_rx_work) submit path, the l2cap_conn_del() -> +cancel_work_sync(&conn->pending_rx_work) teardown path, and the +process_pending_rx() -> mutex_lock(&conn->lock) worker edge. Lockdep +reported: + + WARNING: possible circular locking dependency detected + process_pending_rx+0x21/0x2a [vuln_msv] + l2cap_conn_del.constprop.0+0x3f/0x4e [vuln_msv] + *** DEADLOCK *** + +Cancel pending_rx_work before taking conn->lock, matching the existing +lock-before-drain ordering used for the two delayed works in the same +teardown path. The pending_rx queue is still purged after the work has +been cancelled and conn->lock has been acquired. + +Fixes: 7ab56c3a6ecc ("Bluetooth: Fix deadlock in l2cap_conn_del()") +Cc: stable@vger.kernel.org +Signed-off-by: Runyu Xiao +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Greg Kroah-Hartman +--- + net/bluetooth/l2cap_core.c | 10 ++-------- + 1 file changed, 2 insertions(+), 8 deletions(-) + +--- a/net/bluetooth/l2cap_core.c ++++ b/net/bluetooth/l2cap_core.c +@@ -1778,19 +1778,13 @@ static void l2cap_conn_del(struct hci_co + disable_delayed_work_sync(&conn->info_timer); + disable_delayed_work_sync(&conn->id_addr_timer); + ++ cancel_work_sync(&conn->pending_rx_work); ++ + mutex_lock(&conn->lock); + + kfree_skb(conn->rx_skb); + + skb_queue_purge(&conn->pending_rx); +- +- /* We can not call flush_work(&conn->pending_rx_work) here since we +- * might block if we are running on a worker from the same workqueue +- * pending_rx_work is waiting on. +- */ +- if (work_pending(&conn->pending_rx_work)) +- cancel_work_sync(&conn->pending_rx_work); +- + ida_destroy(&conn->tx_ida); + + l2cap_unregister_all_users(conn); diff --git a/queue-7.1/bluetooth-l2cap-validate-option-length-before-reading-conf-opt-value.patch b/queue-7.1/bluetooth-l2cap-validate-option-length-before-reading-conf-opt-value.patch new file mode 100644 index 0000000000..7faf0d5eaf --- /dev/null +++ b/queue-7.1/bluetooth-l2cap-validate-option-length-before-reading-conf-opt-value.patch @@ -0,0 +1,135 @@ +From 687617555cedfb74c9e3cb85d759b908dcb17856 Mon Sep 17 00:00:00 2001 +From: Muhammad Bilal +Date: Sun, 21 Jun 2026 00:56:35 +0500 +Subject: Bluetooth: L2CAP: validate option length before reading conf opt value + +From: Muhammad Bilal + +commit 687617555cedfb74c9e3cb85d759b908dcb17856 upstream. + +l2cap_get_conf_opt() derives the option length from the +attacker-controlled opt->len field and immediately dereferences +opt->val (as u8, get_unaligned_le16() or get_unaligned_le32(), or a +raw pointer for the default case) before any caller has confirmed +that opt->len bytes are present in the buffer. The callers +(l2cap_parse_conf_req(), l2cap_parse_conf_rsp() and +l2cap_conf_rfc_get()) only detect a malformed option afterwards, once +the running length has gone negative, by which point the +out-of-bounds read has already executed. + +An existing post-hoc length check keeps the garbage value from being +consumed, so this is not a data leak in the current control flow. It +is still a validate-after-use ordering bug: up to 4 bytes are read +past the end of the buffer before it is known to contain them, and it +is fragile to future changes in the callers. + +Fix it at the source. Pass the end of the buffer into +l2cap_get_conf_opt() and refuse to touch opt->val unless the full +option (header + value) fits. Each caller computes an end pointer +once before the loop and checks the return value directly instead of +inferring the error from a negative length. + +Fixes: 7c9cbd0b5e38 ("Bluetooth: Verify that l2cap_get_conf_opt provides large enough buffer") +Cc: stable@vger.kernel.org +Signed-off-by: Muhammad Bilal +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Greg Kroah-Hartman +--- + net/bluetooth/l2cap_core.c | 36 ++++++++++++++++++++++++++++-------- + 1 file changed, 28 insertions(+), 8 deletions(-) + +--- a/net/bluetooth/l2cap_core.c ++++ b/net/bluetooth/l2cap_core.c +@@ -3048,13 +3048,24 @@ fail: + return NULL; + } + +-static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, +- unsigned long *val) ++static inline int l2cap_get_conf_opt(void **ptr, void *end, int *type, ++ int *olen, unsigned long *val) + { + struct l2cap_conf_opt *opt = *ptr; + int len; + ++ /* opt->len is attacker-controlled. Validate that the full option ++ * (header + value) actually fits in the buffer before touching ++ * opt->val, otherwise the switch below reads past the end of the ++ * caller's buffer. ++ */ ++ if (end - *ptr < L2CAP_CONF_OPT_SIZE) ++ return -EINVAL; ++ + len = L2CAP_CONF_OPT_SIZE + opt->len; ++ if (end - *ptr < len) ++ return -EINVAL; ++ + *ptr += len; + + *type = opt->type; +@@ -3426,6 +3437,7 @@ static int l2cap_parse_conf_req(struct l + void *ptr = rsp->data; + void *endptr = data + data_size; + void *req = chan->conf_req; ++ void *req_end = req + chan->conf_len; + int len = chan->conf_len; + int type, hint, olen; + unsigned long val; +@@ -3439,9 +3451,11 @@ static int l2cap_parse_conf_req(struct l + BT_DBG("chan %p", chan); + + while (len >= L2CAP_CONF_OPT_SIZE) { +- len -= l2cap_get_conf_opt(&req, &type, &olen, &val); +- if (len < 0) ++ int ret = l2cap_get_conf_opt(&req, req_end, &type, &olen, &val); ++ ++ if (ret < 0) + break; ++ len -= ret; + + hint = type & L2CAP_CONF_HINT; + type &= L2CAP_CONF_MASK; +@@ -3669,6 +3683,7 @@ static int l2cap_parse_conf_rsp(struct l + struct l2cap_conf_req *req = data; + void *ptr = req->data; + void *endptr = data + size; ++ void *rsp_end = rsp + len; + int type, olen; + unsigned long val; + struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC }; +@@ -3677,9 +3692,11 @@ static int l2cap_parse_conf_rsp(struct l + BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data); + + while (len >= L2CAP_CONF_OPT_SIZE) { +- len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); +- if (len < 0) ++ int ret = l2cap_get_conf_opt(&rsp, rsp_end, &type, &olen, &val); ++ ++ if (ret < 0) + break; ++ len -= ret; + + switch (type) { + case L2CAP_CONF_MTU: +@@ -3930,6 +3947,7 @@ static void l2cap_conf_rfc_get(struct l2 + { + int type, olen; + unsigned long val; ++ void *rsp_end = rsp + len; + /* Use sane default values in case a misbehaving remote device + * did not send an RFC or extended window size option. + */ +@@ -3948,9 +3966,11 @@ static void l2cap_conf_rfc_get(struct l2 + return; + + while (len >= L2CAP_CONF_OPT_SIZE) { +- len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); +- if (len < 0) ++ int ret = l2cap_get_conf_opt(&rsp, rsp_end, &type, &olen, &val); ++ ++ if (ret < 0) + break; ++ len -= ret; + + switch (type) { + case L2CAP_CONF_RFC: diff --git a/queue-7.1/series b/queue-7.1/series index fc9cf7c704..0261f311ae 100644 --- a/queue-7.1/series +++ b/queue-7.1/series @@ -201,3 +201,12 @@ netfilter-ebtables-module-names-must-be-null-terminated.patch netfilter-ebtables-terminate-table-name-before-find_table_lock.patch netfilter-flowtable-fix-offloaded-ct-timeout-never-being-extended.patch netfilter-flowtable-ipip-tunnel-hardware-offload-is-not-yet-support.patch +bluetooth-btmtksdio-fix-infinite-loop-in-btmtksdio_txrx_work.patch +bluetooth-bnep-pin-l2cap-connection-during-netdev-registration.patch +bluetooth-btnxpuart-fix-out-of-bounds-firmware-read-in-nxp_recv_fw_req_v3.patch +bluetooth-fix-uaf-in-bt_accept_dequeue.patch +bluetooth-hci_conn-fix-null-ptr-deref-in-hci_abort_conn.patch +bluetooth-hci_uart-clear-hci_uart_sending-when-write_work-is-canceled.patch +bluetooth-iso-avoid-null-deref-of-conn-in-iso_conn_big_sync.patch +bluetooth-l2cap-cancel-pending_rx_work-before-taking-conn-lock.patch +bluetooth-l2cap-validate-option-length-before-reading-conf-opt-value.patch