From: Greg Kroah-Hartman Date: Mon, 13 Jul 2026 17:33:31 +0000 (+0200) Subject: 6.1-stable patches X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=ce7648e785dbc3666a27bfadd2769f3926a4b00e;p=thirdparty%2Fkernel%2Fstable-queue.git 6.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-fix-uaf-in-bt_accept_dequeue.patch bluetooth-l2cap-validate-option-length-before-reading-conf-opt-value.patch --- diff --git a/queue-6.1/bluetooth-bnep-pin-l2cap-connection-during-netdev-registration.patch b/queue-6.1/bluetooth-bnep-pin-l2cap-connection-during-netdev-registration.patch new file mode 100644 index 0000000000..a1e41a4655 --- /dev/null +++ b/queue-6.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 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.1/bluetooth-btmtksdio-fix-infinite-loop-in-btmtksdio_txrx_work.patch b/queue-6.1/bluetooth-btmtksdio-fix-infinite-loop-in-btmtksdio_txrx_work.patch new file mode 100644 index 0000000000..13d98393ce --- /dev/null +++ b/queue-6.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 +@@ -605,7 +605,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 */ + sdio_writel(bdev->func, C_INT_EN_SET, MTK_REG_CHLPCR, 0); diff --git a/queue-6.1/bluetooth-fix-uaf-in-bt_accept_dequeue.patch b/queue-6.1/bluetooth-fix-uaf-in-bt_accept_dequeue.patch new file mode 100644 index 0000000000..0e0449f525 --- /dev/null +++ b/queue-6.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 +@@ -305,7 +305,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 +@@ -321,13 +321,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; + } + +@@ -337,16 +335,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); +@@ -354,6 +342,7 @@ restart: + } + + release_sock(sk); ++ sock_put(sk); + } + + return NULL; +--- a/net/bluetooth/l2cap_sock.c ++++ b/net/bluetooth/l2cap_sock.c +@@ -1465,8 +1465,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.1/bluetooth-l2cap-validate-option-length-before-reading-conf-opt-value.patch b/queue-6.1/bluetooth-l2cap-validate-option-length-before-reading-conf-opt-value.patch new file mode 100644 index 0000000000..333c1873a1 --- /dev/null +++ b/queue-6.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 +@@ -3188,13 +3188,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; +@@ -3600,6 +3611,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; +@@ -3613,9 +3625,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; +@@ -3849,6 +3863,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 }; +@@ -3857,9 +3872,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: +@@ -4112,6 +4129,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. + */ +@@ -4130,9 +4148,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-6.1/series b/queue-6.1/series index f890e0138a..94226eef48 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -128,3 +128,7 @@ mfd-cros_ec-delay-dev_set_drvdata-until-probe-success.patch netfilter-ctnetlink-use-nf_ct_exp_net-in-expectation-dump.patch netfilter-ebtables-module-names-must-be-null-terminated.patch netfilter-ebtables-terminate-table-name-before-find_table_lock.patch +bluetooth-btmtksdio-fix-infinite-loop-in-btmtksdio_txrx_work.patch +bluetooth-bnep-pin-l2cap-connection-during-netdev-registration.patch +bluetooth-fix-uaf-in-bt_accept_dequeue.patch +bluetooth-l2cap-validate-option-length-before-reading-conf-opt-value.patch