From: Sasha Levin Date: Sun, 24 May 2026 11:59:12 +0000 (-0400) Subject: Fixes for all trees X-Git-Tag: v5.10.258~63 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d2fc471fa3ae30fb16e92ac9d5c9768c443d39da;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for all trees Signed-off-by: Sasha Levin --- 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 index 0000000000..4c64725324 --- /dev/null +++ b/queue-5.10/s390-debug-reject-zero-length-input-before-trimming-.patch @@ -0,0 +1,49 @@ +From 09cc7451932b608d2e1ca3da99956af6f9ef6015 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 10:28:49 +0800 +Subject: s390/debug: Reject zero-length input before trimming a newline + +From: Pengpeng Hou + +[ 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 +Reviewed-by: Benjamin Block +Reviewed-by: Vasily Gorbik +Tested-by: Vasily Gorbik +Link: https://lore.kernel.org/r/20260417073530.96002-1-pengpeng@iscas.ac.cn +Signed-off-by: Vasily Gorbik +Signed-off-by: Alexander Gordeev +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..0bf51f3319 --- /dev/null +++ b/queue-5.10/selftests-lib.mk-also-install-config-and-settings.patch @@ -0,0 +1,36 @@ +From cde6bc671b5ec15da7887b36aa6a15c3d8d98dd9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 26 May 2021 20:17:54 -0700 +Subject: selftests: lib.mk: Also install "config" and "settings" + +From: Kees Cook + +[ 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 +Signed-off-by: Shuah Khan +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-5.10/series b/queue-5.10/series index 0dc6bd55ba..18d017436b 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -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 index 0000000000..cc4c02f365 --- /dev/null +++ b/queue-5.15/io_uring-prevent-opcode-speculation.patch @@ -0,0 +1,41 @@ +From a0f4558747dd5218b3f0de44d5f76a880bd0a469 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 14:28:33 +0800 +Subject: io_uring: prevent opcode speculation + +From: Pavel Begunkov + +[ 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 +Reviewed-by: Li Zetao +Link: https://lore.kernel.org/r/7eddbf31c8ca0a3947f8ed98271acc2b4349c016.1739568408.git.asml.silence@gmail.com +Signed-off-by: Jens Axboe +[ Use req->opcode instead of opcode here. ] +Signed-off-by: Robert Garcia +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..e026a7a830 --- /dev/null +++ b/queue-5.15/s390-debug-reject-zero-length-input-before-trimming-.patch @@ -0,0 +1,49 @@ +From 67bbc6674d5ddb9e7f2fc066c4d9fadf547064e6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 10:28:44 +0800 +Subject: s390/debug: Reject zero-length input before trimming a newline + +From: Pengpeng Hou + +[ 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 +Reviewed-by: Benjamin Block +Reviewed-by: Vasily Gorbik +Tested-by: Vasily Gorbik +Link: https://lore.kernel.org/r/20260417073530.96002-1-pengpeng@iscas.ac.cn +Signed-off-by: Vasily Gorbik +Signed-off-by: Alexander Gordeev +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-5.15/series b/queue-5.15/series index 91c8dd1493..fe706386e2 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -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 index 0000000000..bf88da2034 --- /dev/null +++ b/queue-6.1/io_uring-prevent-opcode-speculation.patch @@ -0,0 +1,40 @@ +From 63a69ee23ce4980618c5304dbdcbf10bf82a7f1a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 13:49:19 +0800 +Subject: io_uring: prevent opcode speculation + +From: Pavel Begunkov + +[ 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 +Reviewed-by: Li Zetao +Link: https://lore.kernel.org/r/7eddbf31c8ca0a3947f8ed98271acc2b4349c016.1739568408.git.asml.silence@gmail.com +Signed-off-by: Jens Axboe +Signed-off-by: Robert Garcia +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..64374dc719 --- /dev/null +++ b/queue-6.1/s390-debug-reject-zero-length-input-before-trimming-.patch @@ -0,0 +1,49 @@ +From 6855bc14b0b41855665bb9062c8c55917450ac26 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 10:28:39 +0800 +Subject: s390/debug: Reject zero-length input before trimming a newline + +From: Pengpeng Hou + +[ 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 +Reviewed-by: Benjamin Block +Reviewed-by: Vasily Gorbik +Tested-by: Vasily Gorbik +Link: https://lore.kernel.org/r/20260417073530.96002-1-pengpeng@iscas.ac.cn +Signed-off-by: Vasily Gorbik +Signed-off-by: Alexander Gordeev +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.1/series b/queue-6.1/series index 88776c115d..e2ffbe3cbd 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -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 index 0000000000..90f066e78c --- /dev/null +++ b/queue-6.12/drm-xe-hdcp-add-null-check-for-media_gt-in-intel_hdc.patch @@ -0,0 +1,70 @@ +From 7b88280e0ecc6ff061528c7431048fd3e8b958b0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +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 +>->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: " tags. (Matt) + +Fixes: 4af50beb4e0f ("drm/xe: Use gsc_proxy_init_done to check proxy status") +Cc: # v6.10+ +Reviewed-by: Matt Roper +Reviewed-by: Shuicheng Lin +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 +(cherry picked from commit bfaf87e84ca3ca3f6e275f9ae56da47a8b55ffd1) +Signed-off-by: Matthew Brost +Signed-off-by: Gustavo Sousa +Signed-off-by: Sasha Levin +--- + 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 = >->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 = >->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 index 0000000000..7e619b3172 --- /dev/null +++ b/queue-6.12/ksmbd-validate-owner-of-durable-handle-on-reconnect.patch @@ -0,0 +1,339 @@ +From 8b7f7f2e71f479151b87f7729ff68b0b9980597e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 22 May 2026 15:25:12 +0800 +Subject: ksmbd: validate owner of durable handle on reconnect + +From: Namjae Jeon + +[ 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 +Reported-by: Navaneeth K +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +[ Minor context conflict resolved. ] +Signed-off-by: Alva Lan +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..fcf5c877cf --- /dev/null +++ b/queue-6.12/mptcp-pm-add_addr-rtx-allow-id-0.patch @@ -0,0 +1,46 @@ +From cdadf9d85c986650e37a2c7a7d17c343677513aa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 05:08:48 +0200 +Subject: mptcp: pm: ADD_ADDR rtx: allow ID 0 + +From: Matthieu Baerts (NGI0) + +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 +Signed-off-by: Matthieu Baerts (NGI0) +Link: https://patch.msgid.link/20260505-net-mptcp-pm-fixes-7-1-rc3-v1-2-fca8091060a4@kernel.org +Signed-off-by: Jakub Kicinski +[ applied to net/mptcp/pm_netlink.c instead of upstream's pm_kernel.c ] +Signed-off-by: Matthieu Baerts (NGI0) +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..1480ff041b --- /dev/null +++ b/queue-6.12/mptcp-pm-add_addr-rtx-always-decrease-sk-refcount.patch @@ -0,0 +1,62 @@ +From dd1e3ca71e8ed4d5b7e3ca41f25d4da15b8e03df Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 05:08:49 +0200 +Subject: mptcp: pm: ADD_ADDR rtx: always decrease sk refcount + +From: Matthieu Baerts (NGI0) + +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 +Signed-off-by: Matthieu Baerts (NGI0) +Link: https://patch.msgid.link/20260505-net-mptcp-pm-fixes-7-1-rc3-v1-4-fca8091060a4@kernel.org +Signed-off-by: Jakub Kicinski +[ applied to net/mptcp/pm_netlink.c instead of upstream's pm_kernel.c ] +Signed-off-by: Matthieu Baerts (NGI0) +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..d70b0b4912 --- /dev/null +++ b/queue-6.12/mptcp-pm-add_addr-rtx-free-sk-if-last.patch @@ -0,0 +1,127 @@ +From 4c2a4761538bc2d0cb491cc4d417bed5f2065790 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 05:08:50 +0200 +Subject: mptcp: pm: ADD_ADDR rtx: free sk if last + +From: Matthieu Baerts (NGI0) + +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 +Signed-off-by: Matthieu Baerts (NGI0) +Link: https://patch.msgid.link/20260505-net-mptcp-pm-fixes-7-1-rc3-v1-5-fca8091060a4@kernel.org +Signed-off-by: Jakub Kicinski +[ 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) +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..341e2dfa01 --- /dev/null +++ b/queue-6.12/mptcp-sync-the-msk-sndbuf-at-accept-time.patch @@ -0,0 +1,72 @@ +From 480cb287ee0fc8988f79ff97c57b801c8fc9fb40 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 05:08:47 +0200 +Subject: mptcp: sync the msk->sndbuf at accept() time + +From: Gang Yan + +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 +Acked-by: Paolo Abeni +Reviewed-by: Matthieu Baerts (NGI0) +Signed-off-by: Matthieu Baerts (NGI0) +Link: https://patch.msgid.link/20260420-net-mptcp-sync-sndbuf-accept-v1-1-e3523e3aeb44@kernel.org +Signed-off-by: Paolo Abeni +[ 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) +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..1e97a77983 --- /dev/null +++ b/queue-6.12/s390-debug-reject-zero-length-input-before-trimming-.patch @@ -0,0 +1,49 @@ +From a9982a19d49df8c9a2e0d654fb5e0e53f94cda44 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 10:28:29 +0800 +Subject: s390/debug: Reject zero-length input before trimming a newline + +From: Pengpeng Hou + +[ 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 +Reviewed-by: Benjamin Block +Reviewed-by: Vasily Gorbik +Tested-by: Vasily Gorbik +Link: https://lore.kernel.org/r/20260417073530.96002-1-pengpeng@iscas.ac.cn +Signed-off-by: Vasily Gorbik +Signed-off-by: Alexander Gordeev +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..a404786c10 --- /dev/null +++ b/queue-6.12/series @@ -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 index 0000000000..4f1ebcc5c4 --- /dev/null +++ b/queue-6.18/drm-xe-hdcp-add-null-check-for-media_gt-in-intel_hdc.patch @@ -0,0 +1,70 @@ +From 40136dd811ff5838af10483a8b38b16688d2a90b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +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 +>->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: " tags. (Matt) + +Fixes: 4af50beb4e0f ("drm/xe: Use gsc_proxy_init_done to check proxy status") +Cc: # v6.10+ +Reviewed-by: Matt Roper +Reviewed-by: Shuicheng Lin +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 +(cherry picked from commit bfaf87e84ca3ca3f6e275f9ae56da47a8b55ffd1) +Signed-off-by: Matthew Brost +Signed-off-by: Sasha Levin +--- + 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 = >->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 = >->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 index 0000000000..9201a10c0f --- /dev/null +++ b/queue-6.18/iommu-amd-fix-illegal-cap-mmio-access-in-iommu-debug.patch @@ -0,0 +1,143 @@ +From fddfcb84e88aa2811927313beab1a8e996714169 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 19 Mar 2026 15:37:54 +0800 +Subject: iommu/amd: Fix illegal cap/mmio access in IOMMU debugfs + +From: Guanghui Feng + +[ 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 +Signed-off-by: Joerg Roedel +Stable-dep-of: 8dfd3d8d7443 ("iommu/amd: Remove latent out-of-bounds access in IOMMU debugfs") +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..779d3cee7b --- /dev/null +++ b/queue-6.18/iommu-amd-remove-latent-out-of-bounds-access-in-iomm.patch @@ -0,0 +1,83 @@ +From f243339518fa09ba6d6a8aa8807901dbb6e15db3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 10 Apr 2026 14:55:50 +0200 +Subject: iommu/amd: Remove latent out-of-bounds access in IOMMU debugfs + +From: Eder Zulian + +[ 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 +Fixes: 7a4ee419e8c1 ("iommu/amd: Add debugfs support to dump IOMMU MMIO registers") +Cc: stable@vger.kernel.org +Signed-off-by: Joerg Roedel +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..9ec974050c --- /dev/null +++ b/queue-6.18/series @@ -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 index 0000000000..4bec723c01 --- /dev/null +++ b/queue-6.6/driver-core-generalize-driver_override-in-struct-dev.patch @@ -0,0 +1,324 @@ +From 2fa22e22ad8668384cd35b8042028395606799a1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 14:01:59 +0000 +Subject: driver core: generalize driver_override in struct device + +From: Danilo Krummrich + +[ 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 +Co-developed-by: Gui-Dong Han +Signed-off-by: Gui-Dong Han +Reviewed-by: Greg Kroah-Hartman +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 +Stable-dep-of: 2b38efc05bf7 ("driver core: platform: use generic driver_override infrastructure") +Signed-off-by: Sasha Levin +Signed-off-by: David Sauerwein +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..a8259a86b0 --- /dev/null +++ b/queue-6.6/driver-core-platform-use-generic-driver_override-inf.patch @@ -0,0 +1,202 @@ +From 0112df6ef8d1c90e445e2aea0a9b29ab308d1924 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 14:02:00 +0000 +Subject: driver core: platform: use generic driver_override infrastructure + +From: Danilo Krummrich + +[ 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 +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 +Link: https://patch.msgid.link/20260303115720.48783-5-dakr@kernel.org +Signed-off-by: Danilo Krummrich +Signed-off-by: Sasha Levin +Signed-off-by: David Sauerwein +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..e9e90fc00c --- /dev/null +++ b/queue-6.6/mptcp-pm-add_addr-rtx-allow-id-0.patch @@ -0,0 +1,46 @@ +From 15074b69b8ae9e602c4837924b2610beee3ec659 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 05:19:09 +0200 +Subject: mptcp: pm: ADD_ADDR rtx: allow ID 0 + +From: Matthieu Baerts (NGI0) + +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 +Signed-off-by: Matthieu Baerts (NGI0) +Link: https://patch.msgid.link/20260505-net-mptcp-pm-fixes-7-1-rc3-v1-2-fca8091060a4@kernel.org +Signed-off-by: Jakub Kicinski +[ applied to net/mptcp/pm_netlink.c instead of upstream's pm_kernel.c ] +Signed-off-by: Matthieu Baerts (NGI0) +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..6e10f6b59d --- /dev/null +++ b/queue-6.6/mptcp-pm-add_addr-rtx-always-decrease-sk-refcount.patch @@ -0,0 +1,62 @@ +From f7f5b43743cb6a0ee2fbd4fa88ed8cb7a15bb7d4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 05:19:10 +0200 +Subject: mptcp: pm: ADD_ADDR rtx: always decrease sk refcount + +From: Matthieu Baerts (NGI0) + +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 +Signed-off-by: Matthieu Baerts (NGI0) +Link: https://patch.msgid.link/20260505-net-mptcp-pm-fixes-7-1-rc3-v1-4-fca8091060a4@kernel.org +Signed-off-by: Jakub Kicinski +[ applied to net/mptcp/pm_netlink.c instead of upstream's pm_kernel.c ] +Signed-off-by: Matthieu Baerts (NGI0) +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..b2c490e6d3 --- /dev/null +++ b/queue-6.6/mptcp-pm-add_addr-rtx-free-sk-if-last.patch @@ -0,0 +1,127 @@ +From 64abb07cc1e185dc8de2427440ee79393353c666 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 05:19:11 +0200 +Subject: mptcp: pm: ADD_ADDR rtx: free sk if last + +From: Matthieu Baerts (NGI0) + +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 +Signed-off-by: Matthieu Baerts (NGI0) +Link: https://patch.msgid.link/20260505-net-mptcp-pm-fixes-7-1-rc3-v1-5-fca8091060a4@kernel.org +Signed-off-by: Jakub Kicinski +[ 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) +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..df720d5506 --- /dev/null +++ b/queue-6.6/mptcp-sync-the-msk-sndbuf-at-accept-time.patch @@ -0,0 +1,72 @@ +From 980948a7233c762028c98372b3935f6a0a3ff0af Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 05:19:08 +0200 +Subject: mptcp: sync the msk->sndbuf at accept() time + +From: Gang Yan + +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 +Acked-by: Paolo Abeni +Reviewed-by: Matthieu Baerts (NGI0) +Signed-off-by: Matthieu Baerts (NGI0) +Link: https://patch.msgid.link/20260420-net-mptcp-sync-sndbuf-accept-v1-1-e3523e3aeb44@kernel.org +Signed-off-by: Paolo Abeni +[ 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) +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..3c01a8c9e6 --- /dev/null +++ b/queue-6.6/s390-debug-reject-zero-length-input-before-trimming-.patch @@ -0,0 +1,49 @@ +From b01ce81fcc381342ba9dbf349e045bdd9acce867 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 10:28:34 +0800 +Subject: s390/debug: Reject zero-length input before trimming a newline + +From: Pengpeng Hou + +[ 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 +Reviewed-by: Benjamin Block +Reviewed-by: Vasily Gorbik +Tested-by: Vasily Gorbik +Link: https://lore.kernel.org/r/20260417073530.96002-1-pengpeng@iscas.ac.cn +Signed-off-by: Vasily Gorbik +Signed-off-by: Alexander Gordeev +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..fdd5fdb2a4 --- /dev/null +++ b/queue-6.6/series @@ -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 index 0000000000..35959b2f11 --- /dev/null +++ b/queue-6.6/spi-spidev-fix-lock-inversion-between-spi_lock-and-b.patch @@ -0,0 +1,220 @@ +From 2b8b474394ed878185b5f908ff92e8e6a6db7258 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2026 14:10:51 +0800 +Subject: spi: spidev: fix lock inversion between spi_lock and buf_lock + +From: Fabian Godehardt + +[ 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 +Link: https://patch.msgid.link/20260211072616.489522-1-fg@emlix.com +Signed-off-by: Mark Brown +[ Minor context conflict resolved. ] +Signed-off-by: Wenshan Lan +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..7ece267c2e --- /dev/null +++ b/queue-7.0/iommu-amd-fix-illegal-cap-mmio-access-in-iommu-debug.patch @@ -0,0 +1,143 @@ +From 74bfa9059f07c553d9a534dedc5baeafe68e8573 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 19 Mar 2026 15:37:54 +0800 +Subject: iommu/amd: Fix illegal cap/mmio access in IOMMU debugfs + +From: Guanghui Feng + +[ 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 +Signed-off-by: Joerg Roedel +Stable-dep-of: 8dfd3d8d7443 ("iommu/amd: Remove latent out-of-bounds access in IOMMU debugfs") +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..21da8e1701 --- /dev/null +++ b/queue-7.0/iommu-amd-remove-latent-out-of-bounds-access-in-iomm.patch @@ -0,0 +1,83 @@ +From 5031bd461b1741d62806c7eefb7db628330ad6fd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 10 Apr 2026 14:55:50 +0200 +Subject: iommu/amd: Remove latent out-of-bounds access in IOMMU debugfs + +From: Eder Zulian + +[ 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 +Fixes: 7a4ee419e8c1 ("iommu/amd: Add debugfs support to dump IOMMU MMIO registers") +Cc: stable@vger.kernel.org +Signed-off-by: Joerg Roedel +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..d7baa45de2 --- /dev/null +++ b/queue-7.0/series @@ -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