hid-logitech-dj-fix-wrong-detection-of-bad-dj_short-.patch
net-qrtr-ns-limit-the-maximum-server-registration-pe.patch
net-qrtr-ns-raise-node-count-limit-to-512.patch
+tls-separate-no-async-decryption-request-handling-fr.patch
--- /dev/null
+From d2493b7bc220532b47092f75e897f66825443465 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 3 Aug 2026 12:51:49 -0700
+Subject: tls: separate no-async decryption request handling from async
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+commit 41532b785e9d79636b3815a64ddf6a096647d011 upstream.
+
+If we're not doing async, the handling is much simpler. There's no
+reference counting, we just need to wait for the completion to wake us
+up and return its result.
+
+We should preferably also use a separate crypto_wait. I'm not seeing a
+UAF as I did in the past, I think aec7961916f3 ("tls: fix race between
+async notify and socket close") took care of it.
+
+This will make the next fix easier.
+
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Link: https://lore.kernel.org/r/47bde5f649707610eaef9f0d679519966fc31061.1709132643.git.sd@queasysnail.net
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Wengang Wang <wen.gang.wang@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/tls/tls_sw.c | 17 ++++++++++-------
+ 1 file changed, 10 insertions(+), 7 deletions(-)
+
+diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
+index 1732e3549a578..29650fa546664 100644
+--- a/net/tls/tls_sw.c
++++ b/net/tls/tls_sw.c
+@@ -257,18 +257,21 @@ static int tls_do_decryption(struct sock *sk,
+ tls_decrypt_done, skb);
+ atomic_inc(&ctx->decrypt_pending);
+ } else {
++ DECLARE_CRYPTO_WAIT(wait);
++
+ aead_request_set_callback(aead_req,
+ CRYPTO_TFM_REQ_MAY_BACKLOG,
+- crypto_req_done, &ctx->async_wait);
++ crypto_req_done, &wait);
++
++ ret = crypto_aead_decrypt(aead_req);
++ if (ret == -EINPROGRESS || ret == -EBUSY)
++ ret = crypto_wait_req(ret, &wait);
++ return ret;
+ }
+
+ ret = crypto_aead_decrypt(aead_req);
+- if (ret == -EINPROGRESS) {
+- if (async)
+- return ret;
+-
+- ret = crypto_wait_req(ret, &ctx->async_wait);
+- }
++ if (ret == -EINPROGRESS)
++ return ret;
+
+ if (async)
+ atomic_dec(&ctx->decrypt_pending);
+--
+2.53.0
+
hid-logitech-dj-fix-wrong-detection-of-bad-dj_short-.patch
net-qrtr-ns-limit-the-maximum-server-registration-pe.patch
net-qrtr-ns-raise-node-count-limit-to-512.patch
+tls-separate-no-async-decryption-request-handling-fr.patch
--- /dev/null
+From 4899deec200708913c7ed2b8b658b54f49a68559 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 3 Aug 2026 12:32:16 -0700
+Subject: tls: separate no-async decryption request handling from async
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+commit 41532b785e9d79636b3815a64ddf6a096647d011 upstream.
+
+If we're not doing async, the handling is much simpler. There's no
+reference counting, we just need to wait for the completion to wake us
+up and return its result.
+
+We should preferably also use a separate crypto_wait. I'm not seeing a
+UAF as I did in the past, I think aec7961916f3 ("tls: fix race between
+async notify and socket close") took care of it.
+
+This will make the next fix easier.
+
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Link: https://lore.kernel.org/r/47bde5f649707610eaef9f0d679519966fc31061.1709132643.git.sd@queasysnail.net
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Wengang Wang <wen.gang.wang@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/tls/tls_sw.c | 13 ++++++++-----
+ 1 file changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
+index 389597bd7ae52..0cdc9131aff22 100644
+--- a/net/tls/tls_sw.c
++++ b/net/tls/tls_sw.c
+@@ -278,9 +278,15 @@ static int tls_do_decryption(struct sock *sk,
+ BUILD_BUG_ON_INVALID(atomic_read(&ctx->decrypt_pending) < 1);
+ atomic_inc(&ctx->decrypt_pending);
+ } else {
++ DECLARE_CRYPTO_WAIT(wait);
++
+ aead_request_set_callback(aead_req,
+ CRYPTO_TFM_REQ_MAY_BACKLOG,
+- crypto_req_done, &ctx->async_wait);
++ crypto_req_done, &wait);
++ ret = crypto_aead_decrypt(aead_req);
++ if (ret == -EINPROGRESS || ret == -EBUSY)
++ ret = crypto_wait_req(ret, &wait);
++ return ret;
+ }
+
+ ret = crypto_aead_decrypt(aead_req);
+@@ -289,10 +295,7 @@ static int tls_do_decryption(struct sock *sk,
+ ret = ret ?: -EINPROGRESS;
+ }
+ if (ret == -EINPROGRESS) {
+- if (darg->async)
+- return 0;
+-
+- ret = crypto_wait_req(ret, &ctx->async_wait);
++ return 0;
+ } else if (darg->async) {
+ atomic_dec(&ctx->decrypt_pending);
+ }
+--
+2.53.0
+
--- /dev/null
+From e4f30b1090d69bbe3ec6c12479f5277fafa093bd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 3 Aug 2026 20:41:29 +0200
+Subject: netconsole: avoid OOB reads, msg is not nul-terminated
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+[ Upstream commit 82aec772fca2223bc5774bd9af486fd95766e578 ]
+
+msg passed to netconsole from the console subsystem is not guaranteed
+to be nul-terminated. Before recent
+commit 7eab73b18630 ("netconsole: convert to NBCON console infrastructure")
+the message would be placed in printk_shared_pbufs, a static global
+buffer, so KASAN had harder time catching OOB accesses. Now we see:
+
+ printk: console [netcon_ext0] enabled
+ BUG: KASAN: slab-out-of-bounds in string+0x1f7/0x240
+ Read of size 1 at addr ffff88813b6d4c00 by task pr/netcon_ext0/594
+
+ CPU: 65 UID: 0 PID: 594 Comm: pr/netcon_ext0 Not tainted 6.19.0-11754-g4246fd6547c9
+ Call Trace:
+ kasan_report+0xe4/0x120
+ string+0x1f7/0x240
+ vsnprintf+0x655/0xba0
+ scnprintf+0xba/0x120
+ netconsole_write+0x3fe/0xa10
+ nbcon_emit_next_record+0x46e/0x860
+ nbcon_kthread_func+0x623/0x750
+
+ Allocated by task 1:
+ nbcon_alloc+0x1ea/0x450
+ register_console+0x26b/0xe10
+ init_netconsole+0xbb0/0xda0
+
+ The buggy address belongs to the object at ffff88813b6d4000
+ which belongs to the cache kmalloc-4k of size 4096
+ The buggy address is located 0 bytes to the right of
+ allocated 3072-byte region [ffff88813b6d4000, ffff88813b6d4c00)
+
+Fixes: c62c0a17f9b7 ("netconsole: Append kernel version to message")
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://patch.msgid.link/20260219195021.2099699-1-kuba@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+[ mb: adjusted context; in 6.12 the affected code lives in
+ send_ext_msg_udp() and writes to a static buffer instead of nt->buf ]
+Signed-off-by: Markus Boehme <markus.boehme@mailbox.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/netconsole.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
+index 60375bb814a1e..3b76ec3cd49b8 100644
+--- a/drivers/net/netconsole.c
++++ b/drivers/net/netconsole.c
+@@ -1103,7 +1103,8 @@ static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg,
+ if (msg_len + release_len + userdata_len <= MAX_PRINT_CHUNK) {
+ /* No fragmentation needed */
+ if (nt->release) {
+- scnprintf(buf, MAX_PRINT_CHUNK, "%s,%s", release, msg);
++ scnprintf(buf, MAX_PRINT_CHUNK, "%s,%.*s", release,
++ msg_len, msg);
+ msg_len += release_len;
+ } else {
+ memcpy(buf, msg, msg_len);
+--
+2.53.0
+
hid-logitech-dj-prevent-report_id_dj_short-related-u.patch
hid-logitech-dj-fix-wrong-detection-of-bad-dj_short-.patch
bpf-reset-register-bounds-before-narrowing-retval-ra.patch
+netconsole-avoid-oob-reads-msg-is-not-nul-terminated.patch
+thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch
--- /dev/null
+From f9c73585c00541f23f471d5f77c63da722bd1f29 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 27 May 2026 07:46:04 -0400
+Subject: thunderbolt: Prevent XDomain delayed work use-after-free on
+ disconnect
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 2c5d2d3c3f70cde2565d7b279b544893a2035842 ]
+
+tb_xdp_handle_request() runs on system_wq and queues
+xd->state_work via queue_delayed_work() in three request handlers:
+PROPERTIES_CHANGED_REQUEST, UUID_REQUEST (via start_handshake),
+and LINK_STATE_CHANGE_REQUEST. Similarly, update_xdomain() queues
+xd->properties_changed_work when local properties change.
+
+Concurrently, tb_xdomain_remove() calls stop_handshake() which does
+cancel_delayed_work_sync() on both delayed works. Later,
+tb_xdomain_unregister() calls device_unregister() which eventually
+frees the xdomain. Since commit 559c1e1e0134 ("thunderbolt: Run
+tb_xdp_handle_request() in system workqueue") moved the request
+handler off tb->wq, the handler and the remove path are no longer
+serialized. If queue_delayed_work() executes after
+cancel_delayed_work_sync() but before the xdomain is freed, the
+delayed work fires on a freed object.
+
+Add xd->removing that tb_xdomain_remove() sets under xd->lock
+before calling stop_handshake(). Each external queue site holds
+the same lock and checks removing before calling
+queue_delayed_work(). This provides the mutual exclusion needed:
+either the queue site acquires the lock first and queues work that
+the subsequent cancel will see, or the remove path acquires the
+lock first and the queue site observes removing == true and skips
+the queue.
+
+Fixes: 559c1e1e0134 ("thunderbolt: Run tb_xdp_handle_request() in system workqueue")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/thunderbolt/xdomain.c | 40 ++++++++++++++++++++++++++---------
+ include/linux/thunderbolt.h | 3 +++
+ 2 files changed, 33 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c
+index 06357073f4ab8..ffe7ce0737491 100644
+--- a/drivers/thunderbolt/xdomain.c
++++ b/drivers/thunderbolt/xdomain.c
+@@ -783,9 +783,13 @@ static void tb_xdp_handle_request(struct work_struct *work)
+ * the xdomain related to this connection as well in
+ * case there is a change in services it offers.
+ */
+- if (xd && device_is_registered(&xd->dev))
+- queue_delayed_work(tb->wq, &xd->state_work,
+- msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
++ if (xd) {
++ mutex_lock(&xd->lock);
++ if (!xd->removing && device_is_registered(&xd->dev))
++ queue_delayed_work(tb->wq, &xd->state_work,
++ msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
++ mutex_unlock(&xd->lock);
++ }
+ break;
+
+ case UUID_REQUEST_OLD:
+@@ -798,8 +802,12 @@ static void tb_xdp_handle_request(struct work_struct *work)
+ * received UUID request from the remote host.
+ */
+ if (!ret && xd && xd->state == XDOMAIN_STATE_ERROR) {
+- dev_dbg(&xd->dev, "restarting handshake\n");
+- start_handshake(xd);
++ mutex_lock(&xd->lock);
++ if (!xd->removing) {
++ dev_dbg(&xd->dev, "restarting handshake\n");
++ start_handshake(xd);
++ }
++ mutex_unlock(&xd->lock);
+ }
+ break;
+
+@@ -827,9 +835,13 @@ static void tb_xdp_handle_request(struct work_struct *work)
+
+ ret = tb_xdp_link_state_change_response(ctl, route,
+ sequence, 0);
+- xd->target_link_width = lsc->tlw;
+- queue_delayed_work(tb->wq, &xd->state_work,
+- msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
++ mutex_lock(&xd->lock);
++ if (!xd->removing) {
++ xd->target_link_width = lsc->tlw;
++ queue_delayed_work(tb->wq, &xd->state_work,
++ msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
++ }
++ mutex_unlock(&xd->lock);
+ } else {
+ tb_xdp_error_response(ctl, route, sequence,
+ ERROR_NOT_READY);
+@@ -2068,6 +2080,10 @@ void tb_xdomain_remove(struct tb_xdomain *xd)
+ {
+ tb_xdomain_debugfs_remove(xd);
+
++ mutex_lock(&xd->lock);
++ xd->removing = true;
++ mutex_unlock(&xd->lock);
++
+ stop_handshake(xd);
+
+ device_for_each_child_reverse(&xd->dev, xd, unregister_service);
+@@ -2464,8 +2480,12 @@ static int update_xdomain(struct device *dev, void *data)
+
+ xd = tb_to_xdomain(dev);
+ if (xd) {
+- queue_delayed_work(xd->tb->wq, &xd->properties_changed_work,
+- msecs_to_jiffies(50));
++ mutex_lock(&xd->lock);
++ if (!xd->removing)
++ queue_delayed_work(xd->tb->wq,
++ &xd->properties_changed_work,
++ msecs_to_jiffies(50));
++ mutex_unlock(&xd->lock);
+ }
+
+ return 0;
+diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h
+index 7d902d8c054b2..4ef9c43026569 100644
+--- a/include/linux/thunderbolt.h
++++ b/include/linux/thunderbolt.h
+@@ -202,6 +202,8 @@ enum tb_link_width {
+ * @link_width: Width of the downstream facing link
+ * @link_usb4: Downstream link is USB4
+ * @is_unplugged: The XDomain is unplugged
++ * @removing: Set by tb_xdomain_remove() under @lock to prevent
++ * concurrent delayed work queueing
+ * @needs_uuid: If the XDomain does not have @remote_uuid it will be
+ * queried first
+ * @service_ids: Used to generate IDs for the services
+@@ -250,6 +252,7 @@ struct tb_xdomain {
+ enum tb_link_width link_width;
+ bool link_usb4;
+ bool is_unplugged;
++ bool removing;
+ bool needs_uuid;
+ struct ida service_ids;
+ struct ida in_hopids;
+--
+2.53.0
+
hid-logitech-dj-fix-wrong-detection-of-bad-dj_short-.patch
lib-alloc_tag-introduce-mem_alloc_profiling_permanen.patch
mm-slab-prevent-unbounded-recursion-in-free-path-wit.patch
+thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch
--- /dev/null
+From d30a3d044ad5ab0b81f233be983ae3e300ec9e8c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 27 May 2026 07:46:04 -0400
+Subject: thunderbolt: Prevent XDomain delayed work use-after-free on
+ disconnect
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 2c5d2d3c3f70cde2565d7b279b544893a2035842 ]
+
+tb_xdp_handle_request() runs on system_wq and queues
+xd->state_work via queue_delayed_work() in three request handlers:
+PROPERTIES_CHANGED_REQUEST, UUID_REQUEST (via start_handshake),
+and LINK_STATE_CHANGE_REQUEST. Similarly, update_xdomain() queues
+xd->properties_changed_work when local properties change.
+
+Concurrently, tb_xdomain_remove() calls stop_handshake() which does
+cancel_delayed_work_sync() on both delayed works. Later,
+tb_xdomain_unregister() calls device_unregister() which eventually
+frees the xdomain. Since commit 559c1e1e0134 ("thunderbolt: Run
+tb_xdp_handle_request() in system workqueue") moved the request
+handler off tb->wq, the handler and the remove path are no longer
+serialized. If queue_delayed_work() executes after
+cancel_delayed_work_sync() but before the xdomain is freed, the
+delayed work fires on a freed object.
+
+Add xd->removing that tb_xdomain_remove() sets under xd->lock
+before calling stop_handshake(). Each external queue site holds
+the same lock and checks removing before calling
+queue_delayed_work(). This provides the mutual exclusion needed:
+either the queue site acquires the lock first and queues work that
+the subsequent cancel will see, or the remove path acquires the
+lock first and the queue site observes removing == true and skips
+the queue.
+
+Fixes: 559c1e1e0134 ("thunderbolt: Run tb_xdp_handle_request() in system workqueue")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/thunderbolt/xdomain.c | 40 ++++++++++++++++++++++++++---------
+ include/linux/thunderbolt.h | 3 +++
+ 2 files changed, 33 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c
+index 458476907eab0..6b16fc7254b8e 100644
+--- a/drivers/thunderbolt/xdomain.c
++++ b/drivers/thunderbolt/xdomain.c
+@@ -785,9 +785,13 @@ static void tb_xdp_handle_request(struct work_struct *work)
+ * the xdomain related to this connection as well in
+ * case there is a change in services it offers.
+ */
+- if (xd && device_is_registered(&xd->dev))
+- queue_delayed_work(tb->wq, &xd->state_work,
+- msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
++ if (xd) {
++ mutex_lock(&xd->lock);
++ if (!xd->removing && device_is_registered(&xd->dev))
++ queue_delayed_work(tb->wq, &xd->state_work,
++ msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
++ mutex_unlock(&xd->lock);
++ }
+ break;
+
+ case UUID_REQUEST_OLD:
+@@ -800,8 +804,12 @@ static void tb_xdp_handle_request(struct work_struct *work)
+ * received UUID request from the remote host.
+ */
+ if (!ret && xd && xd->state == XDOMAIN_STATE_ERROR) {
+- dev_dbg(&xd->dev, "restarting handshake\n");
+- start_handshake(xd);
++ mutex_lock(&xd->lock);
++ if (!xd->removing) {
++ dev_dbg(&xd->dev, "restarting handshake\n");
++ start_handshake(xd);
++ }
++ mutex_unlock(&xd->lock);
+ }
+ break;
+
+@@ -829,9 +837,13 @@ static void tb_xdp_handle_request(struct work_struct *work)
+
+ ret = tb_xdp_link_state_change_response(ctl, route,
+ sequence, 0);
+- xd->target_link_width = lsc->tlw;
+- queue_delayed_work(tb->wq, &xd->state_work,
+- msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
++ mutex_lock(&xd->lock);
++ if (!xd->removing) {
++ xd->target_link_width = lsc->tlw;
++ queue_delayed_work(tb->wq, &xd->state_work,
++ msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
++ }
++ mutex_unlock(&xd->lock);
+ } else {
+ tb_xdp_error_response(ctl, route, sequence,
+ ERROR_NOT_READY);
+@@ -2074,6 +2086,10 @@ void tb_xdomain_remove(struct tb_xdomain *xd)
+ {
+ tb_xdomain_debugfs_remove(xd);
+
++ mutex_lock(&xd->lock);
++ xd->removing = true;
++ mutex_unlock(&xd->lock);
++
+ stop_handshake(xd);
+
+ device_for_each_child_reverse(&xd->dev, xd, unregister_service);
+@@ -2484,8 +2500,12 @@ static int update_xdomain(struct device *dev, void *data)
+
+ xd = tb_to_xdomain(dev);
+ if (xd) {
+- queue_delayed_work(xd->tb->wq, &xd->properties_changed_work,
+- msecs_to_jiffies(50));
++ mutex_lock(&xd->lock);
++ if (!xd->removing)
++ queue_delayed_work(xd->tb->wq,
++ &xd->properties_changed_work,
++ msecs_to_jiffies(50));
++ mutex_unlock(&xd->lock);
+ }
+
+ return 0;
+diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h
+index 0ba112175bb39..7204586c10c3e 100644
+--- a/include/linux/thunderbolt.h
++++ b/include/linux/thunderbolt.h
+@@ -209,6 +209,8 @@ enum tb_link_width {
+ * @link_width: Width of the downstream facing link
+ * @link_usb4: Downstream link is USB4
+ * @is_unplugged: The XDomain is unplugged
++ * @removing: Set by tb_xdomain_remove() under @lock to prevent
++ * concurrent delayed work queueing
+ * @needs_uuid: If the XDomain does not have @remote_uuid it will be
+ * queried first
+ * @service_ids: Used to generate IDs for the services
+@@ -257,6 +259,7 @@ struct tb_xdomain {
+ enum tb_link_width link_width;
+ bool link_usb4;
+ bool is_unplugged;
++ bool removing;
+ bool needs_uuid;
+ struct ida service_ids;
+ struct ida in_hopids;
+--
+2.53.0
+
hid-logitech-dj-prevent-report_id_dj_short-related-u.patch
hid-logitech-dj-fix-wrong-detection-of-bad-dj_short-.patch
soc-qcom-ice-allow-explicit-votes-on-iface-clock-for.patch
+thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch
--- /dev/null
+From 1549dc04bc61ac157e5a782b18d6fb0db62e471f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 27 May 2026 07:46:04 -0400
+Subject: thunderbolt: Prevent XDomain delayed work use-after-free on
+ disconnect
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 2c5d2d3c3f70cde2565d7b279b544893a2035842 ]
+
+tb_xdp_handle_request() runs on system_wq and queues
+xd->state_work via queue_delayed_work() in three request handlers:
+PROPERTIES_CHANGED_REQUEST, UUID_REQUEST (via start_handshake),
+and LINK_STATE_CHANGE_REQUEST. Similarly, update_xdomain() queues
+xd->properties_changed_work when local properties change.
+
+Concurrently, tb_xdomain_remove() calls stop_handshake() which does
+cancel_delayed_work_sync() on both delayed works. Later,
+tb_xdomain_unregister() calls device_unregister() which eventually
+frees the xdomain. Since commit 559c1e1e0134 ("thunderbolt: Run
+tb_xdp_handle_request() in system workqueue") moved the request
+handler off tb->wq, the handler and the remove path are no longer
+serialized. If queue_delayed_work() executes after
+cancel_delayed_work_sync() but before the xdomain is freed, the
+delayed work fires on a freed object.
+
+Add xd->removing that tb_xdomain_remove() sets under xd->lock
+before calling stop_handshake(). Each external queue site holds
+the same lock and checks removing before calling
+queue_delayed_work(). This provides the mutual exclusion needed:
+either the queue site acquires the lock first and queues work that
+the subsequent cancel will see, or the remove path acquires the
+lock first and the queue site observes removing == true and skips
+the queue.
+
+Fixes: 559c1e1e0134 ("thunderbolt: Run tb_xdp_handle_request() in system workqueue")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/thunderbolt/xdomain.c | 40 ++++++++++++++++++++++++++---------
+ include/linux/thunderbolt.h | 3 +++
+ 2 files changed, 33 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c
+index a5ec70327d81a..87fbced665b91 100644
+--- a/drivers/thunderbolt/xdomain.c
++++ b/drivers/thunderbolt/xdomain.c
+@@ -783,9 +783,13 @@ static void tb_xdp_handle_request(struct work_struct *work)
+ * the xdomain related to this connection as well in
+ * case there is a change in services it offers.
+ */
+- if (xd && device_is_registered(&xd->dev))
+- queue_delayed_work(tb->wq, &xd->state_work,
+- msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
++ if (xd) {
++ mutex_lock(&xd->lock);
++ if (!xd->removing && device_is_registered(&xd->dev))
++ queue_delayed_work(tb->wq, &xd->state_work,
++ msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
++ mutex_unlock(&xd->lock);
++ }
+ break;
+
+ case UUID_REQUEST_OLD:
+@@ -798,8 +802,12 @@ static void tb_xdp_handle_request(struct work_struct *work)
+ * received UUID request from the remote host.
+ */
+ if (!ret && xd && xd->state == XDOMAIN_STATE_ERROR) {
+- dev_dbg(&xd->dev, "restarting handshake\n");
+- start_handshake(xd);
++ mutex_lock(&xd->lock);
++ if (!xd->removing) {
++ dev_dbg(&xd->dev, "restarting handshake\n");
++ start_handshake(xd);
++ }
++ mutex_unlock(&xd->lock);
+ }
+ break;
+
+@@ -827,9 +835,13 @@ static void tb_xdp_handle_request(struct work_struct *work)
+
+ ret = tb_xdp_link_state_change_response(ctl, route,
+ sequence, 0);
+- xd->target_link_width = lsc->tlw;
+- queue_delayed_work(tb->wq, &xd->state_work,
+- msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
++ mutex_lock(&xd->lock);
++ if (!xd->removing) {
++ xd->target_link_width = lsc->tlw;
++ queue_delayed_work(tb->wq, &xd->state_work,
++ msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
++ }
++ mutex_unlock(&xd->lock);
+ } else {
+ tb_xdp_error_response(ctl, route, sequence,
+ ERROR_NOT_READY);
+@@ -2018,6 +2030,10 @@ void tb_xdomain_remove(struct tb_xdomain *xd)
+ {
+ tb_xdomain_debugfs_remove(xd);
+
++ mutex_lock(&xd->lock);
++ xd->removing = true;
++ mutex_unlock(&xd->lock);
++
+ stop_handshake(xd);
+
+ device_for_each_child_reverse(&xd->dev, xd, unregister_service);
+@@ -2412,8 +2428,12 @@ static int update_xdomain(struct device *dev, void *data)
+
+ xd = tb_to_xdomain(dev);
+ if (xd) {
+- queue_delayed_work(xd->tb->wq, &xd->properties_changed_work,
+- msecs_to_jiffies(50));
++ mutex_lock(&xd->lock);
++ if (!xd->removing)
++ queue_delayed_work(xd->tb->wq,
++ &xd->properties_changed_work,
++ msecs_to_jiffies(50));
++ mutex_unlock(&xd->lock);
+ }
+
+ return 0;
+diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h
+index 02333f47c9941..d95e69c1ac63b 100644
+--- a/include/linux/thunderbolt.h
++++ b/include/linux/thunderbolt.h
+@@ -203,6 +203,8 @@ enum tb_link_width {
+ * @link_width: Width of the downstream facing link
+ * @link_usb4: Downstream link is USB4
+ * @is_unplugged: The XDomain is unplugged
++ * @removing: Set by tb_xdomain_remove() under @lock to prevent
++ * concurrent delayed work queueing
+ * @needs_uuid: If the XDomain does not have @remote_uuid it will be
+ * queried first
+ * @service_ids: Used to generate IDs for the services
+@@ -251,6 +253,7 @@ struct tb_xdomain {
+ enum tb_link_width link_width;
+ bool link_usb4;
+ bool is_unplugged;
++ bool removing;
+ bool needs_uuid;
+ struct ida service_ids;
+ struct ida in_hopids;
+--
+2.53.0
+
lib-alloc_tag-introduce-mem_alloc_profiling_permanen.patch
mm-slab-prevent-unbounded-recursion-in-free-path-wit.patch
alsa-hda-realtek-add-quirk-for-hp-dragonfly-folio-g3.patch
+thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch
--- /dev/null
+From 061e86c29d697bc254a2975f52ad41d955044b4f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 27 May 2026 07:46:04 -0400
+Subject: thunderbolt: Prevent XDomain delayed work use-after-free on
+ disconnect
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 2c5d2d3c3f70cde2565d7b279b544893a2035842 ]
+
+tb_xdp_handle_request() runs on system_wq and queues
+xd->state_work via queue_delayed_work() in three request handlers:
+PROPERTIES_CHANGED_REQUEST, UUID_REQUEST (via start_handshake),
+and LINK_STATE_CHANGE_REQUEST. Similarly, update_xdomain() queues
+xd->properties_changed_work when local properties change.
+
+Concurrently, tb_xdomain_remove() calls stop_handshake() which does
+cancel_delayed_work_sync() on both delayed works. Later,
+tb_xdomain_unregister() calls device_unregister() which eventually
+frees the xdomain. Since commit 559c1e1e0134 ("thunderbolt: Run
+tb_xdp_handle_request() in system workqueue") moved the request
+handler off tb->wq, the handler and the remove path are no longer
+serialized. If queue_delayed_work() executes after
+cancel_delayed_work_sync() but before the xdomain is freed, the
+delayed work fires on a freed object.
+
+Add xd->removing that tb_xdomain_remove() sets under xd->lock
+before calling stop_handshake(). Each external queue site holds
+the same lock and checks removing before calling
+queue_delayed_work(). This provides the mutual exclusion needed:
+either the queue site acquires the lock first and queues work that
+the subsequent cancel will see, or the remove path acquires the
+lock first and the queue site observes removing == true and skips
+the queue.
+
+Fixes: 559c1e1e0134 ("thunderbolt: Run tb_xdp_handle_request() in system workqueue")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/thunderbolt/xdomain.c | 40 ++++++++++++++++++++++++++---------
+ include/linux/thunderbolt.h | 3 +++
+ 2 files changed, 33 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c
+index 1fd1cf4295a2a..55f91ead8f13b 100644
+--- a/drivers/thunderbolt/xdomain.c
++++ b/drivers/thunderbolt/xdomain.c
+@@ -785,9 +785,13 @@ static void tb_xdp_handle_request(struct work_struct *work)
+ * the xdomain related to this connection as well in
+ * case there is a change in services it offers.
+ */
+- if (xd && device_is_registered(&xd->dev))
+- queue_delayed_work(tb->wq, &xd->state_work,
+- msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
++ if (xd) {
++ mutex_lock(&xd->lock);
++ if (!xd->removing && device_is_registered(&xd->dev))
++ queue_delayed_work(tb->wq, &xd->state_work,
++ msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
++ mutex_unlock(&xd->lock);
++ }
+ break;
+
+ case UUID_REQUEST_OLD:
+@@ -800,8 +804,12 @@ static void tb_xdp_handle_request(struct work_struct *work)
+ * received UUID request from the remote host.
+ */
+ if (!ret && xd && xd->state == XDOMAIN_STATE_ERROR) {
+- dev_dbg(&xd->dev, "restarting handshake\n");
+- start_handshake(xd);
++ mutex_lock(&xd->lock);
++ if (!xd->removing) {
++ dev_dbg(&xd->dev, "restarting handshake\n");
++ start_handshake(xd);
++ }
++ mutex_unlock(&xd->lock);
+ }
+ break;
+
+@@ -829,9 +837,13 @@ static void tb_xdp_handle_request(struct work_struct *work)
+
+ ret = tb_xdp_link_state_change_response(ctl, route,
+ sequence, 0);
+- xd->target_link_width = lsc->tlw;
+- queue_delayed_work(tb->wq, &xd->state_work,
+- msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
++ mutex_lock(&xd->lock);
++ if (!xd->removing) {
++ xd->target_link_width = lsc->tlw;
++ queue_delayed_work(tb->wq, &xd->state_work,
++ msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
++ }
++ mutex_unlock(&xd->lock);
+ } else {
+ tb_xdp_error_response(ctl, route, sequence,
+ ERROR_NOT_READY);
+@@ -2074,6 +2086,10 @@ void tb_xdomain_remove(struct tb_xdomain *xd)
+ {
+ tb_xdomain_debugfs_remove(xd);
+
++ mutex_lock(&xd->lock);
++ xd->removing = true;
++ mutex_unlock(&xd->lock);
++
+ stop_handshake(xd);
+
+ device_for_each_child_reverse(&xd->dev, xd, unregister_service);
+@@ -2484,8 +2500,12 @@ static int update_xdomain(struct device *dev, void *data)
+
+ xd = tb_to_xdomain(dev);
+ if (xd) {
+- queue_delayed_work(xd->tb->wq, &xd->properties_changed_work,
+- msecs_to_jiffies(50));
++ mutex_lock(&xd->lock);
++ if (!xd->removing)
++ queue_delayed_work(xd->tb->wq,
++ &xd->properties_changed_work,
++ msecs_to_jiffies(50));
++ mutex_unlock(&xd->lock);
+ }
+
+ return 0;
+diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h
+index 0ba112175bb39..7204586c10c3e 100644
+--- a/include/linux/thunderbolt.h
++++ b/include/linux/thunderbolt.h
+@@ -209,6 +209,8 @@ enum tb_link_width {
+ * @link_width: Width of the downstream facing link
+ * @link_usb4: Downstream link is USB4
+ * @is_unplugged: The XDomain is unplugged
++ * @removing: Set by tb_xdomain_remove() under @lock to prevent
++ * concurrent delayed work queueing
+ * @needs_uuid: If the XDomain does not have @remote_uuid it will be
+ * queried first
+ * @service_ids: Used to generate IDs for the services
+@@ -257,6 +259,7 @@ struct tb_xdomain {
+ enum tb_link_width link_width;
+ bool link_usb4;
+ bool is_unplugged;
++ bool removing;
+ bool needs_uuid;
+ struct ida service_ids;
+ struct ida in_hopids;
+--
+2.53.0
+