]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.18-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 13 Jul 2026 17:31:12 +0000 (19:31 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 13 Jul 2026 17:31:12 +0000 (19:31 +0200)
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

queue-6.18/bluetooth-bnep-pin-l2cap-connection-during-netdev-registration.patch [new file with mode: 0644]
queue-6.18/bluetooth-btmtksdio-fix-infinite-loop-in-btmtksdio_txrx_work.patch [new file with mode: 0644]
queue-6.18/bluetooth-btnxpuart-fix-out-of-bounds-firmware-read-in-nxp_recv_fw_req_v3.patch [new file with mode: 0644]
queue-6.18/bluetooth-fix-uaf-in-bt_accept_dequeue.patch [new file with mode: 0644]
queue-6.18/bluetooth-hci_conn-fix-null-ptr-deref-in-hci_abort_conn.patch [new file with mode: 0644]
queue-6.18/bluetooth-hci_uart-clear-hci_uart_sending-when-write_work-is-canceled.patch [new file with mode: 0644]
queue-6.18/bluetooth-iso-avoid-null-deref-of-conn-in-iso_conn_big_sync.patch [new file with mode: 0644]
queue-6.18/bluetooth-l2cap-cancel-pending_rx_work-before-taking-conn-lock.patch [new file with mode: 0644]
queue-6.18/bluetooth-l2cap-validate-option-length-before-reading-conf-opt-value.patch [new file with mode: 0644]
queue-6.18/series

diff --git a/queue-6.18/bluetooth-bnep-pin-l2cap-connection-during-netdev-registration.patch b/queue-6.18/bluetooth-bnep-pin-l2cap-connection-during-netdev-registration.patch
new file mode 100644 (file)
index 0000000..7321bb8
--- /dev/null
@@ -0,0 +1,91 @@
+From bb067a99a0356196c0b89a95721985485ebce5a5 Mon Sep 17 00:00:00 2001
+From: Yousef Alhouseen <alhouseenyousef@gmail.com>
+Date: Sun, 28 Jun 2026 02:50:58 +0200
+Subject: Bluetooth: bnep: pin L2CAP connection during netdev registration
+
+From: Yousef Alhouseen <alhouseenyousef@gmail.com>
+
+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 <alhouseenyousef@gmail.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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-6.18/bluetooth-btmtksdio-fix-infinite-loop-in-btmtksdio_txrx_work.patch b/queue-6.18/bluetooth-btmtksdio-fix-infinite-loop-in-btmtksdio_txrx_work.patch
new file mode 100644 (file)
index 0000000..95279f3
--- /dev/null
@@ -0,0 +1,56 @@
+From a257407e2bbbb099ed427719a50563f67fa366d8 Mon Sep 17 00:00:00 2001
+From: Sergey Senozhatsky <senozhatsky@chromium.org>
+Date: Tue, 9 Jun 2026 21:10:06 +0900
+Subject: Bluetooth: btmtksdio: fix infinite loop in btmtksdio_txrx_work()
+
+From: Sergey Senozhatsky <senozhatsky@chromium.org>
+
+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 <senozhatsky@chromium.org>
+Reviewed-by: Sean Wang <sean.wang@mediatek.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/bluetooth/btmtksdio.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/bluetooth/btmtksdio.c
++++ b/drivers/bluetooth/btmtksdio.c
+@@ -607,7 +607,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-6.18/bluetooth-btnxpuart-fix-out-of-bounds-firmware-read-in-nxp_recv_fw_req_v3.patch b/queue-6.18/bluetooth-btnxpuart-fix-out-of-bounds-firmware-read-in-nxp_recv_fw_req_v3.patch
new file mode 100644 (file)
index 0000000..fdd833a
--- /dev/null
@@ -0,0 +1,53 @@
+From badff6c3bed8923a1257a853f137d447976eec30 Mon Sep 17 00:00:00 2001
+From: Maoyi Xie <maoyixie.tju@gmail.com>
+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 <maoyixie.tju@gmail.com>
+
+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 <neeraj.sanjaykale@nxp.com>
+Reviewed-by: Neeraj Sanjay Kale <neeraj.sanjaykale@nxp.com>
+Cc: stable@vger.kernel.org
+Signed-off-by: Maoyi Xie <maoyixie.tju@gmail.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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-6.18/bluetooth-fix-uaf-in-bt_accept_dequeue.patch b/queue-6.18/bluetooth-fix-uaf-in-bt_accept_dequeue.patch
new file mode 100644 (file)
index 0000000..d33acba
--- /dev/null
@@ -0,0 +1,97 @@
+From 4bd0b274054f2679f28b70222b607bb0afc3ab9a Mon Sep 17 00:00:00 2001
+From: Yousef Alhouseen <alhouseenyousef@gmail.com>
+Date: Sun, 28 Jun 2026 02:23:05 +0200
+Subject: Bluetooth: fix UAF in bt_accept_dequeue()
+
+From: Yousef Alhouseen <alhouseenyousef@gmail.com>
+
+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 <alhouseenyousef@gmail.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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
+@@ -1464,8 +1464,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-6.18/bluetooth-hci_conn-fix-null-ptr-deref-in-hci_abort_conn.patch b/queue-6.18/bluetooth-hci_conn-fix-null-ptr-deref-in-hci_abort_conn.patch
new file mode 100644 (file)
index 0000000..22dda6e
--- /dev/null
@@ -0,0 +1,279 @@
+From 12917f591cea1af36087dba5b9ec888652f0b42a Mon Sep 17 00:00:00 2001
+From: Siwei Zhang <oss@fourdim.xyz>
+Date: Mon, 15 Jun 2026 11:33:05 -0400
+Subject: Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()
+
+From: Siwei Zhang <oss@fourdim.xyz>
+
+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 <xiaowu.417@qq.com>
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Siwei Zhang <oss@fourdim.xyz>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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
+@@ -984,6 +984,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
+@@ -2979,26 +2979,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
+@@ -6556,6 +6556,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;
+@@ -6568,6 +6573,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;
+               }
+@@ -6613,6 +6619,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);
+@@ -6648,11 +6660,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;
+ }
+@@ -6927,10 +6942,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)
+@@ -6976,22 +7006,97 @@ int hci_connect_le_sync(struct hci_dev *
+                                      create_le_conn_complete);
+ }
+-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-6.18/bluetooth-hci_uart-clear-hci_uart_sending-when-write_work-is-canceled.patch b/queue-6.18/bluetooth-hci_uart-clear-hci_uart_sending-when-write_work-is-canceled.patch
new file mode 100644 (file)
index 0000000..ceba6e7
--- /dev/null
@@ -0,0 +1,71 @@
+From 1b0d946d6f08bd39211385bc703a440911b41e46 Mon Sep 17 00:00:00 2001
+From: Pauli Virtanen <pav@iki.fi>
+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 <pav@iki.fi>
+
+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 <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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-6.18/bluetooth-iso-avoid-null-deref-of-conn-in-iso_conn_big_sync.patch b/queue-6.18/bluetooth-iso-avoid-null-deref-of-conn-in-iso_conn_big_sync.patch
new file mode 100644 (file)
index 0000000..2e7e098
--- /dev/null
@@ -0,0 +1,74 @@
+From d5541eb148da72d5e0a1bca8ecd171f9fc8b366f Mon Sep 17 00:00:00 2001
+From: Muhammad Bilal <meatuni001@gmail.com>
+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 <meatuni001@gmail.com>
+
+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 <meatuni001@gmail.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/bluetooth/iso.c |   13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+--- a/net/bluetooth/iso.c
++++ b/net/bluetooth/iso.c
+@@ -1515,6 +1515,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;
+@@ -1537,8 +1538,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,
+@@ -1547,6 +1557,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-6.18/bluetooth-l2cap-cancel-pending_rx_work-before-taking-conn-lock.patch b/queue-6.18/bluetooth-l2cap-cancel-pending_rx_work-before-taking-conn-lock.patch
new file mode 100644 (file)
index 0000000..260bfa2
--- /dev/null
@@ -0,0 +1,65 @@
+From 2641a9e0a1dd4af2e21995470a21d55dd35e5203 Mon Sep 17 00:00:00 2001
+From: Runyu Xiao <runyu.xiao@seu.edu.cn>
+Date: Wed, 17 Jun 2026 23:36:13 +0800
+Subject: Bluetooth: L2CAP: cancel pending_rx_work before taking conn->lock
+
+From: Runyu Xiao <runyu.xiao@seu.edu.cn>
+
+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 <runyu.xiao@seu.edu.cn>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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-6.18/bluetooth-l2cap-validate-option-length-before-reading-conf-opt-value.patch b/queue-6.18/bluetooth-l2cap-validate-option-length-before-reading-conf-opt-value.patch
new file mode 100644 (file)
index 0000000..7faf0d5
--- /dev/null
@@ -0,0 +1,135 @@
+From 687617555cedfb74c9e3cb85d759b908dcb17856 Mon Sep 17 00:00:00 2001
+From: Muhammad Bilal <meatuni001@gmail.com>
+Date: Sun, 21 Jun 2026 00:56:35 +0500
+Subject: Bluetooth: L2CAP: validate option length before reading conf opt value
+
+From: Muhammad Bilal <meatuni001@gmail.com>
+
+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 <meatuni001@gmail.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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:
index 224a02fe7f4f6416f2b42100bfdc7e65bc449faa..2482fc092047bdf2da8ffbccb635b845af63eeb5 100644 (file)
@@ -197,3 +197,12 @@ netfilter-ebtables-zero-chainstack-array.patch
 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
+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