]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for all trees
authorSasha Levin <sashal@kernel.org>
Sun, 24 May 2026 11:59:12 +0000 (07:59 -0400)
committerSasha Levin <sashal@kernel.org>
Sun, 24 May 2026 11:59:12 +0000 (07:59 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
33 files changed:
queue-5.10/s390-debug-reject-zero-length-input-before-trimming-.patch [new file with mode: 0644]
queue-5.10/selftests-lib.mk-also-install-config-and-settings.patch [new file with mode: 0644]
queue-5.10/series
queue-5.15/io_uring-prevent-opcode-speculation.patch [new file with mode: 0644]
queue-5.15/s390-debug-reject-zero-length-input-before-trimming-.patch [new file with mode: 0644]
queue-5.15/series
queue-6.1/io_uring-prevent-opcode-speculation.patch [new file with mode: 0644]
queue-6.1/s390-debug-reject-zero-length-input-before-trimming-.patch [new file with mode: 0644]
queue-6.1/series
queue-6.12/drm-xe-hdcp-add-null-check-for-media_gt-in-intel_hdc.patch [new file with mode: 0644]
queue-6.12/ksmbd-validate-owner-of-durable-handle-on-reconnect.patch [new file with mode: 0644]
queue-6.12/mptcp-pm-add_addr-rtx-allow-id-0.patch [new file with mode: 0644]
queue-6.12/mptcp-pm-add_addr-rtx-always-decrease-sk-refcount.patch [new file with mode: 0644]
queue-6.12/mptcp-pm-add_addr-rtx-free-sk-if-last.patch [new file with mode: 0644]
queue-6.12/mptcp-sync-the-msk-sndbuf-at-accept-time.patch [new file with mode: 0644]
queue-6.12/s390-debug-reject-zero-length-input-before-trimming-.patch [new file with mode: 0644]
queue-6.12/series [new file with mode: 0644]
queue-6.18/drm-xe-hdcp-add-null-check-for-media_gt-in-intel_hdc.patch [new file with mode: 0644]
queue-6.18/iommu-amd-fix-illegal-cap-mmio-access-in-iommu-debug.patch [new file with mode: 0644]
queue-6.18/iommu-amd-remove-latent-out-of-bounds-access-in-iomm.patch [new file with mode: 0644]
queue-6.18/series [new file with mode: 0644]
queue-6.6/driver-core-generalize-driver_override-in-struct-dev.patch [new file with mode: 0644]
queue-6.6/driver-core-platform-use-generic-driver_override-inf.patch [new file with mode: 0644]
queue-6.6/mptcp-pm-add_addr-rtx-allow-id-0.patch [new file with mode: 0644]
queue-6.6/mptcp-pm-add_addr-rtx-always-decrease-sk-refcount.patch [new file with mode: 0644]
queue-6.6/mptcp-pm-add_addr-rtx-free-sk-if-last.patch [new file with mode: 0644]
queue-6.6/mptcp-sync-the-msk-sndbuf-at-accept-time.patch [new file with mode: 0644]
queue-6.6/s390-debug-reject-zero-length-input-before-trimming-.patch [new file with mode: 0644]
queue-6.6/series [new file with mode: 0644]
queue-6.6/spi-spidev-fix-lock-inversion-between-spi_lock-and-b.patch [new file with mode: 0644]
queue-7.0/iommu-amd-fix-illegal-cap-mmio-access-in-iommu-debug.patch [new file with mode: 0644]
queue-7.0/iommu-amd-remove-latent-out-of-bounds-access-in-iomm.patch [new file with mode: 0644]
queue-7.0/series [new file with mode: 0644]

diff --git a/queue-5.10/s390-debug-reject-zero-length-input-before-trimming-.patch b/queue-5.10/s390-debug-reject-zero-length-input-before-trimming-.patch
new file mode 100644 (file)
index 0000000..4c64725
--- /dev/null
@@ -0,0 +1,49 @@
+From 09cc7451932b608d2e1ca3da99956af6f9ef6015 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 10:28:49 +0800
+Subject: s390/debug: Reject zero-length input before trimming a newline
+
+From: Pengpeng Hou <pengpeng@iscas.ac.cn>
+
+[ Upstream commit c366a7b5ed7564e41345c380285bd3f6cb98971b ]
+
+debug_get_user_string() copies the userspace buffer into a newly
+allocated NUL-terminated buffer and then unconditionally looks at
+buffer[user_len - 1] to strip a trailing newline.
+
+A zero-length write reaches this helper unchanged, so the newline trim
+reads before the start of the allocated buffer.
+
+Reject empty writes before accessing the last input byte.
+
+Fixes: 66a464dbc8e0 ("[PATCH] s390: debug feature changes")
+Cc: stable@vger.kernel.org
+Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
+Reviewed-by: Benjamin Block <bblock@linux.ibm.com>
+Reviewed-by: Vasily Gorbik <gor@linux.ibm.com>
+Tested-by: Vasily Gorbik <gor@linux.ibm.com>
+Link: https://lore.kernel.org/r/20260417073530.96002-1-pengpeng@iscas.ac.cn
+Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
+Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/s390/kernel/debug.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c
+index ece21ebf6558f..a347f6244654a 100644
+--- a/arch/s390/kernel/debug.c
++++ b/arch/s390/kernel/debug.c
+@@ -1211,6 +1211,9 @@ static inline char *debug_get_user_string(const char __user *user_buf,
+ {
+       char *buffer;
++      if (!user_len)
++              return ERR_PTR(-EINVAL);
++
+       buffer = kmalloc(user_len + 1, GFP_KERNEL);
+       if (!buffer)
+               return ERR_PTR(-ENOMEM);
+-- 
+2.53.0
+
diff --git a/queue-5.10/selftests-lib.mk-also-install-config-and-settings.patch b/queue-5.10/selftests-lib.mk-also-install-config-and-settings.patch
new file mode 100644 (file)
index 0000000..0bf51f3
--- /dev/null
@@ -0,0 +1,36 @@
+From cde6bc671b5ec15da7887b36aa6a15c3d8d98dd9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 26 May 2021 20:17:54 -0700
+Subject: selftests: lib.mk: Also install "config" and "settings"
+
+From: Kees Cook <keescook@chromium.org>
+
+[ Upstream commit de53fa9baa701963722e9fa3d0fe34b897104497 ]
+
+Installed seccomp tests would time out because the "settings" file was
+missing. Install both "settings" (needed for proper test execution) and
+"config" (needed for informational purposes) with the other test
+targets.
+
+Signed-off-by: Kees Cook <keescook@chromium.org>
+Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/testing/selftests/lib.mk | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/tools/testing/selftests/lib.mk b/tools/testing/selftests/lib.mk
+index 56e360e019ecc..a3df78b7702c1 100644
+--- a/tools/testing/selftests/lib.mk
++++ b/tools/testing/selftests/lib.mk
+@@ -101,6 +101,7 @@ define INSTALL_RULE
+       $(eval INSTALL_LIST = $(TEST_CUSTOM_PROGS)) $(INSTALL_SINGLE_RULE)
+       $(eval INSTALL_LIST = $(TEST_GEN_PROGS_EXTENDED)) $(INSTALL_SINGLE_RULE)
+       $(eval INSTALL_LIST = $(TEST_GEN_FILES)) $(INSTALL_SINGLE_RULE)
++      $(eval INSTALL_LIST = $(wildcard config settings)) $(INSTALL_SINGLE_RULE)
+ endef
+ install: all
+-- 
+2.53.0
+
index 0dc6bd55ba6a4daa629eb4c56fc0d71d12afb613..18d017436bae5b2db89f6a5cae8964bb10ff743a 100644 (file)
@@ -525,3 +525,5 @@ drm-panfrost-fix-wait_bo-ioctl-leaking-positive-return-from-dma_resv_wait_timeou
 drm-gma500-oaktrail_hdmi-fix-i2c-adapter-leak-on-setup.patch
 io-wq-check-that-the-predecessor-is-hashed-in-io_wq_remove_pending.patch
 net-rds-reset-op_nents-when-zerocopy-page-pin-fails.patch
+s390-debug-reject-zero-length-input-before-trimming-.patch
+selftests-lib.mk-also-install-config-and-settings.patch
diff --git a/queue-5.15/io_uring-prevent-opcode-speculation.patch b/queue-5.15/io_uring-prevent-opcode-speculation.patch
new file mode 100644 (file)
index 0000000..cc4c02f
--- /dev/null
@@ -0,0 +1,41 @@
+From a0f4558747dd5218b3f0de44d5f76a880bd0a469 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 14:28:33 +0800
+Subject: io_uring: prevent opcode speculation
+
+From: Pavel Begunkov <asml.silence@gmail.com>
+
+[ Upstream commit 1e988c3fe1264708f4f92109203ac5b1d65de50b ]
+
+sqe->opcode is used for different tables, make sure we santitise it
+against speculations.
+
+Cc: stable@vger.kernel.org
+Fixes: d3656344fea03 ("io_uring: add lookup table for various opcode needs")
+Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
+Reviewed-by: Li Zetao <lizetao1@huawei.com>
+Link: https://lore.kernel.org/r/7eddbf31c8ca0a3947f8ed98271acc2b4349c016.1739568408.git.asml.silence@gmail.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+[ Use req->opcode instead of opcode here. ]
+Signed-off-by: Robert Garcia <rob_garcia@163.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ io_uring/io_uring.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
+index cb54ebda0a8a7..8ecf01f1b689e 100644
+--- a/io_uring/io_uring.c
++++ b/io_uring/io_uring.c
+@@ -7366,6 +7366,8 @@ static int io_init_req(struct io_ring_ctx *ctx, struct io_kiocb *req,
+               return -EINVAL;
+       if (unlikely(req->opcode >= IORING_OP_LAST))
+               return -EINVAL;
++      req->opcode = array_index_nospec(req->opcode, IORING_OP_LAST);
++
+       if (!io_check_restriction(ctx, req, sqe_flags))
+               return -EACCES;
+-- 
+2.53.0
+
diff --git a/queue-5.15/s390-debug-reject-zero-length-input-before-trimming-.patch b/queue-5.15/s390-debug-reject-zero-length-input-before-trimming-.patch
new file mode 100644 (file)
index 0000000..e026a7a
--- /dev/null
@@ -0,0 +1,49 @@
+From 67bbc6674d5ddb9e7f2fc066c4d9fadf547064e6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 10:28:44 +0800
+Subject: s390/debug: Reject zero-length input before trimming a newline
+
+From: Pengpeng Hou <pengpeng@iscas.ac.cn>
+
+[ Upstream commit c366a7b5ed7564e41345c380285bd3f6cb98971b ]
+
+debug_get_user_string() copies the userspace buffer into a newly
+allocated NUL-terminated buffer and then unconditionally looks at
+buffer[user_len - 1] to strip a trailing newline.
+
+A zero-length write reaches this helper unchanged, so the newline trim
+reads before the start of the allocated buffer.
+
+Reject empty writes before accessing the last input byte.
+
+Fixes: 66a464dbc8e0 ("[PATCH] s390: debug feature changes")
+Cc: stable@vger.kernel.org
+Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
+Reviewed-by: Benjamin Block <bblock@linux.ibm.com>
+Reviewed-by: Vasily Gorbik <gor@linux.ibm.com>
+Tested-by: Vasily Gorbik <gor@linux.ibm.com>
+Link: https://lore.kernel.org/r/20260417073530.96002-1-pengpeng@iscas.ac.cn
+Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
+Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/s390/kernel/debug.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c
+index 089d91a3cf5a1..1d7b619acdf30 100644
+--- a/arch/s390/kernel/debug.c
++++ b/arch/s390/kernel/debug.c
+@@ -1268,6 +1268,9 @@ static inline char *debug_get_user_string(const char __user *user_buf,
+ {
+       char *buffer;
++      if (!user_len)
++              return ERR_PTR(-EINVAL);
++
+       buffer = kmalloc(user_len + 1, GFP_KERNEL);
+       if (!buffer)
+               return ERR_PTR(-ENOMEM);
+-- 
+2.53.0
+
index 91c8dd14930d1923a473266230903c5b005a9010..fe706386e2f0d48fcc147f7cc989ed6f5f44f9a6 100644 (file)
@@ -672,3 +672,5 @@ drm-panfrost-fix-wait_bo-ioctl-leaking-positive-return-from-dma_resv_wait_timeou
 drm-gma500-oaktrail_hdmi-fix-i2c-adapter-leak-on-setup.patch
 io-wq-check-that-the-predecessor-is-hashed-in-io_wq_remove_pending.patch
 net-rds-reset-op_nents-when-zerocopy-page-pin-fails.patch
+io_uring-prevent-opcode-speculation.patch
+s390-debug-reject-zero-length-input-before-trimming-.patch
diff --git a/queue-6.1/io_uring-prevent-opcode-speculation.patch b/queue-6.1/io_uring-prevent-opcode-speculation.patch
new file mode 100644 (file)
index 0000000..bf88da2
--- /dev/null
@@ -0,0 +1,40 @@
+From 63a69ee23ce4980618c5304dbdcbf10bf82a7f1a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 13:49:19 +0800
+Subject: io_uring: prevent opcode speculation
+
+From: Pavel Begunkov <asml.silence@gmail.com>
+
+[ Upstream commit 1e988c3fe1264708f4f92109203ac5b1d65de50b ]
+
+sqe->opcode is used for different tables, make sure we santitise it
+against speculations.
+
+Cc: stable@vger.kernel.org
+Fixes: d3656344fea03 ("io_uring: add lookup table for various opcode needs")
+Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
+Reviewed-by: Li Zetao <lizetao1@huawei.com>
+Link: https://lore.kernel.org/r/7eddbf31c8ca0a3947f8ed98271acc2b4349c016.1739568408.git.asml.silence@gmail.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Robert Garcia <rob_garcia@163.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ io_uring/io_uring.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
+index d0d9ff6b87a08..fdb8afdb01353 100644
+--- a/io_uring/io_uring.c
++++ b/io_uring/io_uring.c
+@@ -2031,6 +2031,8 @@ static int io_init_req(struct io_ring_ctx *ctx, struct io_kiocb *req,
+               req->opcode = 0;
+               return io_init_fail_req(req, -EINVAL);
+       }
++      opcode = array_index_nospec(opcode, IORING_OP_LAST);
++
+       def = &io_op_defs[opcode];
+       if (unlikely(sqe_flags & ~SQE_COMMON_FLAGS)) {
+               /* enforce forwards compatibility on users */
+-- 
+2.53.0
+
diff --git a/queue-6.1/s390-debug-reject-zero-length-input-before-trimming-.patch b/queue-6.1/s390-debug-reject-zero-length-input-before-trimming-.patch
new file mode 100644 (file)
index 0000000..64374dc
--- /dev/null
@@ -0,0 +1,49 @@
+From 6855bc14b0b41855665bb9062c8c55917450ac26 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 10:28:39 +0800
+Subject: s390/debug: Reject zero-length input before trimming a newline
+
+From: Pengpeng Hou <pengpeng@iscas.ac.cn>
+
+[ Upstream commit c366a7b5ed7564e41345c380285bd3f6cb98971b ]
+
+debug_get_user_string() copies the userspace buffer into a newly
+allocated NUL-terminated buffer and then unconditionally looks at
+buffer[user_len - 1] to strip a trailing newline.
+
+A zero-length write reaches this helper unchanged, so the newline trim
+reads before the start of the allocated buffer.
+
+Reject empty writes before accessing the last input byte.
+
+Fixes: 66a464dbc8e0 ("[PATCH] s390: debug feature changes")
+Cc: stable@vger.kernel.org
+Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
+Reviewed-by: Benjamin Block <bblock@linux.ibm.com>
+Reviewed-by: Vasily Gorbik <gor@linux.ibm.com>
+Tested-by: Vasily Gorbik <gor@linux.ibm.com>
+Link: https://lore.kernel.org/r/20260417073530.96002-1-pengpeng@iscas.ac.cn
+Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
+Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/s390/kernel/debug.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c
+index 9a94979ee4de5..65302aea77e85 100644
+--- a/arch/s390/kernel/debug.c
++++ b/arch/s390/kernel/debug.c
+@@ -1268,6 +1268,9 @@ static inline char *debug_get_user_string(const char __user *user_buf,
+ {
+       char *buffer;
++      if (!user_len)
++              return ERR_PTR(-EINVAL);
++
+       buffer = kmalloc(user_len + 1, GFP_KERNEL);
+       if (!buffer)
+               return ERR_PTR(-ENOMEM);
+-- 
+2.53.0
+
index 88776c115d0c351288273335b76a46dfb38e13e8..e2ffbe3cbd810c15e7a62a0e570b475529c28173 100644 (file)
@@ -830,3 +830,5 @@ drm-gma500-oaktrail_lvds-fix-hang-on-init-failure.patch
 drm-gma500-oaktrail_lvds-fix-i2c-adapter-leaks-on-init.patch
 io-wq-check-that-the-predecessor-is-hashed-in-io_wq_remove_pending.patch
 net-rds-reset-op_nents-when-zerocopy-page-pin-fails.patch
+io_uring-prevent-opcode-speculation.patch
+s390-debug-reject-zero-length-input-before-trimming-.patch
diff --git a/queue-6.12/drm-xe-hdcp-add-null-check-for-media_gt-in-intel_hdc.patch b/queue-6.12/drm-xe-hdcp-add-null-check-for-media_gt-in-intel_hdc.patch
new file mode 100644 (file)
index 0000000..90f066e
--- /dev/null
@@ -0,0 +1,70 @@
+From 7b88280e0ecc6ff061528c7431048fd3e8b958b0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 22 May 2026 16:40:58 -0300
+Subject: drm/xe/hdcp: Add NULL check for media_gt in
+ intel_hdcp_gsc_check_status()
+
+From: Gustavo Sousa <gustavo.sousa@intel.com>
+
+commit 60a1e131a811b68703da58fd805ab359b704ab03 upstream.
+
+When media GT is disabled via configfs, there is no allocation for
+media_gt, which is kept as NULL.  In such scenario,
+intel_hdcp_gsc_check_status() results in a kernel pagefault error due to
+&gt->uc.gsc being evaluated as an invalid memory address.
+
+Fix that by introducing a NULL check on media_gt and bailing out early
+if so.
+
+While at it, also drop the NULL check for gsc, since it can't be NULL if
+media_gt is not NULL.
+
+v2:
+  - Get address for gsc only after checking that gt is not NULL.
+    (Shuicheng)
+  - Drop the NULL check for gsc. (Shuicheng)
+v3:
+  - Add "Fixes" and "Cc: <stable...>" tags. (Matt)
+
+Fixes: 4af50beb4e0f ("drm/xe: Use gsc_proxy_init_done to check proxy status")
+Cc: <stable@vger.kernel.org> # v6.10+
+Reviewed-by: Matt Roper <matthew.d.roper@intel.com>
+Reviewed-by: Shuicheng Lin <shuicheng.lin@intel.com>
+Link: https://patch.msgid.link/20260416-check-for-null-media_gt-in-intel_hdcp_gsc_check_status-v2-1-9adb9fd3b621@intel.com
+Signed-off-by: Gustavo Sousa <gustavo.sousa@intel.com>
+(cherry picked from commit bfaf87e84ca3ca3f6e275f9ae56da47a8b55ffd1)
+Signed-off-by: Matthew Brost <matthew.brost@intel.com>
+Signed-off-by: Gustavo Sousa <gustavo.sousa@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/xe/display/xe_hdcp_gsc.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c b/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c
+index f4332f06b6c80..695d625c83ee6 100644
+--- a/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c
++++ b/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c
+@@ -39,10 +39,18 @@ bool intel_hdcp_gsc_check_status(struct xe_device *xe)
+ {
+       struct xe_tile *tile = xe_device_get_root_tile(xe);
+       struct xe_gt *gt = tile->media_gt;
+-      struct xe_gsc *gsc = &gt->uc.gsc;
++      struct xe_gsc *gsc;
+       bool ret = true;
+-      if (!gsc || !xe_uc_fw_is_enabled(&gsc->fw)) {
++      if (!gt) {
++              drm_dbg_kms(&xe->drm,
++                          "not checking GSC status for HDCP2.x: media GT not present or disabled\n");
++              return false;
++      }
++
++      gsc = &gt->uc.gsc;
++
++      if (!xe_uc_fw_is_enabled(&gsc->fw)) {
+               drm_dbg_kms(&xe->drm,
+                           "GSC Components not ready for HDCP2.x\n");
+               return false;
+-- 
+2.53.0
+
diff --git a/queue-6.12/ksmbd-validate-owner-of-durable-handle-on-reconnect.patch b/queue-6.12/ksmbd-validate-owner-of-durable-handle-on-reconnect.patch
new file mode 100644 (file)
index 0000000..7e619b3
--- /dev/null
@@ -0,0 +1,339 @@
+From 8b7f7f2e71f479151b87f7729ff68b0b9980597e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 22 May 2026 15:25:12 +0800
+Subject: ksmbd: validate owner of durable handle on reconnect
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit 49110a8ce654bbe56bef7c5e44cce31f4b102b8a ]
+
+Currently, ksmbd does not verify if the user attempting to reconnect
+to a durable handle is the same user who originally opened the file.
+This allows any authenticated user to hijack an orphaned durable handle
+by predicting or brute-forcing the persistent ID.
+
+According to MS-SMB2, the server MUST verify that the SecurityContext
+of the reconnect request matches the SecurityContext associated with
+the existing open.
+Add a durable_owner structure to ksmbd_file to store the original opener's
+UID, GID, and account name. and catpure the owner information when a file
+handle becomes orphaned. and implementing ksmbd_vfs_compare_durable_owner()
+to validate the identity of the requester during SMB2_CREATE (DHnC).
+
+Fixes: c8efcc786146 ("ksmbd: add support for durable handles v1/v2")
+Reported-by: Davide Ornaghi <d.ornaghi97@gmail.com>
+Reported-by: Navaneeth K <knavaneeth786@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+[ Minor context conflict resolved. ]
+Signed-off-by: Alva Lan <alvalan9@foxmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/smb/server/mgmt/user_session.c |  7 ++-
+ fs/smb/server/oplock.c            |  7 +++
+ fs/smb/server/oplock.h            |  1 +
+ fs/smb/server/smb2pdu.c           |  3 +-
+ fs/smb/server/vfs_cache.c         | 87 +++++++++++++++++++++++++++----
+ fs/smb/server/vfs_cache.h         | 12 ++++-
+ 6 files changed, 102 insertions(+), 15 deletions(-)
+
+diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c
+index 151248e02e9eb..ecd511351f19b 100644
+--- a/fs/smb/server/mgmt/user_session.c
++++ b/fs/smb/server/mgmt/user_session.c
+@@ -161,11 +161,10 @@ void ksmbd_session_destroy(struct ksmbd_session *sess)
+       if (!sess)
+               return;
++      ksmbd_tree_conn_session_logoff(sess);
++      ksmbd_destroy_file_table(sess);
+       if (sess->user)
+               ksmbd_free_user(sess->user);
+-
+-      ksmbd_tree_conn_session_logoff(sess);
+-      ksmbd_destroy_file_table(&sess->file_table);
+       ksmbd_launch_ksmbd_durable_scavenger();
+       ksmbd_session_rpc_clear_list(sess);
+       free_channel_list(sess);
+@@ -402,7 +401,7 @@ void destroy_previous_session(struct ksmbd_conn *conn,
+               goto out;
+       }
+-      ksmbd_destroy_file_table(&prev_sess->file_table);
++      ksmbd_destroy_file_table(prev_sess);
+       prev_sess->state = SMB2_SESSION_EXPIRED;
+       ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_SETUP);
+       ksmbd_launch_ksmbd_durable_scavenger();
+diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
+index 590ddd31a68da..bbb2cb3782d0c 100644
+--- a/fs/smb/server/oplock.c
++++ b/fs/smb/server/oplock.c
+@@ -1841,6 +1841,7 @@ int smb2_check_durable_oplock(struct ksmbd_conn *conn,
+                             struct ksmbd_share_config *share,
+                             struct ksmbd_file *fp,
+                             struct lease_ctx_info *lctx,
++                            struct ksmbd_user *user,
+                             char *name)
+ {
+       struct oplock_info *opinfo = opinfo_get(fp);
+@@ -1849,6 +1850,12 @@ int smb2_check_durable_oplock(struct ksmbd_conn *conn,
+       if (!opinfo)
+               return 0;
++      if (ksmbd_vfs_compare_durable_owner(fp, user) == false) {
++              ksmbd_debug(SMB, "Durable handle reconnect failed: owner mismatch\n");
++              ret = -EBADF;
++              goto out;
++      }
++
+       if (opinfo->is_lease == false) {
+               if (lctx) {
+                       pr_err("create context include lease\n");
+diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h
+index 921e3199e4df4..d91a8266e065e 100644
+--- a/fs/smb/server/oplock.h
++++ b/fs/smb/server/oplock.h
+@@ -126,5 +126,6 @@ int smb2_check_durable_oplock(struct ksmbd_conn *conn,
+                             struct ksmbd_share_config *share,
+                             struct ksmbd_file *fp,
+                             struct lease_ctx_info *lctx,
++                            struct ksmbd_user *user,
+                             char *name);
+ #endif /* __KSMBD_OPLOCK_H */
+diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
+index 700d9da3c65a9..a691801e1d7bd 100644
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -3016,7 +3016,8 @@ int smb2_open(struct ksmbd_work *work)
+               }
+               if (dh_info.reconnected == true) {
+-                      rc = smb2_check_durable_oplock(conn, share, dh_info.fp, lc, name);
++                      rc = smb2_check_durable_oplock(conn, share, dh_info.fp,
++                                      lc, sess->user, name);
+                       if (rc) {
+                               ksmbd_put_durable_fd(dh_info.fp);
+                               goto err_out2;
+diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
+index 08f25a2d75416..d29cc1d01bd2c 100644
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -18,6 +18,7 @@
+ #include "connection.h"
+ #include "mgmt/tree_connect.h"
+ #include "mgmt/user_session.h"
++#include "mgmt/user_config.h"
+ #include "smb_common.h"
+ #include "server.h"
+@@ -383,6 +384,8 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)
+       if (ksmbd_stream_fd(fp))
+               kfree(fp->stream.name);
++      kfree(fp->owner.name);
++
+       kmem_cache_free(filp_cache, fp);
+ }
+@@ -694,11 +697,13 @@ void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp,
+ }
+ static int
+-__close_file_table_ids(struct ksmbd_file_table *ft,
++__close_file_table_ids(struct ksmbd_session *sess,
+                      struct ksmbd_tree_connect *tcon,
+                      bool (*skip)(struct ksmbd_tree_connect *tcon,
+-                                  struct ksmbd_file *fp))
++                                  struct ksmbd_file *fp,
++                                  struct ksmbd_user *user))
+ {
++      struct ksmbd_file_table *ft = &sess->file_table;
+       struct ksmbd_file *fp;
+       unsigned int id = 0;
+       int num = 0;
+@@ -711,7 +716,7 @@ __close_file_table_ids(struct ksmbd_file_table *ft,
+                       break;
+               }
+-              if (skip(tcon, fp) ||
++              if (skip(tcon, fp, sess->user) ||
+                   !atomic_dec_and_test(&fp->refcount)) {
+                       id++;
+                       write_unlock(&ft->lock);
+@@ -763,7 +768,8 @@ static inline bool is_reconnectable(struct ksmbd_file *fp)
+ }
+ static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon,
+-                             struct ksmbd_file *fp)
++                             struct ksmbd_file *fp,
++                             struct ksmbd_user *user)
+ {
+       return fp->tcon != tcon;
+ }
+@@ -898,8 +904,62 @@ void ksmbd_stop_durable_scavenger(void)
+       kthread_stop(server_conf.dh_task);
+ }
++/*
++ * ksmbd_vfs_copy_durable_owner - Copy owner info for durable reconnect
++ * @fp: ksmbd file pointer to store owner info
++ * @user: user pointer to copy from
++ *
++ * This function binds the current user's identity to the file handle
++ * to satisfy MS-SMB2 Step 8 (SecurityContext matching) during reconnect.
++ *
++ * Return: 0 on success, or negative error code on failure
++ */
++static int ksmbd_vfs_copy_durable_owner(struct ksmbd_file *fp,
++              struct ksmbd_user *user)
++{
++      if (!user)
++              return -EINVAL;
++
++      /* Duplicate the user name to ensure identity persistence */
++      fp->owner.name = kstrdup(user->name, GFP_KERNEL);
++      if (!fp->owner.name)
++              return -ENOMEM;
++
++      fp->owner.uid = user->uid;
++      fp->owner.gid = user->gid;
++
++      return 0;
++}
++
++/**
++ * ksmbd_vfs_compare_durable_owner - Verify if the requester is original owner
++ * @fp: existing ksmbd file pointer
++ * @user: user pointer of the reconnect requester
++ *
++ * Compares the UID, GID, and name of the current requester against the
++ * original owner stored in the file handle.
++ *
++ * Return: true if the user matches, false otherwise
++ */
++bool ksmbd_vfs_compare_durable_owner(struct ksmbd_file *fp,
++              struct ksmbd_user *user)
++{
++      if (!user || !fp->owner.name)
++              return false;
++
++      /* Check if the UID and GID match first (fast path) */
++      if (fp->owner.uid != user->uid || fp->owner.gid != user->gid)
++              return false;
++
++      /* Validate the account name to ensure the same SecurityContext */
++      if (strcmp(fp->owner.name, user->name))
++              return false;
++
++      return true;
++}
++
+ static bool session_fd_check(struct ksmbd_tree_connect *tcon,
+-                           struct ksmbd_file *fp)
++                           struct ksmbd_file *fp, struct ksmbd_user *user)
+ {
+       struct ksmbd_inode *ci;
+       struct oplock_info *op;
+@@ -909,6 +969,9 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon,
+       if (!is_reconnectable(fp))
+               return false;
++      if (ksmbd_vfs_copy_durable_owner(fp, user))
++              return false;
++
+       conn = fp->conn;
+       ci = fp->f_ci;
+       down_write(&ci->m_lock);
+@@ -940,7 +1003,7 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon,
+ void ksmbd_close_tree_conn_fds(struct ksmbd_work *work)
+ {
+-      int num = __close_file_table_ids(&work->sess->file_table,
++      int num = __close_file_table_ids(work->sess,
+                                        work->tcon,
+                                        tree_conn_fd_check);
+@@ -949,7 +1012,7 @@ void ksmbd_close_tree_conn_fds(struct ksmbd_work *work)
+ void ksmbd_close_session_fds(struct ksmbd_work *work)
+ {
+-      int num = __close_file_table_ids(&work->sess->file_table,
++      int num = __close_file_table_ids(work->sess,
+                                        work->tcon,
+                                        session_fd_check);
+@@ -1046,6 +1109,10 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp)
+       }
+       up_write(&ci->m_lock);
++      fp->owner.uid = fp->owner.gid = 0;
++      kfree(fp->owner.name);
++      fp->owner.name = NULL;
++
+       return 0;
+ }
+@@ -1060,12 +1127,14 @@ int ksmbd_init_file_table(struct ksmbd_file_table *ft)
+       return 0;
+ }
+-void ksmbd_destroy_file_table(struct ksmbd_file_table *ft)
++void ksmbd_destroy_file_table(struct ksmbd_session *sess)
+ {
++      struct ksmbd_file_table *ft = &sess->file_table;
++
+       if (!ft->idr)
+               return;
+-      __close_file_table_ids(ft, NULL, session_fd_check);
++      __close_file_table_ids(sess, NULL, session_fd_check);
+       idr_destroy(ft->idr);
+       kfree(ft->idr);
+       ft->idr = NULL;
+diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h
+index 5bbb179736c29..1b2a947490ca5 100644
+--- a/fs/smb/server/vfs_cache.h
++++ b/fs/smb/server/vfs_cache.h
+@@ -67,6 +67,13 @@ enum {
+       FP_CLOSED
+ };
++/* Owner information for durable handle reconnect */
++struct durable_owner {
++      unsigned int uid;
++      unsigned int gid;
++      char *name;
++};
++
+ struct ksmbd_file {
+       struct file                     *filp;
+       u64                             persistent_id;
+@@ -111,6 +118,7 @@ struct ksmbd_file {
+       bool                            is_durable;
+       bool                            is_persistent;
+       bool                            is_resilient;
++      struct durable_owner            owner;
+ };
+ static inline void set_ctx_actor(struct dir_context *ctx,
+@@ -137,7 +145,7 @@ static inline bool ksmbd_stream_fd(struct ksmbd_file *fp)
+ }
+ int ksmbd_init_file_table(struct ksmbd_file_table *ft);
+-void ksmbd_destroy_file_table(struct ksmbd_file_table *ft);
++void ksmbd_destroy_file_table(struct ksmbd_session *sess);
+ int ksmbd_close_fd(struct ksmbd_work *work, u64 id);
+ struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, u64 id);
+ struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id);
+@@ -163,6 +171,8 @@ void ksmbd_free_global_file_table(void);
+ void ksmbd_set_fd_limit(unsigned long limit);
+ void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp,
+                        unsigned int state);
++bool ksmbd_vfs_compare_durable_owner(struct ksmbd_file *fp,
++              struct ksmbd_user *user);
+ /*
+  * INODE hash
+-- 
+2.53.0
+
diff --git a/queue-6.12/mptcp-pm-add_addr-rtx-allow-id-0.patch b/queue-6.12/mptcp-pm-add_addr-rtx-allow-id-0.patch
new file mode 100644 (file)
index 0000000..fcf5c87
--- /dev/null
@@ -0,0 +1,46 @@
+From cdadf9d85c986650e37a2c7a7d17c343677513aa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 05:08:48 +0200
+Subject: mptcp: pm: ADD_ADDR rtx: allow ID 0
+
+From: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+
+commit 03f324f3f1f7619a47b9c91282cb12775ab0a2f1 upstream.
+
+ADD_ADDR can be sent for the ID 0, which corresponds to the local
+address and port linked to the initial subflow.
+
+Indeed, this address could be removed, and re-added later on, e.g. what
+is done in the "delete re-add signal" MPTCP Join selftests. So no reason
+to ignore it.
+
+Fixes: 00cfd77b9063 ("mptcp: retransmit ADD_ADDR when timeout")
+Cc: stable@vger.kernel.org
+Reviewed-by: Mat Martineau <martineau@kernel.org>
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Link: https://patch.msgid.link/20260505-net-mptcp-pm-fixes-7-1-rc3-v1-2-fca8091060a4@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+[ applied to net/mptcp/pm_netlink.c instead of upstream's pm_kernel.c ]
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mptcp/pm_netlink.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c
+index 5d892583ab4ef..857e8db670a75 100644
+--- a/net/mptcp/pm_netlink.c
++++ b/net/mptcp/pm_netlink.c
+@@ -304,9 +304,6 @@ static void mptcp_pm_add_timer(struct timer_list *timer)
+       if (inet_sk_state_load(sk) == TCP_CLOSE)
+               return;
+-      if (!entry->addr.id)
+-              return;
+-
+       bh_lock_sock(sk);
+       if (sock_owned_by_user(sk)) {
+               /* Try again later. */
+-- 
+2.53.0
+
diff --git a/queue-6.12/mptcp-pm-add_addr-rtx-always-decrease-sk-refcount.patch b/queue-6.12/mptcp-pm-add_addr-rtx-always-decrease-sk-refcount.patch
new file mode 100644 (file)
index 0000000..1480ff0
--- /dev/null
@@ -0,0 +1,62 @@
+From dd1e3ca71e8ed4d5b7e3ca41f25d4da15b8e03df Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 05:08:49 +0200
+Subject: mptcp: pm: ADD_ADDR rtx: always decrease sk refcount
+
+From: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+
+commit 9634cb35af17019baec21ca648516ce376fa10e6 upstream.
+
+When an ADD_ADDR is retransmitted, the sk is held in sk_reset_timer().
+It should then be released in all cases at the end.
+
+Some (unlikely) checks were returning directly instead of calling
+sock_put() to decrease the refcount. Jump to a new 'exit' label to call
+__sock_put() (which will become sock_put() in the next commit) to fix
+this potential leak.
+
+While at it, drop the '!msk' check which cannot happen because it is
+never reset, and explicitly mark the remaining one as "unlikely".
+
+Fixes: 00cfd77b9063 ("mptcp: retransmit ADD_ADDR when timeout")
+Cc: stable@vger.kernel.org
+Reviewed-by: Mat Martineau <martineau@kernel.org>
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Link: https://patch.msgid.link/20260505-net-mptcp-pm-fixes-7-1-rc3-v1-4-fca8091060a4@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+[ applied to net/mptcp/pm_netlink.c instead of upstream's pm_kernel.c ]
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mptcp/pm_netlink.c | 8 +++-----
+ 1 file changed, 3 insertions(+), 5 deletions(-)
+
+diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c
+index 857e8db670a75..be531df02c371 100644
+--- a/net/mptcp/pm_netlink.c
++++ b/net/mptcp/pm_netlink.c
+@@ -298,11 +298,8 @@ static void mptcp_pm_add_timer(struct timer_list *timer)
+       pr_debug("msk=%p\n", msk);
+-      if (!msk)
+-              return;
+-
+-      if (inet_sk_state_load(sk) == TCP_CLOSE)
+-              return;
++      if (unlikely(inet_sk_state_load(sk) == TCP_CLOSE))
++              goto exit;
+       bh_lock_sock(sk);
+       if (sock_owned_by_user(sk)) {
+@@ -340,6 +337,7 @@ static void mptcp_pm_add_timer(struct timer_list *timer)
+ out:
+       bh_unlock_sock(sk);
++exit:
+       __sock_put(sk);
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/mptcp-pm-add_addr-rtx-free-sk-if-last.patch b/queue-6.12/mptcp-pm-add_addr-rtx-free-sk-if-last.patch
new file mode 100644 (file)
index 0000000..d70b0b4
--- /dev/null
@@ -0,0 +1,127 @@
+From 4c2a4761538bc2d0cb491cc4d417bed5f2065790 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 05:08:50 +0200
+Subject: mptcp: pm: ADD_ADDR rtx: free sk if last
+
+From: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+
+commit b7b9a461569734d33d3259d58d2507adfac107ed upstream.
+
+When an ADD_ADDR is retransmitted, the sk is held in sk_reset_timer(),
+and released at the end.
+
+If at that moment, it was the last reference being held, the sk would
+not be freed. sock_put() should then be called instead of __sock_put().
+
+But that's not enough: if it is the last reference, sock_put() will call
+sk_free(), which will end up calling sk_stop_timer_sync() on the same
+timer, and waiting indefinitely to finish. So it is needed to mark that
+the timer is done at the end of the timer handler when it has not been
+rescheduled, not to call sk_stop_timer_sync() on "itself".
+
+Fixes: 00cfd77b9063 ("mptcp: retransmit ADD_ADDR when timeout")
+Cc: stable@vger.kernel.org
+Reviewed-by: Mat Martineau <martineau@kernel.org>
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Link: https://patch.msgid.link/20260505-net-mptcp-pm-fixes-7-1-rc3-v1-5-fca8091060a4@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+[ Applied to net/mptcp/pm_netlink.c instead of upstream's pm_kernel.c.
+  Also, there were conflicts, because commit 30549eebc4d8 ("mptcp: make
+  ADD_ADDR retransmission timeout adaptive") is not in this version and
+  changed the context. Also, other conflicts were due to newer patches
+  being backported with resolved conflicts before this one. ]
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mptcp/pm_netlink.c | 28 +++++++++++++++++-----------
+ 1 file changed, 17 insertions(+), 11 deletions(-)
+
+diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c
+index be531df02c371..4ff6721ad5c7a 100644
+--- a/net/mptcp/pm_netlink.c
++++ b/net/mptcp/pm_netlink.c
+@@ -22,6 +22,7 @@ struct mptcp_pm_add_entry {
+       struct list_head        list;
+       struct mptcp_addr_info  addr;
+       u8                      retrans_times;
++      bool                    timer_done;
+       struct timer_list       add_timer;
+       struct mptcp_sock       *sock;
+       struct rcu_head         rcu;
+@@ -294,22 +295,22 @@ static void mptcp_pm_add_timer(struct timer_list *timer)
+       struct mptcp_pm_add_entry *entry = from_timer(entry, timer, add_timer);
+       struct mptcp_sock *msk = entry->sock;
+       struct sock *sk = (struct sock *)msk;
+-      unsigned int timeout;
++      unsigned int timeout = 0;
+       pr_debug("msk=%p\n", msk);
++      bh_lock_sock(sk);
+       if (unlikely(inet_sk_state_load(sk) == TCP_CLOSE))
+-              goto exit;
++              goto out;
+-      bh_lock_sock(sk);
+       if (sock_owned_by_user(sk)) {
+               /* Try again later. */
+-              sk_reset_timer(sk, timer, jiffies + HZ / 20);
++              timeout = HZ / 20;
+               goto out;
+       }
+       if (mptcp_pm_should_add_signal_addr(msk)) {
+-              sk_reset_timer(sk, timer, jiffies + HZ);
++              timeout = HZ;
+               goto out;
+       }
+@@ -326,9 +327,8 @@ static void mptcp_pm_add_timer(struct timer_list *timer)
+               entry->retrans_times++;
+       }
+-      if (entry->retrans_times < ADD_ADDR_RETRANS_MAX)
+-              sk_reset_timer(sk, timer,
+-                             jiffies + timeout);
++      if (entry->retrans_times >= ADD_ADDR_RETRANS_MAX)
++              timeout = 0;
+       spin_unlock_bh(&msk->pm.lock);
+@@ -336,9 +336,13 @@ static void mptcp_pm_add_timer(struct timer_list *timer)
+               mptcp_pm_subflow_established(msk);
+ out:
++      if (timeout)
++              sk_reset_timer(sk, timer, jiffies + timeout);
++      else
++              /* if sock_put calls sk_free: avoid waiting for this timer */
++              entry->timer_done = true;
+       bh_unlock_sock(sk);
+-exit:
+-      __sock_put(sk);
++      sock_put(sk);
+ }
+ struct mptcp_pm_add_entry *
+@@ -402,6 +406,7 @@ bool mptcp_pm_alloc_anno_list(struct mptcp_sock *msk,
+       timer_setup(&add_entry->add_timer, mptcp_pm_add_timer, 0);
+ reset_timer:
++      add_entry->timer_done = false;
+       timeout = mptcp_get_add_addr_timeout(net);
+       if (timeout)
+               sk_reset_timer(sk, &add_entry->add_timer, jiffies + timeout);
+@@ -422,7 +427,8 @@ void mptcp_pm_free_anno_list(struct mptcp_sock *msk)
+       spin_unlock_bh(&msk->pm.lock);
+       list_for_each_entry_safe(entry, tmp, &free_list, list) {
+-              sk_stop_timer_sync(sk, &entry->add_timer);
++              if (!entry->timer_done)
++                      sk_stop_timer_sync(sk, &entry->add_timer);
+               kfree_rcu(entry, rcu);
+       }
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.12/mptcp-sync-the-msk-sndbuf-at-accept-time.patch b/queue-6.12/mptcp-sync-the-msk-sndbuf-at-accept-time.patch
new file mode 100644 (file)
index 0000000..341e2df
--- /dev/null
@@ -0,0 +1,72 @@
+From 480cb287ee0fc8988f79ff97c57b801c8fc9fb40 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 05:08:47 +0200
+Subject: mptcp: sync the msk->sndbuf at accept() time
+
+From: Gang Yan <yangang@kylinos.cn>
+
+commit fcf04b14334641f4b0b8647824480935e9416d52 upstream.
+
+On passive MPTCP connections, the msk sndbuf is not updated correctly.
+
+The root cause is an order issue in the accept path:
+
+- tcp_check_req() -> subflow_syn_recv_sock() -> mptcp_sk_clone_init()
+  calls __mptcp_propagate_sndbuf() to copy the ssk sndbuf into msk
+
+- Later, tcp_child_process() -> tcp_init_transfer() ->
+  tcp_sndbuf_expand() grows the ssk sndbuf.
+
+So __mptcp_propagate_sndbuf() runs before the ssk sndbuf has been
+expanded and the msk ends up with a much smaller sndbuf than the
+subflow:
+
+  MPTCP: msk->sndbuf:20480, msk->first->sndbuf:2626560
+
+Fix this by moving the __mptcp_propagate_sndbuf() call from
+mptcp_sk_clone_init() -- the ssk sndbuf is not yet finalized there -- to
+__mptcp_propagate_sndbuf() at accept() time, when the ssk sndbuf has
+been fully expanded by tcp_sndbuf_expand().
+
+Fixes: 8005184fd1ca ("mptcp: refactor sndbuf auto-tuning")
+Cc: stable@vger.kernel.org
+Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/602
+Signed-off-by: Gang Yan <yangang@kylinos.cn>
+Acked-by: Paolo Abeni <pabeni@redhat.com>
+Reviewed-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Link: https://patch.msgid.link/20260420-net-mptcp-sync-sndbuf-accept-v1-1-e3523e3aeb44@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+[ No conflicts, but move __mptcp_propagate_sndbuf() above the for-loop
+  (mptcp_for_each_subflow()) present in this version, which will modify
+  'subflow' used by __mptcp_propagate_sndbuf() in this new patch. ]
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mptcp/protocol.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
+index 7dbb666c72c30..c1b1fb0fe8bcb 100644
+--- a/net/mptcp/protocol.c
++++ b/net/mptcp/protocol.c
+@@ -3493,7 +3493,6 @@ struct sock *mptcp_sk_clone_init(const struct sock *sk,
+        * uses the correct data
+        */
+       mptcp_copy_inaddrs(nsk, ssk);
+-      __mptcp_propagate_sndbuf(nsk, ssk);
+       mptcp_rcv_space_init(msk, ssk);
+       msk->rcvq_space.time = mptcp_stamp();
+@@ -4101,6 +4100,8 @@ static int mptcp_stream_accept(struct socket *sock, struct socket *newsock,
+               msk = mptcp_sk(newsk);
+               msk->in_accept_queue = 0;
++              __mptcp_propagate_sndbuf(newsk, mptcp_subflow_tcp_sock(subflow));
++
+               /* set ssk->sk_socket of accept()ed flows to mptcp socket.
+                * This is needed so NOSPACE flag can be set from tcp stack.
+                */
+-- 
+2.53.0
+
diff --git a/queue-6.12/s390-debug-reject-zero-length-input-before-trimming-.patch b/queue-6.12/s390-debug-reject-zero-length-input-before-trimming-.patch
new file mode 100644 (file)
index 0000000..1e97a77
--- /dev/null
@@ -0,0 +1,49 @@
+From a9982a19d49df8c9a2e0d654fb5e0e53f94cda44 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 10:28:29 +0800
+Subject: s390/debug: Reject zero-length input before trimming a newline
+
+From: Pengpeng Hou <pengpeng@iscas.ac.cn>
+
+[ Upstream commit c366a7b5ed7564e41345c380285bd3f6cb98971b ]
+
+debug_get_user_string() copies the userspace buffer into a newly
+allocated NUL-terminated buffer and then unconditionally looks at
+buffer[user_len - 1] to strip a trailing newline.
+
+A zero-length write reaches this helper unchanged, so the newline trim
+reads before the start of the allocated buffer.
+
+Reject empty writes before accessing the last input byte.
+
+Fixes: 66a464dbc8e0 ("[PATCH] s390: debug feature changes")
+Cc: stable@vger.kernel.org
+Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
+Reviewed-by: Benjamin Block <bblock@linux.ibm.com>
+Reviewed-by: Vasily Gorbik <gor@linux.ibm.com>
+Tested-by: Vasily Gorbik <gor@linux.ibm.com>
+Link: https://lore.kernel.org/r/20260417073530.96002-1-pengpeng@iscas.ac.cn
+Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
+Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/s390/kernel/debug.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c
+index e4b324dcfe0d3..16bb554a07fef 100644
+--- a/arch/s390/kernel/debug.c
++++ b/arch/s390/kernel/debug.c
+@@ -1256,6 +1256,9 @@ static inline char *debug_get_user_string(const char __user *user_buf,
+ {
+       char *buffer;
++      if (!user_len)
++              return ERR_PTR(-EINVAL);
++
+       buffer = kmalloc(user_len + 1, GFP_KERNEL);
+       if (!buffer)
+               return ERR_PTR(-ENOMEM);
+-- 
+2.53.0
+
diff --git a/queue-6.12/series b/queue-6.12/series
new file mode 100644 (file)
index 0000000..a404786
--- /dev/null
@@ -0,0 +1,7 @@
+mptcp-sync-the-msk-sndbuf-at-accept-time.patch
+mptcp-pm-add_addr-rtx-allow-id-0.patch
+mptcp-pm-add_addr-rtx-always-decrease-sk-refcount.patch
+mptcp-pm-add_addr-rtx-free-sk-if-last.patch
+ksmbd-validate-owner-of-durable-handle-on-reconnect.patch
+drm-xe-hdcp-add-null-check-for-media_gt-in-intel_hdc.patch
+s390-debug-reject-zero-length-input-before-trimming-.patch
diff --git a/queue-6.18/drm-xe-hdcp-add-null-check-for-media_gt-in-intel_hdc.patch b/queue-6.18/drm-xe-hdcp-add-null-check-for-media_gt-in-intel_hdc.patch
new file mode 100644 (file)
index 0000000..4f1ebcc
--- /dev/null
@@ -0,0 +1,70 @@
+From 40136dd811ff5838af10483a8b38b16688d2a90b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 22 May 2026 15:47:22 -0300
+Subject: drm/xe/hdcp: Add NULL check for media_gt in
+ intel_hdcp_gsc_check_status()
+
+From: Gustavo Sousa <gustavo.sousa@intel.com>
+
+commit 60a1e131a811b68703da58fd805ab359b704ab03 upstream.
+
+When media GT is disabled via configfs, there is no allocation for
+media_gt, which is kept as NULL.  In such scenario,
+intel_hdcp_gsc_check_status() results in a kernel pagefault error due to
+&gt->uc.gsc being evaluated as an invalid memory address.
+
+Fix that by introducing a NULL check on media_gt and bailing out early
+if so.
+
+While at it, also drop the NULL check for gsc, since it can't be NULL if
+media_gt is not NULL.
+
+v2:
+  - Get address for gsc only after checking that gt is not NULL.
+    (Shuicheng)
+  - Drop the NULL check for gsc. (Shuicheng)
+v3:
+  - Add "Fixes" and "Cc: <stable...>" tags. (Matt)
+
+Fixes: 4af50beb4e0f ("drm/xe: Use gsc_proxy_init_done to check proxy status")
+Cc: <stable@vger.kernel.org> # v6.10+
+Reviewed-by: Matt Roper <matthew.d.roper@intel.com>
+Reviewed-by: Shuicheng Lin <shuicheng.lin@intel.com>
+Link: https://patch.msgid.link/20260416-check-for-null-media_gt-in-intel_hdcp_gsc_check_status-v2-1-9adb9fd3b621@intel.com
+Signed-off-by: Gustavo Sousa <gustavo.sousa@intel.com>
+(cherry picked from commit bfaf87e84ca3ca3f6e275f9ae56da47a8b55ffd1)
+Signed-off-by: Matthew Brost <matthew.brost@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/xe/display/xe_hdcp_gsc.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c b/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c
+index 4ae847b628e23..6324f526dcfab 100644
+--- a/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c
++++ b/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c
+@@ -35,11 +35,19 @@ bool intel_hdcp_gsc_check_status(struct drm_device *drm)
+       struct xe_device *xe = to_xe_device(drm);
+       struct xe_tile *tile = xe_device_get_root_tile(xe);
+       struct xe_gt *gt = tile->media_gt;
+-      struct xe_gsc *gsc = &gt->uc.gsc;
++      struct xe_gsc *gsc;
+       bool ret = true;
+       unsigned int fw_ref;
+-      if (!gsc || !xe_uc_fw_is_enabled(&gsc->fw)) {
++      if (!gt) {
++              drm_dbg_kms(&xe->drm,
++                          "not checking GSC status for HDCP2.x: media GT not present or disabled\n");
++              return false;
++      }
++
++      gsc = &gt->uc.gsc;
++
++      if (!xe_uc_fw_is_enabled(&gsc->fw)) {
+               drm_dbg_kms(&xe->drm,
+                           "GSC Components not ready for HDCP2.x\n");
+               return false;
+-- 
+2.53.0
+
diff --git a/queue-6.18/iommu-amd-fix-illegal-cap-mmio-access-in-iommu-debug.patch b/queue-6.18/iommu-amd-fix-illegal-cap-mmio-access-in-iommu-debug.patch
new file mode 100644 (file)
index 0000000..9201a10
--- /dev/null
@@ -0,0 +1,143 @@
+From fddfcb84e88aa2811927313beab1a8e996714169 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 19 Mar 2026 15:37:54 +0800
+Subject: iommu/amd: Fix illegal cap/mmio access in IOMMU debugfs
+
+From: Guanghui Feng <guanghuifeng@linux.alibaba.com>
+
+[ Upstream commit 0e59645683b7b6fa20eceb21a6f420e4f7412943 ]
+
+In the current AMD IOMMU debugfs, when multiple processes simultaneously
+access the IOMMU mmio/cap registers using the IOMMU debugfs, illegal
+access issues can occur in the following execution flow:
+
+1. CPU1: Sets a valid access address using iommu_mmio/capability_write,
+and verifies the access address's validity in iommu_mmio/capability_show
+
+2. CPU2: Sets an invalid address using iommu_mmio/capability_write
+
+3. CPU1: accesses the IOMMU mmio/cap registers based on the invalid
+address, resulting in an illegal access.
+
+This patch modifies the execution process to first verify the address's
+validity and then access it based on the same address, ensuring
+correctness and robustness.
+
+Signed-off-by: Guanghui Feng <guanghuifeng@linux.alibaba.com>
+Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
+Stable-dep-of: 8dfd3d8d7443 ("iommu/amd: Remove latent out-of-bounds access in IOMMU debugfs")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/iommu/amd/debugfs.c | 42 +++++++++++++++++--------------------
+ 1 file changed, 19 insertions(+), 23 deletions(-)
+
+diff --git a/drivers/iommu/amd/debugfs.c b/drivers/iommu/amd/debugfs.c
+index 20b04996441d6..0584ca2f859d9 100644
+--- a/drivers/iommu/amd/debugfs.c
++++ b/drivers/iommu/amd/debugfs.c
+@@ -26,22 +26,19 @@ static ssize_t iommu_mmio_write(struct file *filp, const char __user *ubuf,
+ {
+       struct seq_file *m = filp->private_data;
+       struct amd_iommu *iommu = m->private;
+-      int ret;
+-
+-      iommu->dbg_mmio_offset = -1;
++      int ret, dbg_mmio_offset = iommu->dbg_mmio_offset = -1;
+       if (cnt > OFS_IN_SZ)
+               return -EINVAL;
+-      ret = kstrtou32_from_user(ubuf, cnt, 0, &iommu->dbg_mmio_offset);
++      ret = kstrtou32_from_user(ubuf, cnt, 0, &dbg_mmio_offset);
+       if (ret)
+               return ret;
+-      if (iommu->dbg_mmio_offset > iommu->mmio_phys_end - sizeof(u64)) {
+-              iommu->dbg_mmio_offset = -1;
+-              return  -EINVAL;
+-      }
++      if (dbg_mmio_offset > iommu->mmio_phys_end - sizeof(u64))
++              return -EINVAL;
++      iommu->dbg_mmio_offset = dbg_mmio_offset;
+       return cnt;
+ }
+@@ -49,14 +46,16 @@ static int iommu_mmio_show(struct seq_file *m, void *unused)
+ {
+       struct amd_iommu *iommu = m->private;
+       u64 value;
++      int dbg_mmio_offset = iommu->dbg_mmio_offset;
+-      if (iommu->dbg_mmio_offset < 0) {
++      if (dbg_mmio_offset < 0 || dbg_mmio_offset >
++                      iommu->mmio_phys_end - sizeof(u64)) {
+               seq_puts(m, "Please provide mmio register's offset\n");
+               return 0;
+       }
+-      value = readq(iommu->mmio_base + iommu->dbg_mmio_offset);
+-      seq_printf(m, "Offset:0x%x Value:0x%016llx\n", iommu->dbg_mmio_offset, value);
++      value = readq(iommu->mmio_base + dbg_mmio_offset);
++      seq_printf(m, "Offset:0x%x Value:0x%016llx\n", dbg_mmio_offset, value);
+       return 0;
+ }
+@@ -67,23 +66,20 @@ static ssize_t iommu_capability_write(struct file *filp, const char __user *ubuf
+ {
+       struct seq_file *m = filp->private_data;
+       struct amd_iommu *iommu = m->private;
+-      int ret;
+-
+-      iommu->dbg_cap_offset = -1;
++      int ret, dbg_cap_offset = iommu->dbg_cap_offset = -1;
+       if (cnt > OFS_IN_SZ)
+               return -EINVAL;
+-      ret = kstrtou32_from_user(ubuf, cnt, 0, &iommu->dbg_cap_offset);
++      ret = kstrtou32_from_user(ubuf, cnt, 0, &dbg_cap_offset);
+       if (ret)
+               return ret;
+       /* Capability register at offset 0x14 is the last IOMMU capability register. */
+-      if (iommu->dbg_cap_offset > 0x14) {
+-              iommu->dbg_cap_offset = -1;
++      if (dbg_cap_offset > 0x14)
+               return -EINVAL;
+-      }
++      iommu->dbg_cap_offset = dbg_cap_offset;
+       return cnt;
+ }
+@@ -91,21 +87,21 @@ static int iommu_capability_show(struct seq_file *m, void *unused)
+ {
+       struct amd_iommu *iommu = m->private;
+       u32 value;
+-      int err;
++      int err, dbg_cap_offset = iommu->dbg_cap_offset;
+-      if (iommu->dbg_cap_offset < 0) {
++      if (dbg_cap_offset < 0 || dbg_cap_offset > 0x14) {
+               seq_puts(m, "Please provide capability register's offset in the range [0x00 - 0x14]\n");
+               return 0;
+       }
+-      err = pci_read_config_dword(iommu->dev, iommu->cap_ptr + iommu->dbg_cap_offset, &value);
++      err = pci_read_config_dword(iommu->dev, iommu->cap_ptr + dbg_cap_offset, &value);
+       if (err) {
+               seq_printf(m, "Not able to read capability register at 0x%x\n",
+-                         iommu->dbg_cap_offset);
++                         dbg_cap_offset);
+               return 0;
+       }
+-      seq_printf(m, "Offset:0x%x Value:0x%08x\n", iommu->dbg_cap_offset, value);
++      seq_printf(m, "Offset:0x%x Value:0x%08x\n", dbg_cap_offset, value);
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.18/iommu-amd-remove-latent-out-of-bounds-access-in-iomm.patch b/queue-6.18/iommu-amd-remove-latent-out-of-bounds-access-in-iomm.patch
new file mode 100644 (file)
index 0000000..779d3ce
--- /dev/null
@@ -0,0 +1,83 @@
+From f243339518fa09ba6d6a8aa8807901dbb6e15db3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 10 Apr 2026 14:55:50 +0200
+Subject: iommu/amd: Remove latent out-of-bounds access in IOMMU debugfs
+
+From: Eder Zulian <ezulian@redhat.com>
+
+[ Upstream commit 8dfd3d8d74435344ee8dc9237596959c8b2a6cbe ]
+
+In iommu_mmio_write() and iommu_capability_write(), the variables
+dbg_mmio_offset and dbg_cap_offset are declared as int. However, they
+are populated using kstrtou32_from_user(). If a user provides a
+sufficiently large value, it can become a negative integer.
+
+Prior to this patch, the AMD IOMMU debugfs implementation was already
+protected by different mechanisms.
+
+1. #define OFS_IN_SZ 8 ensures the user string <= 8 bytes, so
+   e.g. 0xffffffff isn't a valid input.
+
+  if (cnt > OFS_IN_SZ)
+     return -EINVAL;
+
+2. Implicit type promotion in iommu_mmio_write(), dbg_mmio_offset is int
+   and iommu->mmio_phys_end is u64
+
+  if (dbg_mmio_offset > iommu->mmio_phys_end - sizeof(u64))
+      return -EINVAL;
+
+3. The show handlers would currently catch the negative number and
+   refuse to perform the read.
+
+Replace kstrtou32_from_user() with kstrtos32_from_user() to parse the
+input, and check for negative values to explicitly prevent out-of-bounds
+memory accesses directly in iommu_mmio_write() and
+iommu_capability_write().
+
+Signed-off-by: Eder Zulian <ezulian@redhat.com>
+Fixes: 7a4ee419e8c1 ("iommu/amd: Add debugfs support to dump IOMMU MMIO registers")
+Cc: stable@vger.kernel.org
+Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/iommu/amd/debugfs.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/iommu/amd/debugfs.c b/drivers/iommu/amd/debugfs.c
+index 0584ca2f859d9..3909a1fb218e9 100644
+--- a/drivers/iommu/amd/debugfs.c
++++ b/drivers/iommu/amd/debugfs.c
+@@ -31,11 +31,12 @@ static ssize_t iommu_mmio_write(struct file *filp, const char __user *ubuf,
+       if (cnt > OFS_IN_SZ)
+               return -EINVAL;
+-      ret = kstrtou32_from_user(ubuf, cnt, 0, &dbg_mmio_offset);
++      ret = kstrtos32_from_user(ubuf, cnt, 0, &dbg_mmio_offset);
+       if (ret)
+               return ret;
+-      if (dbg_mmio_offset > iommu->mmio_phys_end - sizeof(u64))
++      if (dbg_mmio_offset < 0 || dbg_mmio_offset >
++                      iommu->mmio_phys_end - sizeof(u64))
+               return -EINVAL;
+       iommu->dbg_mmio_offset = dbg_mmio_offset;
+@@ -71,12 +72,12 @@ static ssize_t iommu_capability_write(struct file *filp, const char __user *ubuf
+       if (cnt > OFS_IN_SZ)
+               return -EINVAL;
+-      ret = kstrtou32_from_user(ubuf, cnt, 0, &dbg_cap_offset);
++      ret = kstrtos32_from_user(ubuf, cnt, 0, &dbg_cap_offset);
+       if (ret)
+               return ret;
+       /* Capability register at offset 0x14 is the last IOMMU capability register. */
+-      if (dbg_cap_offset > 0x14)
++      if (dbg_cap_offset < 0 || dbg_cap_offset > 0x14)
+               return -EINVAL;
+       iommu->dbg_cap_offset = dbg_cap_offset;
+-- 
+2.53.0
+
diff --git a/queue-6.18/series b/queue-6.18/series
new file mode 100644 (file)
index 0000000..9ec9740
--- /dev/null
@@ -0,0 +1,3 @@
+drm-xe-hdcp-add-null-check-for-media_gt-in-intel_hdc.patch
+iommu-amd-fix-illegal-cap-mmio-access-in-iommu-debug.patch
+iommu-amd-remove-latent-out-of-bounds-access-in-iomm.patch
diff --git a/queue-6.6/driver-core-generalize-driver_override-in-struct-dev.patch b/queue-6.6/driver-core-generalize-driver_override-in-struct-dev.patch
new file mode 100644 (file)
index 0000000..4bec723
--- /dev/null
@@ -0,0 +1,324 @@
+From 2fa22e22ad8668384cd35b8042028395606799a1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 14:01:59 +0000
+Subject: driver core: generalize driver_override in struct device
+
+From: Danilo Krummrich <dakr@kernel.org>
+
+[ Upstream commit cb3d1049f4ea77d5ad93f17d8ac1f2ed4da70501 ]
+
+Currently, there are 12 busses (including platform and PCI) that
+duplicate the driver_override logic for their individual devices.
+
+All of them seem to be prone to the bug described in [1].
+
+While this could be solved for every bus individually using a separate
+lock, solving this in the driver-core generically results in less (and
+cleaner) changes overall.
+
+Thus, move driver_override to struct device, provide corresponding
+accessors for busses and handle locking with a separate lock internally.
+
+In particular, add device_set_driver_override(),
+device_has_driver_override(), device_match_driver_override() and
+generalize the sysfs store() and show() callbacks via a driver_override
+feature flag in struct bus_type.
+
+Until all busses have migrated, keep driver_set_override() in place.
+
+Note that we can't use the device lock for the reasons described in [2].
+
+Link: https://bugzilla.kernel.org/show_bug.cgi?id=220789 [1]
+Link: https://lore.kernel.org/driver-core/DGRGTIRHA62X.3RY09D9SOK77P@kernel.org/ [2]
+Tested-by: Gui-Dong Han <hanguidong02@gmail.com>
+Co-developed-by: Gui-Dong Han <hanguidong02@gmail.com>
+Signed-off-by: Gui-Dong Han <hanguidong02@gmail.com>
+Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Link: https://patch.msgid.link/20260303115720.48783-2-dakr@kernel.org
+[ Use dev->bus instead of sp->bus for consistency; fix commit message to
+  refer to the struct bus_type's driver_override feature flag. - Danilo ]
+Signed-off-by: Danilo Krummrich <dakr@kernel.org>
+Stable-dep-of: 2b38efc05bf7 ("driver core: platform: use generic driver_override infrastructure")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: David Sauerwein <dssauerw@amazon.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/base/bus.c         | 43 ++++++++++++++++++++++++++-
+ drivers/base/core.c        |  2 ++
+ drivers/base/dd.c          | 60 ++++++++++++++++++++++++++++++++++++++
+ include/linux/device.h     | 54 ++++++++++++++++++++++++++++++++++
+ include/linux/device/bus.h |  4 +++
+ 5 files changed, 162 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/base/bus.c b/drivers/base/bus.c
+index b97e13a52c330..ae57df879bca2 100644
+--- a/drivers/base/bus.c
++++ b/drivers/base/bus.c
+@@ -463,6 +463,36 @@ int bus_for_each_drv(const struct bus_type *bus, struct device_driver *start,
+ }
+ EXPORT_SYMBOL_GPL(bus_for_each_drv);
++static ssize_t driver_override_store(struct device *dev,
++                                   struct device_attribute *attr,
++                                   const char *buf, size_t count)
++{
++      int ret;
++
++      ret = __device_set_driver_override(dev, buf, count);
++      if (ret)
++              return ret;
++
++      return count;
++}
++
++static ssize_t driver_override_show(struct device *dev,
++                                  struct device_attribute *attr, char *buf)
++{
++      guard(spinlock)(&dev->driver_override.lock);
++      return sysfs_emit(buf, "%s\n", dev->driver_override.name);
++}
++static DEVICE_ATTR_RW(driver_override);
++
++static struct attribute *driver_override_dev_attrs[] = {
++      &dev_attr_driver_override.attr,
++      NULL,
++};
++
++static const struct attribute_group driver_override_dev_group = {
++      .attrs = driver_override_dev_attrs,
++};
++
+ /**
+  * bus_add_device - add device to bus
+  * @dev: device being added
+@@ -496,9 +526,15 @@ int bus_add_device(struct device *dev)
+       if (error)
+               goto out_put;
++      if (dev->bus->driver_override) {
++              error = device_add_group(dev, &driver_override_dev_group);
++              if (error)
++                      goto out_groups;
++      }
++
+       error = sysfs_create_link(&sp->devices_kset->kobj, &dev->kobj, dev_name(dev));
+       if (error)
+-              goto out_groups;
++              goto out_override;
+       error = sysfs_create_link(&dev->kobj, &sp->subsys.kobj, "subsystem");
+       if (error)
+@@ -509,6 +545,9 @@ int bus_add_device(struct device *dev)
+ out_subsys:
+       sysfs_remove_link(&sp->devices_kset->kobj, dev_name(dev));
++out_override:
++      if (dev->bus->driver_override)
++              device_remove_group(dev, &driver_override_dev_group);
+ out_groups:
+       device_remove_groups(dev, sp->bus->dev_groups);
+ out_put:
+@@ -567,6 +606,8 @@ void bus_remove_device(struct device *dev)
+       sysfs_remove_link(&dev->kobj, "subsystem");
+       sysfs_remove_link(&sp->devices_kset->kobj, dev_name(dev));
++      if (dev->bus->driver_override)
++              device_remove_group(dev, &driver_override_dev_group);
+       device_remove_groups(dev, dev->bus->dev_groups);
+       if (klist_node_attached(&dev->p->knode_bus))
+               klist_del(&dev->p->knode_bus);
+diff --git a/drivers/base/core.c b/drivers/base/core.c
+index 3c172e6d3fe0d..5048849cb97a9 100644
+--- a/drivers/base/core.c
++++ b/drivers/base/core.c
+@@ -2505,6 +2505,7 @@ static void device_release(struct kobject *kobj)
+       devres_release_all(dev);
+       kfree(dev->dma_range_map);
++      kfree(dev->driver_override.name);
+       if (dev->release)
+               dev->release(dev);
+@@ -3153,6 +3154,7 @@ void device_initialize(struct device *dev)
+       kobject_init(&dev->kobj, &device_ktype);
+       INIT_LIST_HEAD(&dev->dma_pools);
+       mutex_init(&dev->mutex);
++      spin_lock_init(&dev->driver_override.lock);
+       lockdep_set_novalidate_class(&dev->mutex);
+       spin_lock_init(&dev->devres_lock);
+       INIT_LIST_HEAD(&dev->devres_head);
+diff --git a/drivers/base/dd.c b/drivers/base/dd.c
+index d371c3437dc6b..44f9d0a06d5b7 100644
+--- a/drivers/base/dd.c
++++ b/drivers/base/dd.c
+@@ -380,6 +380,66 @@ static void __exit deferred_probe_exit(void)
+ }
+ __exitcall(deferred_probe_exit);
++int __device_set_driver_override(struct device *dev, const char *s, size_t len)
++{
++      const char *new, *old;
++      char *cp;
++
++      if (!s)
++              return -EINVAL;
++
++      /*
++       * The stored value will be used in sysfs show callback (sysfs_emit()),
++       * which has a length limit of PAGE_SIZE and adds a trailing newline.
++       * Thus we can store one character less to avoid truncation during sysfs
++       * show.
++       */
++      if (len >= (PAGE_SIZE - 1))
++              return -EINVAL;
++
++      /*
++       * Compute the real length of the string in case userspace sends us a
++       * bunch of \0 characters like python likes to do.
++       */
++      len = strlen(s);
++
++      if (!len) {
++              /* Empty string passed - clear override */
++              spin_lock(&dev->driver_override.lock);
++              old = dev->driver_override.name;
++              dev->driver_override.name = NULL;
++              spin_unlock(&dev->driver_override.lock);
++              kfree(old);
++
++              return 0;
++      }
++
++      cp = strnchr(s, len, '\n');
++      if (cp)
++              len = cp - s;
++
++      new = kstrndup(s, len, GFP_KERNEL);
++      if (!new)
++              return -ENOMEM;
++
++      spin_lock(&dev->driver_override.lock);
++      old = dev->driver_override.name;
++      if (cp != s) {
++              dev->driver_override.name = new;
++              spin_unlock(&dev->driver_override.lock);
++      } else {
++              /* "\n" passed - clear override */
++              dev->driver_override.name = NULL;
++              spin_unlock(&dev->driver_override.lock);
++
++              kfree(new);
++      }
++      kfree(old);
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(__device_set_driver_override);
++
+ /**
+  * device_is_bound() - Check if device is bound to a driver
+  * @dev: device to check
+diff --git a/include/linux/device.h b/include/linux/device.h
+index 8fb9bd71fcd0b..2a9017eec15c2 100644
+--- a/include/linux/device.h
++++ b/include/linux/device.h
+@@ -643,6 +643,8 @@ enum struct_device_flags {
+  *            on.  This shrinks the "Board Support Packages" (BSPs) and
+  *            minimizes board-specific #ifdefs in drivers.
+  * @driver_data: Private pointer for driver specific info.
++ * @driver_override: Driver name to force a match.  Do not touch directly; use
++ *                 device_set_driver_override() instead.
+  * @links:    Links to suppliers and consumers of this device.
+  * @power:    For device power management.
+  *            See Documentation/driver-api/pm/devices.rst for details.
+@@ -735,6 +737,10 @@ struct device {
+                                          core doesn't touch it */
+       void            *driver_data;   /* Driver data, set and get with
+                                          dev_set_drvdata/dev_get_drvdata */
++      struct {
++              const char      *name;
++              spinlock_t      lock;
++      } driver_override;
+       struct mutex            mutex;  /* mutex to synchronize calls to
+                                        * its driver.
+                                        */
+@@ -882,6 +888,54 @@ struct device_link {
+ #define kobj_to_dev(__kobj)   container_of_const(__kobj, struct device, kobj)
++int __device_set_driver_override(struct device *dev, const char *s, size_t len);
++
++/**
++ * device_set_driver_override() - Helper to set or clear driver override.
++ * @dev: Device to change
++ * @s: NUL-terminated string, new driver name to force a match, pass empty
++ *     string to clear it ("" or "\n", where the latter is only for sysfs
++ *     interface).
++ *
++ * Helper to set or clear driver override of a device.
++ *
++ * Returns: 0 on success or a negative error code on failure.
++ */
++static inline int device_set_driver_override(struct device *dev, const char *s)
++{
++      return __device_set_driver_override(dev, s, s ? strlen(s) : 0);
++}
++
++/**
++ * device_has_driver_override() - Check if a driver override has been set.
++ * @dev: device to check
++ *
++ * Returns true if a driver override has been set for this device.
++ */
++static inline bool device_has_driver_override(struct device *dev)
++{
++      guard(spinlock)(&dev->driver_override.lock);
++      return !!dev->driver_override.name;
++}
++
++/**
++ * device_match_driver_override() - Match a driver against the device's driver_override.
++ * @dev: device to check
++ * @drv: driver to match against
++ *
++ * Returns > 0 if a driver override is set and matches the given driver, 0 if a
++ * driver override is set but does not match, or < 0 if a driver override is not
++ * set at all.
++ */
++static inline int device_match_driver_override(struct device *dev,
++                                             const struct device_driver *drv)
++{
++      guard(spinlock)(&dev->driver_override.lock);
++      if (dev->driver_override.name)
++              return !strcmp(dev->driver_override.name, drv->name);
++      return -1;
++}
++
+ /**
+  * device_iommu_mapped - Returns true when the device DMA is translated
+  *                     by an IOMMU
+diff --git a/include/linux/device/bus.h b/include/linux/device/bus.h
+index ae10c43227543..d43ce072d747e 100644
+--- a/include/linux/device/bus.h
++++ b/include/linux/device/bus.h
+@@ -65,6 +65,9 @@ struct fwnode_handle;
+  * @iommu_ops:  IOMMU specific operations for this bus, used to attach IOMMU
+  *              driver implementations to a bus and allow the driver to do
+  *              bus-specific setup
++ * @driver_override:  Set to true if this bus supports the driver_override
++ *                    mechanism, which allows userspace to force a specific
++ *                    driver to bind to a device via a sysfs attribute.
+  * @need_parent_lock: When probing or removing a device on this bus, the
+  *                    device core should lock the device's parent.
+  *
+@@ -106,6 +109,7 @@ struct bus_type {
+       const struct iommu_ops *iommu_ops;
++      bool driver_override;
+       bool need_parent_lock;
+ };
+-- 
+2.53.0
+
diff --git a/queue-6.6/driver-core-platform-use-generic-driver_override-inf.patch b/queue-6.6/driver-core-platform-use-generic-driver_override-inf.patch
new file mode 100644 (file)
index 0000000..a8259a8
--- /dev/null
@@ -0,0 +1,202 @@
+From 0112df6ef8d1c90e445e2aea0a9b29ab308d1924 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 May 2026 14:02:00 +0000
+Subject: driver core: platform: use generic driver_override infrastructure
+
+From: Danilo Krummrich <dakr@kernel.org>
+
+[ Upstream commit 2b38efc05bf7a8568ec74bfffea0f5cfa62bc01d ]
+
+When a driver is probed through __driver_attach(), the bus' match()
+callback is called without the device lock held, thus accessing the
+driver_override field without a lock, which can cause a UAF.
+
+Fix this by using the driver-core driver_override infrastructure taking
+care of proper locking internally.
+
+Note that calling match() from __driver_attach() without the device lock
+held is intentional. [1]
+
+Link: https://lore.kernel.org/driver-core/DGRGTIRHA62X.3RY09D9SOK77P@kernel.org/ [1]
+Reported-by: Gui-Dong Han <hanguidong02@gmail.com>
+Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220789
+Fixes: 3d713e0e382e ("driver core: platform: add device binding path 'driver_override'")
+Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Link: https://patch.msgid.link/20260303115720.48783-5-dakr@kernel.org
+Signed-off-by: Danilo Krummrich <dakr@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: David Sauerwein <dssauerw@amazon.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/base/platform.c         | 37 +++++----------------------------
+ drivers/bus/simple-pm-bus.c     |  4 ++--
+ drivers/clk/imx/clk-scu.c       |  3 +--
+ drivers/slimbus/qcom-ngd-ctrl.c |  6 ++----
+ include/linux/platform_device.h |  5 -----
+ sound/soc/samsung/i2s.c         |  6 +++---
+ 6 files changed, 13 insertions(+), 48 deletions(-)
+
+diff --git a/drivers/base/platform.c b/drivers/base/platform.c
+index 76bfcba250039..1cc9a876b3cde 100644
+--- a/drivers/base/platform.c
++++ b/drivers/base/platform.c
+@@ -561,7 +561,6 @@ static void platform_device_release(struct device *dev)
+       kfree(pa->pdev.dev.platform_data);
+       kfree(pa->pdev.mfd_cell);
+       kfree(pa->pdev.resource);
+-      kfree(pa->pdev.driver_override);
+       kfree(pa);
+ }
+@@ -1265,38 +1264,9 @@ static ssize_t numa_node_show(struct device *dev,
+ }
+ static DEVICE_ATTR_RO(numa_node);
+-static ssize_t driver_override_show(struct device *dev,
+-                                  struct device_attribute *attr, char *buf)
+-{
+-      struct platform_device *pdev = to_platform_device(dev);
+-      ssize_t len;
+-
+-      device_lock(dev);
+-      len = sysfs_emit(buf, "%s\n", pdev->driver_override);
+-      device_unlock(dev);
+-
+-      return len;
+-}
+-
+-static ssize_t driver_override_store(struct device *dev,
+-                                   struct device_attribute *attr,
+-                                   const char *buf, size_t count)
+-{
+-      struct platform_device *pdev = to_platform_device(dev);
+-      int ret;
+-
+-      ret = driver_set_override(dev, &pdev->driver_override, buf, count);
+-      if (ret)
+-              return ret;
+-
+-      return count;
+-}
+-static DEVICE_ATTR_RW(driver_override);
+-
+ static struct attribute *platform_dev_attrs[] = {
+       &dev_attr_modalias.attr,
+       &dev_attr_numa_node.attr,
+-      &dev_attr_driver_override.attr,
+       NULL,
+ };
+@@ -1336,10 +1306,12 @@ static int platform_match(struct device *dev, struct device_driver *drv)
+ {
+       struct platform_device *pdev = to_platform_device(dev);
+       struct platform_driver *pdrv = to_platform_driver(drv);
++      int ret;
+       /* When driver_override is set, only bind to the matching driver */
+-      if (pdev->driver_override)
+-              return !strcmp(pdev->driver_override, drv->name);
++      ret = device_match_driver_override(dev, drv);
++      if (ret >= 0)
++              return ret;
+       /* Attempt an OF style match first */
+       if (of_driver_match_device(dev, drv))
+@@ -1482,6 +1454,7 @@ static const struct dev_pm_ops platform_dev_pm_ops = {
+ struct bus_type platform_bus_type = {
+       .name           = "platform",
+       .dev_groups     = platform_dev_groups,
++      .driver_override = true,
+       .match          = platform_match,
+       .uevent         = platform_uevent,
+       .probe          = platform_probe,
+diff --git a/drivers/bus/simple-pm-bus.c b/drivers/bus/simple-pm-bus.c
+index aafcc481de91d..cb71774400d42 100644
+--- a/drivers/bus/simple-pm-bus.c
++++ b/drivers/bus/simple-pm-bus.c
+@@ -36,7 +36,7 @@ static int simple_pm_bus_probe(struct platform_device *pdev)
+        * that's not listed in simple_pm_bus_of_match. We don't want to do any
+        * of the simple-pm-bus tasks for these devices, so return early.
+        */
+-      if (pdev->driver_override)
++      if (device_has_driver_override(&pdev->dev))
+               return 0;
+       match = of_match_device(dev->driver->of_match_table, dev);
+@@ -78,7 +78,7 @@ static int simple_pm_bus_remove(struct platform_device *pdev)
+ {
+       const void *data = of_device_get_match_data(&pdev->dev);
+-      if (pdev->driver_override || data)
++      if (device_has_driver_override(&pdev->dev) || data)
+               return 0;
+       dev_dbg(&pdev->dev, "%s\n", __func__);
+diff --git a/drivers/clk/imx/clk-scu.c b/drivers/clk/imx/clk-scu.c
+index 564f549ec204f..3a68576cdae76 100644
+--- a/drivers/clk/imx/clk-scu.c
++++ b/drivers/clk/imx/clk-scu.c
+@@ -700,8 +700,7 @@ struct clk_hw *imx_clk_scu_alloc_dev(const char *name,
+               return ERR_PTR(ret);
+       }
+-      ret = driver_set_override(&pdev->dev, &pdev->driver_override,
+-                                "imx-scu-clk", strlen("imx-scu-clk"));
++      ret = device_set_driver_override(&pdev->dev, "imx-scu-clk");
+       if (ret) {
+               platform_device_put(pdev);
+               return ERR_PTR(ret);
+diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c
+index fe3a96577f31f..d0d289c49c8d1 100644
+--- a/drivers/slimbus/qcom-ngd-ctrl.c
++++ b/drivers/slimbus/qcom-ngd-ctrl.c
+@@ -1537,10 +1537,8 @@ static int of_qcom_slim_ngd_register(struct device *parent,
+               ngd->id = id;
+               ngd->pdev->dev.parent = parent;
+-              ret = driver_set_override(&ngd->pdev->dev,
+-                                        &ngd->pdev->driver_override,
+-                                        QCOM_SLIM_NGD_DRV_NAME,
+-                                        strlen(QCOM_SLIM_NGD_DRV_NAME));
++              ret = device_set_driver_override(&ngd->pdev->dev,
++                                               QCOM_SLIM_NGD_DRV_NAME);
+               if (ret) {
+                       platform_device_put(ngd->pdev);
+                       kfree(ngd);
+diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h
+index 7a41c72c19591..8828b0d275d79 100644
+--- a/include/linux/platform_device.h
++++ b/include/linux/platform_device.h
+@@ -31,11 +31,6 @@ struct platform_device {
+       struct resource *resource;
+       const struct platform_device_id *id_entry;
+-      /*
+-       * Driver name to force a match.  Do not set directly, because core
+-       * frees it.  Use driver_set_override() to set or clear it.
+-       */
+-      const char *driver_override;
+       /* MFD cell pointer */
+       struct mfd_cell *mfd_cell;
+diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
+index 3af48c9b5ab7b..925782e676730 100644
+--- a/sound/soc/samsung/i2s.c
++++ b/sound/soc/samsung/i2s.c
+@@ -1364,10 +1364,10 @@ static int i2s_create_secondary_device(struct samsung_i2s_priv *priv)
+       if (!pdev_sec)
+               return -ENOMEM;
+-      pdev_sec->driver_override = kstrdup("samsung-i2s", GFP_KERNEL);
+-      if (!pdev_sec->driver_override) {
++      ret = device_set_driver_override(&pdev_sec->dev, "samsung-i2s");
++      if (ret) {
+               platform_device_put(pdev_sec);
+-              return -ENOMEM;
++              return ret;
+       }
+       ret = platform_device_add(pdev_sec);
+-- 
+2.53.0
+
diff --git a/queue-6.6/mptcp-pm-add_addr-rtx-allow-id-0.patch b/queue-6.6/mptcp-pm-add_addr-rtx-allow-id-0.patch
new file mode 100644 (file)
index 0000000..e9e90fc
--- /dev/null
@@ -0,0 +1,46 @@
+From 15074b69b8ae9e602c4837924b2610beee3ec659 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 05:19:09 +0200
+Subject: mptcp: pm: ADD_ADDR rtx: allow ID 0
+
+From: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+
+commit 03f324f3f1f7619a47b9c91282cb12775ab0a2f1 upstream.
+
+ADD_ADDR can be sent for the ID 0, which corresponds to the local
+address and port linked to the initial subflow.
+
+Indeed, this address could be removed, and re-added later on, e.g. what
+is done in the "delete re-add signal" MPTCP Join selftests. So no reason
+to ignore it.
+
+Fixes: 00cfd77b9063 ("mptcp: retransmit ADD_ADDR when timeout")
+Cc: stable@vger.kernel.org
+Reviewed-by: Mat Martineau <martineau@kernel.org>
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Link: https://patch.msgid.link/20260505-net-mptcp-pm-fixes-7-1-rc3-v1-2-fca8091060a4@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+[ applied to net/mptcp/pm_netlink.c instead of upstream's pm_kernel.c ]
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mptcp/pm_netlink.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c
+index 4a5802126c8e4..23aef214f30d8 100644
+--- a/net/mptcp/pm_netlink.c
++++ b/net/mptcp/pm_netlink.c
+@@ -305,9 +305,6 @@ static void mptcp_pm_add_timer(struct timer_list *timer)
+       if (inet_sk_state_load(sk) == TCP_CLOSE)
+               return;
+-      if (!entry->addr.id)
+-              return;
+-
+       bh_lock_sock(sk);
+       if (sock_owned_by_user(sk)) {
+               /* Try again later. */
+-- 
+2.53.0
+
diff --git a/queue-6.6/mptcp-pm-add_addr-rtx-always-decrease-sk-refcount.patch b/queue-6.6/mptcp-pm-add_addr-rtx-always-decrease-sk-refcount.patch
new file mode 100644 (file)
index 0000000..6e10f6b
--- /dev/null
@@ -0,0 +1,62 @@
+From f7f5b43743cb6a0ee2fbd4fa88ed8cb7a15bb7d4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 05:19:10 +0200
+Subject: mptcp: pm: ADD_ADDR rtx: always decrease sk refcount
+
+From: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+
+commit 9634cb35af17019baec21ca648516ce376fa10e6 upstream.
+
+When an ADD_ADDR is retransmitted, the sk is held in sk_reset_timer().
+It should then be released in all cases at the end.
+
+Some (unlikely) checks were returning directly instead of calling
+sock_put() to decrease the refcount. Jump to a new 'exit' label to call
+__sock_put() (which will become sock_put() in the next commit) to fix
+this potential leak.
+
+While at it, drop the '!msk' check which cannot happen because it is
+never reset, and explicitly mark the remaining one as "unlikely".
+
+Fixes: 00cfd77b9063 ("mptcp: retransmit ADD_ADDR when timeout")
+Cc: stable@vger.kernel.org
+Reviewed-by: Mat Martineau <martineau@kernel.org>
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Link: https://patch.msgid.link/20260505-net-mptcp-pm-fixes-7-1-rc3-v1-4-fca8091060a4@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+[ applied to net/mptcp/pm_netlink.c instead of upstream's pm_kernel.c ]
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mptcp/pm_netlink.c | 8 +++-----
+ 1 file changed, 3 insertions(+), 5 deletions(-)
+
+diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c
+index 23aef214f30d8..d087d5fc3067d 100644
+--- a/net/mptcp/pm_netlink.c
++++ b/net/mptcp/pm_netlink.c
+@@ -299,11 +299,8 @@ static void mptcp_pm_add_timer(struct timer_list *timer)
+       pr_debug("msk=%p\n", msk);
+-      if (!msk)
+-              return;
+-
+-      if (inet_sk_state_load(sk) == TCP_CLOSE)
+-              return;
++      if (unlikely(inet_sk_state_load(sk) == TCP_CLOSE))
++              goto exit;
+       bh_lock_sock(sk);
+       if (sock_owned_by_user(sk)) {
+@@ -341,6 +338,7 @@ static void mptcp_pm_add_timer(struct timer_list *timer)
+ out:
+       bh_unlock_sock(sk);
++exit:
+       __sock_put(sk);
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.6/mptcp-pm-add_addr-rtx-free-sk-if-last.patch b/queue-6.6/mptcp-pm-add_addr-rtx-free-sk-if-last.patch
new file mode 100644 (file)
index 0000000..b2c490e
--- /dev/null
@@ -0,0 +1,127 @@
+From 64abb07cc1e185dc8de2427440ee79393353c666 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 05:19:11 +0200
+Subject: mptcp: pm: ADD_ADDR rtx: free sk if last
+
+From: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+
+commit b7b9a461569734d33d3259d58d2507adfac107ed upstream.
+
+When an ADD_ADDR is retransmitted, the sk is held in sk_reset_timer(),
+and released at the end.
+
+If at that moment, it was the last reference being held, the sk would
+not be freed. sock_put() should then be called instead of __sock_put().
+
+But that's not enough: if it is the last reference, sock_put() will call
+sk_free(), which will end up calling sk_stop_timer_sync() on the same
+timer, and waiting indefinitely to finish. So it is needed to mark that
+the timer is done at the end of the timer handler when it has not been
+rescheduled, not to call sk_stop_timer_sync() on "itself".
+
+Fixes: 00cfd77b9063 ("mptcp: retransmit ADD_ADDR when timeout")
+Cc: stable@vger.kernel.org
+Reviewed-by: Mat Martineau <martineau@kernel.org>
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Link: https://patch.msgid.link/20260505-net-mptcp-pm-fixes-7-1-rc3-v1-5-fca8091060a4@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+[ Applied to net/mptcp/pm_netlink.c instead of upstream's pm_kernel.c.
+  Also, there were conflicts, because commit 30549eebc4d8 ("mptcp: make
+  ADD_ADDR retransmission timeout adaptive") is not in this version and
+  changed the context. Also, other conflicts were due to newer patches
+  being backported with resolved conflicts before this one. ]
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mptcp/pm_netlink.c | 28 +++++++++++++++++-----------
+ 1 file changed, 17 insertions(+), 11 deletions(-)
+
+diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c
+index d087d5fc3067d..6e5570f97236a 100644
+--- a/net/mptcp/pm_netlink.c
++++ b/net/mptcp/pm_netlink.c
+@@ -27,6 +27,7 @@ struct mptcp_pm_add_entry {
+       struct list_head        list;
+       struct mptcp_addr_info  addr;
+       u8                      retrans_times;
++      bool                    timer_done;
+       struct timer_list       add_timer;
+       struct mptcp_sock       *sock;
+       struct rcu_head         rcu;
+@@ -295,22 +296,22 @@ static void mptcp_pm_add_timer(struct timer_list *timer)
+       struct mptcp_pm_add_entry *entry = from_timer(entry, timer, add_timer);
+       struct mptcp_sock *msk = entry->sock;
+       struct sock *sk = (struct sock *)msk;
+-      unsigned int timeout;
++      unsigned int timeout = 0;
+       pr_debug("msk=%p\n", msk);
++      bh_lock_sock(sk);
+       if (unlikely(inet_sk_state_load(sk) == TCP_CLOSE))
+-              goto exit;
++              goto out;
+-      bh_lock_sock(sk);
+       if (sock_owned_by_user(sk)) {
+               /* Try again later. */
+-              sk_reset_timer(sk, timer, jiffies + HZ / 20);
++              timeout = HZ / 20;
+               goto out;
+       }
+       if (mptcp_pm_should_add_signal_addr(msk)) {
+-              sk_reset_timer(sk, timer, jiffies + HZ);
++              timeout = HZ;
+               goto out;
+       }
+@@ -327,9 +328,8 @@ static void mptcp_pm_add_timer(struct timer_list *timer)
+               entry->retrans_times++;
+       }
+-      if (entry->retrans_times < ADD_ADDR_RETRANS_MAX)
+-              sk_reset_timer(sk, timer,
+-                             jiffies + timeout);
++      if (entry->retrans_times >= ADD_ADDR_RETRANS_MAX)
++              timeout = 0;
+       spin_unlock_bh(&msk->pm.lock);
+@@ -337,9 +337,13 @@ static void mptcp_pm_add_timer(struct timer_list *timer)
+               mptcp_pm_subflow_established(msk);
+ out:
++      if (timeout)
++              sk_reset_timer(sk, timer, jiffies + timeout);
++      else
++              /* if sock_put calls sk_free: avoid waiting for this timer */
++              entry->timer_done = true;
+       bh_unlock_sock(sk);
+-exit:
+-      __sock_put(sk);
++      sock_put(sk);
+ }
+ struct mptcp_pm_add_entry *
+@@ -403,6 +407,7 @@ bool mptcp_pm_alloc_anno_list(struct mptcp_sock *msk,
+       timer_setup(&add_entry->add_timer, mptcp_pm_add_timer, 0);
+ reset_timer:
++      add_entry->timer_done = false;
+       timeout = mptcp_get_add_addr_timeout(net);
+       if (timeout)
+               sk_reset_timer(sk, &add_entry->add_timer, jiffies + timeout);
+@@ -423,7 +428,8 @@ void mptcp_pm_free_anno_list(struct mptcp_sock *msk)
+       spin_unlock_bh(&msk->pm.lock);
+       list_for_each_entry_safe(entry, tmp, &free_list, list) {
+-              sk_stop_timer_sync(sk, &entry->add_timer);
++              if (!entry->timer_done)
++                      sk_stop_timer_sync(sk, &entry->add_timer);
+               kfree_rcu(entry, rcu);
+       }
+ }
+-- 
+2.53.0
+
diff --git a/queue-6.6/mptcp-sync-the-msk-sndbuf-at-accept-time.patch b/queue-6.6/mptcp-sync-the-msk-sndbuf-at-accept-time.patch
new file mode 100644 (file)
index 0000000..df720d5
--- /dev/null
@@ -0,0 +1,72 @@
+From 980948a7233c762028c98372b3935f6a0a3ff0af Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 05:19:08 +0200
+Subject: mptcp: sync the msk->sndbuf at accept() time
+
+From: Gang Yan <yangang@kylinos.cn>
+
+commit fcf04b14334641f4b0b8647824480935e9416d52 upstream.
+
+On passive MPTCP connections, the msk sndbuf is not updated correctly.
+
+The root cause is an order issue in the accept path:
+
+- tcp_check_req() -> subflow_syn_recv_sock() -> mptcp_sk_clone_init()
+  calls __mptcp_propagate_sndbuf() to copy the ssk sndbuf into msk
+
+- Later, tcp_child_process() -> tcp_init_transfer() ->
+  tcp_sndbuf_expand() grows the ssk sndbuf.
+
+So __mptcp_propagate_sndbuf() runs before the ssk sndbuf has been
+expanded and the msk ends up with a much smaller sndbuf than the
+subflow:
+
+  MPTCP: msk->sndbuf:20480, msk->first->sndbuf:2626560
+
+Fix this by moving the __mptcp_propagate_sndbuf() call from
+mptcp_sk_clone_init() -- the ssk sndbuf is not yet finalized there -- to
+__mptcp_propagate_sndbuf() at accept() time, when the ssk sndbuf has
+been fully expanded by tcp_sndbuf_expand().
+
+Fixes: 8005184fd1ca ("mptcp: refactor sndbuf auto-tuning")
+Cc: stable@vger.kernel.org
+Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/602
+Signed-off-by: Gang Yan <yangang@kylinos.cn>
+Acked-by: Paolo Abeni <pabeni@redhat.com>
+Reviewed-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Link: https://patch.msgid.link/20260420-net-mptcp-sync-sndbuf-accept-v1-1-e3523e3aeb44@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+[ No conflicts, but move __mptcp_propagate_sndbuf() above the for-loop
+  (mptcp_for_each_subflow()) present in this version, which will modify
+  'subflow' used by __mptcp_propagate_sndbuf() in this new patch. ]
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mptcp/protocol.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
+index aed6c04c7de67..ff1632d03a96d 100644
+--- a/net/mptcp/protocol.c
++++ b/net/mptcp/protocol.c
+@@ -3451,7 +3451,6 @@ struct sock *mptcp_sk_clone_init(const struct sock *sk,
+        * uses the correct data
+        */
+       mptcp_copy_inaddrs(nsk, ssk);
+-      __mptcp_propagate_sndbuf(nsk, ssk);
+       mptcp_rcv_space_init(msk, ssk);
+       msk->rcvq_space.time = mptcp_stamp();
+@@ -4064,6 +4063,8 @@ static int mptcp_stream_accept(struct socket *sock, struct socket *newsock,
+               msk = mptcp_sk(newsk);
+               msk->in_accept_queue = 0;
++              __mptcp_propagate_sndbuf(newsk, mptcp_subflow_tcp_sock(subflow));
++
+               /* set ssk->sk_socket of accept()ed flows to mptcp socket.
+                * This is needed so NOSPACE flag can be set from tcp stack.
+                */
+-- 
+2.53.0
+
diff --git a/queue-6.6/s390-debug-reject-zero-length-input-before-trimming-.patch b/queue-6.6/s390-debug-reject-zero-length-input-before-trimming-.patch
new file mode 100644 (file)
index 0000000..3c01a8c
--- /dev/null
@@ -0,0 +1,49 @@
+From b01ce81fcc381342ba9dbf349e045bdd9acce867 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 10:28:34 +0800
+Subject: s390/debug: Reject zero-length input before trimming a newline
+
+From: Pengpeng Hou <pengpeng@iscas.ac.cn>
+
+[ Upstream commit c366a7b5ed7564e41345c380285bd3f6cb98971b ]
+
+debug_get_user_string() copies the userspace buffer into a newly
+allocated NUL-terminated buffer and then unconditionally looks at
+buffer[user_len - 1] to strip a trailing newline.
+
+A zero-length write reaches this helper unchanged, so the newline trim
+reads before the start of the allocated buffer.
+
+Reject empty writes before accessing the last input byte.
+
+Fixes: 66a464dbc8e0 ("[PATCH] s390: debug feature changes")
+Cc: stable@vger.kernel.org
+Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
+Reviewed-by: Benjamin Block <bblock@linux.ibm.com>
+Reviewed-by: Vasily Gorbik <gor@linux.ibm.com>
+Tested-by: Vasily Gorbik <gor@linux.ibm.com>
+Link: https://lore.kernel.org/r/20260417073530.96002-1-pengpeng@iscas.ac.cn
+Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
+Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/s390/kernel/debug.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c
+index cbe209fe0df15..893c35aeb746a 100644
+--- a/arch/s390/kernel/debug.c
++++ b/arch/s390/kernel/debug.c
+@@ -1258,6 +1258,9 @@ static inline char *debug_get_user_string(const char __user *user_buf,
+ {
+       char *buffer;
++      if (!user_len)
++              return ERR_PTR(-EINVAL);
++
+       buffer = kmalloc(user_len + 1, GFP_KERNEL);
+       if (!buffer)
+               return ERR_PTR(-ENOMEM);
+-- 
+2.53.0
+
diff --git a/queue-6.6/series b/queue-6.6/series
new file mode 100644 (file)
index 0000000..fdd5fdb
--- /dev/null
@@ -0,0 +1,8 @@
+mptcp-sync-the-msk-sndbuf-at-accept-time.patch
+mptcp-pm-add_addr-rtx-allow-id-0.patch
+mptcp-pm-add_addr-rtx-always-decrease-sk-refcount.patch
+mptcp-pm-add_addr-rtx-free-sk-if-last.patch
+spi-spidev-fix-lock-inversion-between-spi_lock-and-b.patch
+driver-core-generalize-driver_override-in-struct-dev.patch
+driver-core-platform-use-generic-driver_override-inf.patch
+s390-debug-reject-zero-length-input-before-trimming-.patch
diff --git a/queue-6.6/spi-spidev-fix-lock-inversion-between-spi_lock-and-b.patch b/queue-6.6/spi-spidev-fix-lock-inversion-between-spi_lock-and-b.patch
new file mode 100644 (file)
index 0000000..35959b2
--- /dev/null
@@ -0,0 +1,220 @@
+From 2b8b474394ed878185b5f908ff92e8e6a6db7258 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 May 2026 14:10:51 +0800
+Subject: spi: spidev: fix lock inversion between spi_lock and buf_lock
+
+From: Fabian Godehardt <fg@emlix.com>
+
+[ Upstream commit 40534d19ed2afb880ecf202dab26a8e7a5808d16 ]
+
+The spidev driver previously used two mutexes, spi_lock and buf_lock,
+but acquired them in different orders depending on the code path:
+
+  write()/read(): buf_lock -> spi_lock
+  ioctl():       spi_lock -> buf_lock
+
+This AB-BA locking pattern triggers lockdep warnings and can
+cause real deadlocks:
+
+  WARNING: possible circular locking dependency detected
+  spidev_ioctl() -> mutex_lock(&spidev->buf_lock)
+  spidev_sync_write() -> mutex_lock(&spidev->spi_lock)
+  *** DEADLOCK ***
+
+The issue is reproducible with a simple userspace program that
+performs write() and SPI_IOC_WR_MAX_SPEED_HZ ioctl() calls from
+separate threads on the same spidev file descriptor.
+
+Fix this by simplifying the locking model and removing the lock
+inversion entirely. spidev_sync() no longer performs any locking,
+and all callers serialize access using spi_lock.
+
+buf_lock is removed since its functionality is fully covered by
+spi_lock, eliminating the possibility of lock ordering issues.
+
+This removes the lock inversion and prevents deadlocks without
+changing userspace ABI or behaviour.
+
+Signed-off-by: Fabian Godehardt <fg@emlix.com>
+Link: https://patch.msgid.link/20260211072616.489522-1-fg@emlix.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+[ Minor context conflict resolved. ]
+Signed-off-by: Wenshan Lan <jetlan9@163.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spidev.c | 63 ++++++++++++++++----------------------------
+ 1 file changed, 22 insertions(+), 41 deletions(-)
+
+diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
+index 16bb4fc3a4ba9..2024532dfc447 100644
+--- a/drivers/spi/spidev.c
++++ b/drivers/spi/spidev.c
+@@ -74,7 +74,6 @@ struct spidev_data {
+       struct list_head        device_entry;
+       /* TX/RX buffers are NULL unless this device is open (users > 0) */
+-      struct mutex            buf_lock;
+       unsigned                users;
+       u8                      *tx_buffer;
+       u8                      *rx_buffer;
+@@ -102,24 +101,6 @@ spidev_sync_unlocked(struct spi_device *spi, struct spi_message *message)
+       return status;
+ }
+-static ssize_t
+-spidev_sync(struct spidev_data *spidev, struct spi_message *message)
+-{
+-      ssize_t status;
+-      struct spi_device *spi;
+-
+-      mutex_lock(&spidev->spi_lock);
+-      spi = spidev->spi;
+-
+-      if (spi == NULL)
+-              status = -ESHUTDOWN;
+-      else
+-              status = spidev_sync_unlocked(spi, message);
+-
+-      mutex_unlock(&spidev->spi_lock);
+-      return status;
+-}
+-
+ static inline ssize_t
+ spidev_sync_write(struct spidev_data *spidev, size_t len)
+ {
+@@ -132,7 +113,8 @@ spidev_sync_write(struct spidev_data *spidev, size_t len)
+       spi_message_init(&m);
+       spi_message_add_tail(&t, &m);
+-      return spidev_sync(spidev, &m);
++
++      return spidev_sync_unlocked(spidev->spi, &m);
+ }
+ static inline ssize_t
+@@ -147,7 +129,8 @@ spidev_sync_read(struct spidev_data *spidev, size_t len)
+       spi_message_init(&m);
+       spi_message_add_tail(&t, &m);
+-      return spidev_sync(spidev, &m);
++
++      return spidev_sync_unlocked(spidev->spi, &m);
+ }
+ /*-------------------------------------------------------------------------*/
+@@ -157,7 +140,7 @@ static ssize_t
+ spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
+ {
+       struct spidev_data      *spidev;
+-      ssize_t                 status;
++      ssize_t                 status = -ESHUTDOWN;
+       /* chipselect only toggles at start or end of operation */
+       if (count > bufsiz)
+@@ -165,7 +148,11 @@ spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
+       spidev = filp->private_data;
+-      mutex_lock(&spidev->buf_lock);
++      mutex_lock(&spidev->spi_lock);
++
++      if (spidev->spi == NULL)
++              goto err_spi_removed;
++
+       status = spidev_sync_read(spidev, count);
+       if (status > 0) {
+               unsigned long   missing;
+@@ -176,7 +163,9 @@ spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
+               else
+                       status = status - missing;
+       }
+-      mutex_unlock(&spidev->buf_lock);
++
++err_spi_removed:
++      mutex_unlock(&spidev->spi_lock);
+       return status;
+ }
+@@ -187,7 +176,7 @@ spidev_write(struct file *filp, const char __user *buf,
+               size_t count, loff_t *f_pos)
+ {
+       struct spidev_data      *spidev;
+-      ssize_t                 status;
++      ssize_t                 status = -ESHUTDOWN;
+       unsigned long           missing;
+       /* chipselect only toggles at start or end of operation */
+@@ -196,13 +185,19 @@ spidev_write(struct file *filp, const char __user *buf,
+       spidev = filp->private_data;
+-      mutex_lock(&spidev->buf_lock);
++      mutex_lock(&spidev->spi_lock);
++
++      if (spidev->spi == NULL)
++              goto err_spi_removed;
++
+       missing = copy_from_user(spidev->tx_buffer, buf, count);
+       if (missing == 0)
+               status = spidev_sync_write(spidev, count);
+       else
+               status = -EFAULT;
+-      mutex_unlock(&spidev->buf_lock);
++
++err_spi_removed:
++      mutex_unlock(&spidev->spi_lock);
+       return status;
+ }
+@@ -376,14 +371,6 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+               return -ESHUTDOWN;
+       }
+-      /* use the buffer lock here for triple duty:
+-       *  - prevent I/O (from us) so calling spi_setup() is safe;
+-       *  - prevent concurrent SPI_IOC_WR_* from morphing
+-       *    data fields while SPI_IOC_RD_* reads them;
+-       *  - SPI_IOC_MESSAGE needs the buffer locked "normally".
+-       */
+-      mutex_lock(&spidev->buf_lock);
+-
+       switch (cmd) {
+       /* read requests */
+       case SPI_IOC_RD_MODE:
+@@ -516,7 +503,6 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+               break;
+       }
+-      mutex_unlock(&spidev->buf_lock);
+       spi_dev_put(spi);
+       mutex_unlock(&spidev->spi_lock);
+       return retval;
+@@ -547,9 +533,6 @@ spidev_compat_ioc_message(struct file *filp, unsigned int cmd,
+               return -ESHUTDOWN;
+       }
+-      /* SPI_IOC_MESSAGE needs the buffer locked "normally" */
+-      mutex_lock(&spidev->buf_lock);
+-
+       /* Check message and copy into scratch area */
+       ioc = spidev_get_ioc_message(cmd, u_ioc, &n_ioc);
+       if (IS_ERR(ioc)) {
+@@ -570,7 +553,6 @@ spidev_compat_ioc_message(struct file *filp, unsigned int cmd,
+       kfree(ioc);
+ done:
+-      mutex_unlock(&spidev->buf_lock);
+       spi_dev_put(spi);
+       mutex_unlock(&spidev->spi_lock);
+       return retval;
+@@ -795,7 +777,6 @@ static int spidev_probe(struct spi_device *spi)
+       /* Initialize the driver data */
+       spidev->spi = spi;
+       mutex_init(&spidev->spi_lock);
+-      mutex_init(&spidev->buf_lock);
+       INIT_LIST_HEAD(&spidev->device_entry);
+-- 
+2.53.0
+
diff --git a/queue-7.0/iommu-amd-fix-illegal-cap-mmio-access-in-iommu-debug.patch b/queue-7.0/iommu-amd-fix-illegal-cap-mmio-access-in-iommu-debug.patch
new file mode 100644 (file)
index 0000000..7ece267
--- /dev/null
@@ -0,0 +1,143 @@
+From 74bfa9059f07c553d9a534dedc5baeafe68e8573 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 19 Mar 2026 15:37:54 +0800
+Subject: iommu/amd: Fix illegal cap/mmio access in IOMMU debugfs
+
+From: Guanghui Feng <guanghuifeng@linux.alibaba.com>
+
+[ Upstream commit 0e59645683b7b6fa20eceb21a6f420e4f7412943 ]
+
+In the current AMD IOMMU debugfs, when multiple processes simultaneously
+access the IOMMU mmio/cap registers using the IOMMU debugfs, illegal
+access issues can occur in the following execution flow:
+
+1. CPU1: Sets a valid access address using iommu_mmio/capability_write,
+and verifies the access address's validity in iommu_mmio/capability_show
+
+2. CPU2: Sets an invalid address using iommu_mmio/capability_write
+
+3. CPU1: accesses the IOMMU mmio/cap registers based on the invalid
+address, resulting in an illegal access.
+
+This patch modifies the execution process to first verify the address's
+validity and then access it based on the same address, ensuring
+correctness and robustness.
+
+Signed-off-by: Guanghui Feng <guanghuifeng@linux.alibaba.com>
+Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
+Stable-dep-of: 8dfd3d8d7443 ("iommu/amd: Remove latent out-of-bounds access in IOMMU debugfs")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/iommu/amd/debugfs.c | 42 +++++++++++++++++--------------------
+ 1 file changed, 19 insertions(+), 23 deletions(-)
+
+diff --git a/drivers/iommu/amd/debugfs.c b/drivers/iommu/amd/debugfs.c
+index 20b04996441d6..0584ca2f859d9 100644
+--- a/drivers/iommu/amd/debugfs.c
++++ b/drivers/iommu/amd/debugfs.c
+@@ -26,22 +26,19 @@ static ssize_t iommu_mmio_write(struct file *filp, const char __user *ubuf,
+ {
+       struct seq_file *m = filp->private_data;
+       struct amd_iommu *iommu = m->private;
+-      int ret;
+-
+-      iommu->dbg_mmio_offset = -1;
++      int ret, dbg_mmio_offset = iommu->dbg_mmio_offset = -1;
+       if (cnt > OFS_IN_SZ)
+               return -EINVAL;
+-      ret = kstrtou32_from_user(ubuf, cnt, 0, &iommu->dbg_mmio_offset);
++      ret = kstrtou32_from_user(ubuf, cnt, 0, &dbg_mmio_offset);
+       if (ret)
+               return ret;
+-      if (iommu->dbg_mmio_offset > iommu->mmio_phys_end - sizeof(u64)) {
+-              iommu->dbg_mmio_offset = -1;
+-              return  -EINVAL;
+-      }
++      if (dbg_mmio_offset > iommu->mmio_phys_end - sizeof(u64))
++              return -EINVAL;
++      iommu->dbg_mmio_offset = dbg_mmio_offset;
+       return cnt;
+ }
+@@ -49,14 +46,16 @@ static int iommu_mmio_show(struct seq_file *m, void *unused)
+ {
+       struct amd_iommu *iommu = m->private;
+       u64 value;
++      int dbg_mmio_offset = iommu->dbg_mmio_offset;
+-      if (iommu->dbg_mmio_offset < 0) {
++      if (dbg_mmio_offset < 0 || dbg_mmio_offset >
++                      iommu->mmio_phys_end - sizeof(u64)) {
+               seq_puts(m, "Please provide mmio register's offset\n");
+               return 0;
+       }
+-      value = readq(iommu->mmio_base + iommu->dbg_mmio_offset);
+-      seq_printf(m, "Offset:0x%x Value:0x%016llx\n", iommu->dbg_mmio_offset, value);
++      value = readq(iommu->mmio_base + dbg_mmio_offset);
++      seq_printf(m, "Offset:0x%x Value:0x%016llx\n", dbg_mmio_offset, value);
+       return 0;
+ }
+@@ -67,23 +66,20 @@ static ssize_t iommu_capability_write(struct file *filp, const char __user *ubuf
+ {
+       struct seq_file *m = filp->private_data;
+       struct amd_iommu *iommu = m->private;
+-      int ret;
+-
+-      iommu->dbg_cap_offset = -1;
++      int ret, dbg_cap_offset = iommu->dbg_cap_offset = -1;
+       if (cnt > OFS_IN_SZ)
+               return -EINVAL;
+-      ret = kstrtou32_from_user(ubuf, cnt, 0, &iommu->dbg_cap_offset);
++      ret = kstrtou32_from_user(ubuf, cnt, 0, &dbg_cap_offset);
+       if (ret)
+               return ret;
+       /* Capability register at offset 0x14 is the last IOMMU capability register. */
+-      if (iommu->dbg_cap_offset > 0x14) {
+-              iommu->dbg_cap_offset = -1;
++      if (dbg_cap_offset > 0x14)
+               return -EINVAL;
+-      }
++      iommu->dbg_cap_offset = dbg_cap_offset;
+       return cnt;
+ }
+@@ -91,21 +87,21 @@ static int iommu_capability_show(struct seq_file *m, void *unused)
+ {
+       struct amd_iommu *iommu = m->private;
+       u32 value;
+-      int err;
++      int err, dbg_cap_offset = iommu->dbg_cap_offset;
+-      if (iommu->dbg_cap_offset < 0) {
++      if (dbg_cap_offset < 0 || dbg_cap_offset > 0x14) {
+               seq_puts(m, "Please provide capability register's offset in the range [0x00 - 0x14]\n");
+               return 0;
+       }
+-      err = pci_read_config_dword(iommu->dev, iommu->cap_ptr + iommu->dbg_cap_offset, &value);
++      err = pci_read_config_dword(iommu->dev, iommu->cap_ptr + dbg_cap_offset, &value);
+       if (err) {
+               seq_printf(m, "Not able to read capability register at 0x%x\n",
+-                         iommu->dbg_cap_offset);
++                         dbg_cap_offset);
+               return 0;
+       }
+-      seq_printf(m, "Offset:0x%x Value:0x%08x\n", iommu->dbg_cap_offset, value);
++      seq_printf(m, "Offset:0x%x Value:0x%08x\n", dbg_cap_offset, value);
+       return 0;
+ }
+-- 
+2.53.0
+
diff --git a/queue-7.0/iommu-amd-remove-latent-out-of-bounds-access-in-iomm.patch b/queue-7.0/iommu-amd-remove-latent-out-of-bounds-access-in-iomm.patch
new file mode 100644 (file)
index 0000000..21da8e1
--- /dev/null
@@ -0,0 +1,83 @@
+From 5031bd461b1741d62806c7eefb7db628330ad6fd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 10 Apr 2026 14:55:50 +0200
+Subject: iommu/amd: Remove latent out-of-bounds access in IOMMU debugfs
+
+From: Eder Zulian <ezulian@redhat.com>
+
+[ Upstream commit 8dfd3d8d74435344ee8dc9237596959c8b2a6cbe ]
+
+In iommu_mmio_write() and iommu_capability_write(), the variables
+dbg_mmio_offset and dbg_cap_offset are declared as int. However, they
+are populated using kstrtou32_from_user(). If a user provides a
+sufficiently large value, it can become a negative integer.
+
+Prior to this patch, the AMD IOMMU debugfs implementation was already
+protected by different mechanisms.
+
+1. #define OFS_IN_SZ 8 ensures the user string <= 8 bytes, so
+   e.g. 0xffffffff isn't a valid input.
+
+  if (cnt > OFS_IN_SZ)
+     return -EINVAL;
+
+2. Implicit type promotion in iommu_mmio_write(), dbg_mmio_offset is int
+   and iommu->mmio_phys_end is u64
+
+  if (dbg_mmio_offset > iommu->mmio_phys_end - sizeof(u64))
+      return -EINVAL;
+
+3. The show handlers would currently catch the negative number and
+   refuse to perform the read.
+
+Replace kstrtou32_from_user() with kstrtos32_from_user() to parse the
+input, and check for negative values to explicitly prevent out-of-bounds
+memory accesses directly in iommu_mmio_write() and
+iommu_capability_write().
+
+Signed-off-by: Eder Zulian <ezulian@redhat.com>
+Fixes: 7a4ee419e8c1 ("iommu/amd: Add debugfs support to dump IOMMU MMIO registers")
+Cc: stable@vger.kernel.org
+Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/iommu/amd/debugfs.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/iommu/amd/debugfs.c b/drivers/iommu/amd/debugfs.c
+index 0584ca2f859d9..3909a1fb218e9 100644
+--- a/drivers/iommu/amd/debugfs.c
++++ b/drivers/iommu/amd/debugfs.c
+@@ -31,11 +31,12 @@ static ssize_t iommu_mmio_write(struct file *filp, const char __user *ubuf,
+       if (cnt > OFS_IN_SZ)
+               return -EINVAL;
+-      ret = kstrtou32_from_user(ubuf, cnt, 0, &dbg_mmio_offset);
++      ret = kstrtos32_from_user(ubuf, cnt, 0, &dbg_mmio_offset);
+       if (ret)
+               return ret;
+-      if (dbg_mmio_offset > iommu->mmio_phys_end - sizeof(u64))
++      if (dbg_mmio_offset < 0 || dbg_mmio_offset >
++                      iommu->mmio_phys_end - sizeof(u64))
+               return -EINVAL;
+       iommu->dbg_mmio_offset = dbg_mmio_offset;
+@@ -71,12 +72,12 @@ static ssize_t iommu_capability_write(struct file *filp, const char __user *ubuf
+       if (cnt > OFS_IN_SZ)
+               return -EINVAL;
+-      ret = kstrtou32_from_user(ubuf, cnt, 0, &dbg_cap_offset);
++      ret = kstrtos32_from_user(ubuf, cnt, 0, &dbg_cap_offset);
+       if (ret)
+               return ret;
+       /* Capability register at offset 0x14 is the last IOMMU capability register. */
+-      if (dbg_cap_offset > 0x14)
++      if (dbg_cap_offset < 0 || dbg_cap_offset > 0x14)
+               return -EINVAL;
+       iommu->dbg_cap_offset = dbg_cap_offset;
+-- 
+2.53.0
+
diff --git a/queue-7.0/series b/queue-7.0/series
new file mode 100644 (file)
index 0000000..d7baa45
--- /dev/null
@@ -0,0 +1,2 @@
+iommu-amd-fix-illegal-cap-mmio-access-in-iommu-debug.patch
+iommu-amd-remove-latent-out-of-bounds-access-in-iomm.patch