]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for all trees master
authorSasha Levin <sashal@kernel.org>
Tue, 4 Aug 2026 01:05:42 +0000 (21:05 -0400)
committerSasha Levin <sashal@kernel.org>
Tue, 4 Aug 2026 01:05:42 +0000 (21:05 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
13 files changed:
queue-5.10/series
queue-5.10/tls-separate-no-async-decryption-request-handling-fr.patch [new file with mode: 0644]
queue-5.15/series
queue-5.15/tls-separate-no-async-decryption-request-handling-fr.patch [new file with mode: 0644]
queue-6.12/netconsole-avoid-oob-reads-msg-is-not-nul-terminated.patch [new file with mode: 0644]
queue-6.12/series
queue-6.12/thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch [new file with mode: 0644]
queue-6.18/series
queue-6.18/thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch [new file with mode: 0644]
queue-6.6/series
queue-6.6/thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch [new file with mode: 0644]
queue-7.1/series
queue-7.1/thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch [new file with mode: 0644]

index f7bfe5c1ca5348c16c109cf45988aa3a8299e36b..2e649fc0592394cc8f15d224701ac41c7a05f828 100644 (file)
@@ -199,3 +199,4 @@ hid-logitech-dj-prevent-report_id_dj_short-related-u.patch
 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
diff --git a/queue-5.10/tls-separate-no-async-decryption-request-handling-fr.patch b/queue-5.10/tls-separate-no-async-decryption-request-handling-fr.patch
new file mode 100644 (file)
index 0000000..8444a3d
--- /dev/null
@@ -0,0 +1,64 @@
+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
+
index 825d3fff9ed03535484652bbba22478018dcaf2f..f2c8434b1cb0d75de5db391ad8bf8d4396bb4c73 100644 (file)
@@ -242,3 +242,4 @@ hid-logitech-dj-prevent-report_id_dj_short-related-u.patch
 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
diff --git a/queue-5.15/tls-separate-no-async-decryption-request-handling-fr.patch b/queue-5.15/tls-separate-no-async-decryption-request-handling-fr.patch
new file mode 100644 (file)
index 0000000..7c81904
--- /dev/null
@@ -0,0 +1,64 @@
+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
+
diff --git a/queue-6.12/netconsole-avoid-oob-reads-msg-is-not-nul-terminated.patch b/queue-6.12/netconsole-avoid-oob-reads-msg-is-not-nul-terminated.patch
new file mode 100644 (file)
index 0000000..9ba1fb1
--- /dev/null
@@ -0,0 +1,69 @@
+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
+
index 4065f68f20c326c010cc8614c84a9d6a6009f30b..85365a67ae27e6affffa946cc947755646ab1cf0 100644 (file)
@@ -15,3 +15,5 @@ hid-logitech-dj-standardise-hid_report_enum-variable.patch
 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
diff --git a/queue-6.12/thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch b/queue-6.12/thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch
new file mode 100644 (file)
index 0000000..b94a098
--- /dev/null
@@ -0,0 +1,149 @@
+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
+
index f19915941179f160847b09473517a0b76b755fb4..aca90a1feec54196dca4ea8af1657e252d904794 100644 (file)
@@ -10,3 +10,4 @@ hid-logitech-dj-prevent-report_id_dj_short-related-u.patch
 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
diff --git a/queue-6.18/thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch b/queue-6.18/thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch
new file mode 100644 (file)
index 0000000..f7bb022
--- /dev/null
@@ -0,0 +1,149 @@
+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
+
index 6542c4811d0a5f974f3843843c51475df24d6555..441fddf862ed4be55e107e92f9b10aaf404f2781 100644 (file)
@@ -6,3 +6,4 @@ hid-logitech-dj-standardise-hid_report_enum-variable.patch
 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
diff --git a/queue-6.6/thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch b/queue-6.6/thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch
new file mode 100644 (file)
index 0000000..5560bd5
--- /dev/null
@@ -0,0 +1,149 @@
+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
+
index 519f7143ad5a6f964615d9681a16f606fb618c05..c1b4db88670e16a7c378255f810094fb0ab3c6e3 100644 (file)
@@ -3,3 +3,4 @@ mm-slab-decouple-slab_no_sheaves-from-slab_no_obj_ex.patch
 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
diff --git a/queue-7.1/thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch b/queue-7.1/thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch
new file mode 100644 (file)
index 0000000..9021438
--- /dev/null
@@ -0,0 +1,149 @@
+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
+