]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.18-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 14 Jul 2026 14:46:30 +0000 (16:46 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 14 Jul 2026 14:46:30 +0000 (16:46 +0200)
added patches:
ksmbd-add-a-permission-check-for-fsctl_set_zero_data.patch
ksmbd-add-a-write_dac-write_owner-check-to-smb2-set_info-security.patch
ksmbd-add-per-handle-permission-check-to-file_link_information.patch
ksmbd-add-permission-checks-for-fsctl_duplicate_extents_to_file.patch
ksmbd-enforce-file_read_attributes-on-smb_find_file_posix_information.patch
ksmbd-fix-uaf-of-struct-file_lock-in-smb2_lock-deferred-lock-cancellation.patch
ksmbd-require-source-read-access-for-duplicate-extents.patch
ksmbd-run-set-info-with-opener-credentials.patch
ksmbd-serialize-query_directory-requests-per-file.patch
ksmbd-use-opener-credentials-for-ads-i-o.patch
ksmbd-use-opener-credentials-for-delete-on-close.patch
smb-client-fix-atime-clamp-check-in-read-completion.patch
smb-client-fix-change-notify-replay-double-free.patch
smb-client-fix-double-free-in-smb2_close-replay.patch
smb-client-fix-double-free-in-smb2_flush-replay.patch
smb-client-fix-double-free-in-smb2_ioctl-replay.patch
smb-client-fix-double-free-in-smb2_open-replay.patch
smb-client-fix-error-code-in-smb2_aead_req_alloc.patch
smb-client-fix-next-buffer-leak-in-receive_encrypted_standard.patch
smb-client-fix-query-directory-replay-double-free.patch
smb-client-fix-query_info-replay-double-free.patch
smb-client-harden-posix-sid-length-parsing.patch
smb-client-mask-server-provided-mode-to-07777-in-modefromsid.patch
smb-client-resolve-swn-tcon-from-live-registrations.patch
smb-client-use-unaligned-reads-in-parse_posix_ctxt.patch

26 files changed:
queue-6.18/ksmbd-add-a-permission-check-for-fsctl_set_zero_data.patch [new file with mode: 0644]
queue-6.18/ksmbd-add-a-write_dac-write_owner-check-to-smb2-set_info-security.patch [new file with mode: 0644]
queue-6.18/ksmbd-add-per-handle-permission-check-to-file_link_information.patch [new file with mode: 0644]
queue-6.18/ksmbd-add-permission-checks-for-fsctl_duplicate_extents_to_file.patch [new file with mode: 0644]
queue-6.18/ksmbd-enforce-file_read_attributes-on-smb_find_file_posix_information.patch [new file with mode: 0644]
queue-6.18/ksmbd-fix-uaf-of-struct-file_lock-in-smb2_lock-deferred-lock-cancellation.patch [new file with mode: 0644]
queue-6.18/ksmbd-require-source-read-access-for-duplicate-extents.patch [new file with mode: 0644]
queue-6.18/ksmbd-run-set-info-with-opener-credentials.patch [new file with mode: 0644]
queue-6.18/ksmbd-serialize-query_directory-requests-per-file.patch [new file with mode: 0644]
queue-6.18/ksmbd-use-opener-credentials-for-ads-i-o.patch [new file with mode: 0644]
queue-6.18/ksmbd-use-opener-credentials-for-delete-on-close.patch [new file with mode: 0644]
queue-6.18/series
queue-6.18/smb-client-fix-atime-clamp-check-in-read-completion.patch [new file with mode: 0644]
queue-6.18/smb-client-fix-change-notify-replay-double-free.patch [new file with mode: 0644]
queue-6.18/smb-client-fix-double-free-in-smb2_close-replay.patch [new file with mode: 0644]
queue-6.18/smb-client-fix-double-free-in-smb2_flush-replay.patch [new file with mode: 0644]
queue-6.18/smb-client-fix-double-free-in-smb2_ioctl-replay.patch [new file with mode: 0644]
queue-6.18/smb-client-fix-double-free-in-smb2_open-replay.patch [new file with mode: 0644]
queue-6.18/smb-client-fix-error-code-in-smb2_aead_req_alloc.patch [new file with mode: 0644]
queue-6.18/smb-client-fix-next-buffer-leak-in-receive_encrypted_standard.patch [new file with mode: 0644]
queue-6.18/smb-client-fix-query-directory-replay-double-free.patch [new file with mode: 0644]
queue-6.18/smb-client-fix-query_info-replay-double-free.patch [new file with mode: 0644]
queue-6.18/smb-client-harden-posix-sid-length-parsing.patch [new file with mode: 0644]
queue-6.18/smb-client-mask-server-provided-mode-to-07777-in-modefromsid.patch [new file with mode: 0644]
queue-6.18/smb-client-resolve-swn-tcon-from-live-registrations.patch [new file with mode: 0644]
queue-6.18/smb-client-use-unaligned-reads-in-parse_posix_ctxt.patch [new file with mode: 0644]

diff --git a/queue-6.18/ksmbd-add-a-permission-check-for-fsctl_set_zero_data.patch b/queue-6.18/ksmbd-add-a-permission-check-for-fsctl_set_zero_data.patch
new file mode 100644 (file)
index 0000000..4b9ca73
--- /dev/null
@@ -0,0 +1,50 @@
+From 3320ba068198adc144c89d6661b805acce01735b Mon Sep 17 00:00:00 2001
+From: Gil Portnoy <dddhkts1@gmail.com>
+Date: Wed, 10 Jun 2026 20:07:04 +0900
+Subject: ksmbd: add a permission check for FSCTL_SET_ZERO_DATA
+
+From: Gil Portnoy <dddhkts1@gmail.com>
+
+commit 3320ba068198adc144c89d6661b805acce01735b upstream.
+
+FSCTL_SET_ZERO_DATA in smb2_ioctl() destroys file data via
+ksmbd_vfs_zero_data() -> vfs_fallocate(PUNCH_HOLE/ZERO_RANGE) after
+checking only the share-level KSMBD_TREE_CONN_FLAG_WRITABLE, with no
+per-handle access check. A handle opened with only FILE_WRITE_ATTRIBUTES
+still yields an FMODE_WRITE filp (FILE_WRITE_ATTRIBUTES is part of
+FILE_WRITE_DESIRE_ACCESS_LE, so smb2_create_open_flags() opens it
+O_WRONLY), so the vfs_fallocate FMODE_WRITE check does not stop it; only
+the missing fp->daccess gate would. Reproduced on mainline 7.1-rc7 with
+KASAN by an authenticated SMB client: a FILE_WRITE_ATTRIBUTES-only handle
+zeroed 4096 bytes of file data it had no FILE_WRITE_DATA right to
+(6/6; a FILE_READ_DATA-only handle was correctly denied).
+
+This is the unfixed sibling of commit cc57232cae23 ("ksmbd: fix FSCTL
+permission bypass by adding a permission check for FSCTL_SET_SPARSE").
+Because SET_ZERO_DATA writes data (not an attribute), require
+FILE_WRITE_DATA.
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Gil Portnoy <dddhkts1@gmail.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/smb2pdu.c |    6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -8488,6 +8488,12 @@ int smb2_ioctl(struct ksmbd_work *work)
+                               goto out;
+                       }
++                      if (!(fp->daccess & FILE_WRITE_DATA_LE)) {
++                              ksmbd_fd_put(work, fp);
++                              ret = -EACCES;
++                              goto out;
++                      }
++
+                       ret = ksmbd_vfs_zero_data(work, fp, off, len);
+                       ksmbd_fd_put(work, fp);
+                       if (ret < 0)
diff --git a/queue-6.18/ksmbd-add-a-write_dac-write_owner-check-to-smb2-set_info-security.patch b/queue-6.18/ksmbd-add-a-write_dac-write_owner-check-to-smb2-set_info-security.patch
new file mode 100644 (file)
index 0000000..1c3468b
--- /dev/null
@@ -0,0 +1,60 @@
+From 44df157a1183a7f746caa970c169255da5ac61f8 Mon Sep 17 00:00:00 2001
+From: Gil Portnoy <dddhkts1@gmail.com>
+Date: Tue, 9 Jun 2026 00:00:00 +0000
+Subject: ksmbd: add a WRITE_DAC/WRITE_OWNER check to SMB2 SET_INFO SECURITY
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Gil Portnoy <dddhkts1@gmail.com>
+
+commit 44df157a1183a7f746caa970c169255da5ac61f8 upstream.
+
+commit cc57232cae23 ("ksmbd: fix FSCTL permission bypass by adding a
+permission check for FSCTL_SET_SPARSE") added a fp->daccess gate to
+fsctl_set_sparse and noted that "similar handle-level checks exist in other
+functions but are missing here." The SMB2 SET_INFO SECURITY arm is one of
+the missing ones, and the most security-relevant: smb2_set_info_sec() calls
+set_info_sec() with no per-handle access check.
+
+set_info_sec() (fs/smb/server/smbacl.c) re-permissions the file: it
+rewrites owner/group/mode via notify_change(), rewrites the POSIX ACL via
+set_posix_acl(), and on KSMBD_SHARE_FLAG_ACL_XATTR shares removes and
+rewrites the Windows security descriptor via ksmbd_vfs_set_sd_xattr().
+Every other persistent-mutation arm of the sibling handler
+smb2_set_info_file() checks fp->daccess first (FILE_WRITE_DATA /
+FILE_DELETE / FILE_WRITE_EA / FILE_WRITE_ATTRIBUTES); the SECURITY arm —
+which mutates the access control itself — is the only one with no gate.
+
+A client can therefore open a handle with FILE_WRITE_ATTRIBUTES only (no
+FILE_WRITE_DAC / FILE_WRITE_OWNER) and use SMB2_SET_INFO with InfoType
+SMB2_O_INFO_SECURITY to rewrite the file's DACL and owner, granting itself
+access the handle's daccess never carried. Unlike the FSCTL data arms this
+is a metadata/xattr operation, so there is no FMODE_WRITE VFS backstop —
+the missing fp->daccess check is the entire gate.
+
+Setting a security descriptor is the WRITE_DAC / WRITE_OWNER operation, so
+require at least one of those on the handle before re-permissioning the
+file. -EACCES is mapped to STATUS_ACCESS_DENIED by smb2_set_info().
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Gil Portnoy <dddhkts1@gmail.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/smb2pdu.c |    3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -6623,6 +6623,9 @@ static int smb2_set_info_sec(struct ksmb
+       fp->saccess |= FILE_SHARE_DELETE_LE;
++      if (!(fp->daccess & (FILE_WRITE_DAC_LE | FILE_WRITE_OWNER_LE)))
++              return -EACCES;
++
+       return set_info_sec(fp->conn, fp->tcon, &fp->filp->f_path, pntsd,
+                       buf_len, false, true);
+ }
diff --git a/queue-6.18/ksmbd-add-per-handle-permission-check-to-file_link_information.patch b/queue-6.18/ksmbd-add-per-handle-permission-check-to-file_link_information.patch
new file mode 100644 (file)
index 0000000..a30964b
--- /dev/null
@@ -0,0 +1,49 @@
+From 13f3942f2bf45856bb751faed2f0c4618f41ca20 Mon Sep 17 00:00:00 2001
+From: Gil Portnoy <dddhkts1@gmail.com>
+Date: Wed, 10 Jun 2026 20:13:51 +0900
+Subject: ksmbd: add per-handle permission check to FILE_LINK_INFORMATION
+
+From: Gil Portnoy <dddhkts1@gmail.com>
+
+commit 13f3942f2bf45856bb751faed2f0c4618f41ca20 upstream.
+
+The FILE_LINK_INFORMATION arm of smb2_set_info_file() calls
+smb2_create_link() with no per-handle fp->daccess check. On the
+ReplaceIfExists path smb2_create_link() unlinks an existing file at the
+target name (ksmbd_vfs_remove_file) and creates a hardlink
+(ksmbd_vfs_link); neither helper checks daccess. A handle opened with
+FILE_READ_DATA only (no FILE_DELETE, no FILE_WRITE_DATA) can therefore
+delete an arbitrary file in the share and plant a hardlink over its name.
+
+The sibling delete/move arms in the same switch already gate:
+FILE_RENAME_INFORMATION and FILE_DISPOSITION_INFORMATION both require
+FILE_DELETE_LE; FILE_FULL_EA_INFORMATION requires FILE_WRITE_EA_LE. Gate
+the link arm the same way as its closest analogue (rename), since it
+mutates the namespace and, on replace, deletes an existing entry.
+
+This is a sibling of commit cc57232cae23 ("ksmbd: fix FSCTL permission
+bypass by adding a permission check for FSCTL_SET_SPARSE").
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Gil Portnoy <dddhkts1@gmail.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/smb2pdu.c |    5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -6572,6 +6572,11 @@ static int smb2_set_info_file(struct ksm
+       }
+       case FILE_LINK_INFORMATION:
+       {
++              if (!(fp->daccess & FILE_DELETE_LE)) {
++                      pr_err("no right to delete : 0x%x\n", fp->daccess);
++                      return -EACCES;
++              }
++
+               if (buf_len < sizeof(struct smb2_file_link_info))
+                       return -EINVAL;
diff --git a/queue-6.18/ksmbd-add-permission-checks-for-fsctl_duplicate_extents_to_file.patch b/queue-6.18/ksmbd-add-permission-checks-for-fsctl_duplicate_extents_to_file.patch
new file mode 100644 (file)
index 0000000..b890430
--- /dev/null
@@ -0,0 +1,48 @@
+From 388e4139db27a9e3612c9d356b826f5b1ff6a9e3 Mon Sep 17 00:00:00 2001
+From: Gil Portnoy <dddhkts1@gmail.com>
+Date: Fri, 12 Jun 2026 07:15:38 +0900
+Subject: ksmbd: add permission checks for FSCTL_DUPLICATE_EXTENTS_TO_FILE
+
+From: Gil Portnoy <dddhkts1@gmail.com>
+
+commit 388e4139db27a9e3612c9d356b826f5b1ff6a9e3 upstream.
+
+The FSCTL_DUPLICATE_EXTENTS_TO_FILE arm of smb2_ioctl() overwrites the
+destination file's data via vfs_clone_file_range() with neither the
+share-level KSMBD_TREE_CONN_FLAG_WRITABLE check nor a per-handle
+fp->daccess check that the other write-bearing arms carry. A client can
+overwrite destination data on a read-only share, or from a handle opened
+with only FILE_WRITE_ATTRIBUTES (which still yields an FMODE_WRITE filp).
+FILE_WRITE_ATTRIBUTES-only destination handle overwrote the file's data via
+the clone. Add both checks, matching the FSCTL_SET_SPARSE permission fix;
+require FILE_WRITE_DATA since this writes data.
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Gil Portnoy <dddhkts1@gmail.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/smb2pdu.c |   11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -8563,6 +8563,17 @@ int smb2_ioctl(struct ksmbd_work *work)
+                       goto dup_ext_out;
+               }
++              if (!test_tree_conn_flag(work->tcon,
++                                       KSMBD_TREE_CONN_FLAG_WRITABLE)) {
++                      ret = -EACCES;
++                      goto dup_ext_out;
++              }
++
++              if (!(fp_out->daccess & FILE_WRITE_DATA_LE)) {
++                      ret = -EACCES;
++                      goto dup_ext_out;
++              }
++
+               src_off = le64_to_cpu(dup_ext->SourceFileOffset);
+               dst_off = le64_to_cpu(dup_ext->TargetFileOffset);
+               length = le64_to_cpu(dup_ext->ByteCount);
diff --git a/queue-6.18/ksmbd-enforce-file_read_attributes-on-smb_find_file_posix_information.patch b/queue-6.18/ksmbd-enforce-file_read_attributes-on-smb_find_file_posix_information.patch
new file mode 100644 (file)
index 0000000..87a49f3
--- /dev/null
@@ -0,0 +1,47 @@
+From 20c8442dc1003f9f7bb522d3dcd81d09ea59a79e Mon Sep 17 00:00:00 2001
+From: Gil Portnoy <dddhkts1@gmail.com>
+Date: Thu, 11 Jun 2026 22:59:51 +0900
+Subject: ksmbd: enforce FILE_READ_ATTRIBUTES on SMB_FIND_FILE_POSIX_INFORMATION
+
+From: Gil Portnoy <dddhkts1@gmail.com>
+
+commit 20c8442dc1003f9f7bb522d3dcd81d09ea59a79e upstream.
+
+find_file_posix_info() in smb2_query_info() returns file metadata (owner
+uid, group gid, mode, inode, size, allocation size, hard-link count and all
+four timestamps) but performs no per-handle access check. Every sibling
+query handler gates on the handle's granted access first --
+get_file_basic_info(), get_file_all_info(), get_file_network_open_info()
+and get_file_attribute_tag_info() all reject a handle lacking
+FILE_READ_ATTRIBUTES_LE with -EACCES. The POSIX handler is gated only by
+the connection-scoped tcon->posix_extensions flag, which is not a
+per-handle authorization, so a handle opened with only FILE_WRITE_DATA is
+correctly denied FileBasicInformation yet is allowed the strict-superset
+POSIX info. Mirror the FILE_READ_ATTRIBUTES_LE gate the sibling info
+handlers already use.
+
+Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3")
+Cc: stable@vger.kernel.org
+Signed-off-by: Gil Portnoy <dddhkts1@gmail.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/smb2pdu.c |    6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -5339,6 +5339,12 @@ static int find_file_posix_info(struct s
+       int out_buf_len = sizeof(struct smb311_posix_qinfo) + 32;
+       int ret;
++      if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) {
++              pr_err("no right to read the attributes : 0x%x\n",
++                     fp->daccess);
++              return -EACCES;
++      }
++
+       ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
+                         AT_STATX_SYNC_AS_STAT);
+       if (ret)
diff --git a/queue-6.18/ksmbd-fix-uaf-of-struct-file_lock-in-smb2_lock-deferred-lock-cancellation.patch b/queue-6.18/ksmbd-fix-uaf-of-struct-file_lock-in-smb2_lock-deferred-lock-cancellation.patch
new file mode 100644 (file)
index 0000000..dd63972
--- /dev/null
@@ -0,0 +1,86 @@
+From d20d1c8ba5765d1d12eefc0aee6385ab3f240e1e Mon Sep 17 00:00:00 2001
+From: Davide Ornaghi <d.ornaghi97@gmail.com>
+Date: Sat, 6 Jun 2026 16:11:04 +0900
+Subject: ksmbd: fix UAF of struct file_lock in SMB2_LOCK deferred-lock cancellation
+
+From: Davide Ornaghi <d.ornaghi97@gmail.com>
+
+commit d20d1c8ba5765d1d12eefc0aee6385ab3f240e1e upstream.
+
+When a blocking byte-range lock request is deferred in the
+FILE_LOCK_DEFERRED path, ksmbd registers the asynchronous work into
+the connection's async_requests list via setup_async_work(). The cancel
+callback smb2_remove_blocked_lock() holds a reference to the flock.
+
+If the lock waiter is subsequently woken up but the work state is no
+longer KSMBD_WORK_ACTIVE (e.g., due to a concurrent cancellation), the
+cleanup path calls locks_free_lock(flock) without dequeuing the work from
+the async_requests list. Concurrently, smb2_cancel() walks the list
+under conn->request_lock and invokes the cancel callback, which then
+dereferences the already freed 'flock'. This leads to a slab-use-after-free
+inside __wake_up_common.
+
+Fix this by restructuring the cleanup logic after the worker returns
+from ksmbd_vfs_posix_lock_wait(). Move list_del(&smb_lock->llist) and
+release_async_work(work) to the top of the cleanup block. This guarantees
+that the async work is completely dequeued and serialized under
+conn->request_lock before locks_free_lock(flock) is called, rendering
+the flock unreachable for any concurrent smb2_cancel().
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Davide Ornaghi <d.ornaghi97@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/smb2pdu.c |   34 ++++++++++++++++------------------
+ 1 file changed, 16 insertions(+), 18 deletions(-)
+
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -7747,29 +7747,27 @@ skip:
+                               list_del(&work->fp_entry);
+                               spin_unlock(&fp->f_lock);
+-                              if (work->state != KSMBD_WORK_ACTIVE) {
+-                                      list_del(&smb_lock->llist);
+-                                      locks_free_lock(flock);
++                              list_del(&smb_lock->llist);
++                              release_async_work(work);
++
++                              if (work->state == KSMBD_WORK_ACTIVE)
++                                      goto retry;
+-                                      if (work->state == KSMBD_WORK_CANCELLED) {
+-                                              rsp->hdr.Status =
+-                                                      STATUS_CANCELLED;
+-                                              kfree(smb_lock);
+-                                              smb2_send_interim_resp(work,
+-                                                                     STATUS_CANCELLED);
+-                                              work->send_no_response = 1;
+-                                              goto out;
+-                                      }
++                              locks_free_lock(flock);
+-                                      rsp->hdr.Status =
+-                                              STATUS_RANGE_NOT_LOCKED;
++                              if (work->state == KSMBD_WORK_CANCELLED) {
++                                      rsp->hdr.Status = STATUS_CANCELLED;
+                                       kfree(smb_lock);
+-                                      goto out2;
++                                      smb2_send_interim_resp(work,
++                                                      STATUS_CANCELLED);
++                                      work->send_no_response = 1;
++                                      goto out;
+                               }
+-                              list_del(&smb_lock->llist);
+-                              release_async_work(work);
+-                              goto retry;
++                              rsp->hdr.Status =
++                                      STATUS_RANGE_NOT_LOCKED;
++                              kfree(smb_lock);
++                              goto out2;
+                       } else if (!rc) {
+                               list_add(&smb_lock->llist, &rollback_list);
+                               spin_lock(&work->conn->llist_lock);
diff --git a/queue-6.18/ksmbd-require-source-read-access-for-duplicate-extents.patch b/queue-6.18/ksmbd-require-source-read-access-for-duplicate-extents.patch
new file mode 100644 (file)
index 0000000..0e75118
--- /dev/null
@@ -0,0 +1,40 @@
+From cedff600f1642aa982178503552f0d007bc829c8 Mon Sep 17 00:00:00 2001
+From: Namjae Jeon <linkinjeon@kernel.org>
+Date: Sat, 13 Jun 2026 22:00:02 +0900
+Subject: ksmbd: require source read access for duplicate extents
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+commit cedff600f1642aa982178503552f0d007bc829c8 upstream.
+
+FSCTL_DUPLICATE_EXTENTS_TO_FILE passes the source file directly to
+vfs_clone_file_range() or vfs_copy_file_range() without checking the SMB
+access mask granted to the source handle. A handle opened with attribute
+access can consequently be used to copy file contents into an
+attacker-readable destination.
+
+Require FILE_READ_DATA on the source handle before either VFS operation,
+matching other ksmbd data-copy paths.
+
+Cc: stable@vger.kernel.org
+Reported-by: Musaab Khan <musaab.khan@protonmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/smb2pdu.c |    4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -8581,6 +8581,10 @@ int smb2_ioctl(struct ksmbd_work *work)
+                       ret = -EACCES;
+                       goto dup_ext_out;
+               }
++              if (!(fp_in->daccess & FILE_READ_DATA_LE)) {
++                      ret = -EACCES;
++                      goto dup_ext_out;
++              }
+               src_off = le64_to_cpu(dup_ext->SourceFileOffset);
+               dst_off = le64_to_cpu(dup_ext->TargetFileOffset);
diff --git a/queue-6.18/ksmbd-run-set-info-with-opener-credentials.patch b/queue-6.18/ksmbd-run-set-info-with-opener-credentials.patch
new file mode 100644 (file)
index 0000000..ad87a1e
--- /dev/null
@@ -0,0 +1,68 @@
+From b383bcad3d2fe634b26efbce53e22bbb5753a520 Mon Sep 17 00:00:00 2001
+From: Namjae Jeon <linkinjeon@kernel.org>
+Date: Sat, 13 Jun 2026 22:00:01 +0900
+Subject: ksmbd: run set info with opener credentials
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+commit b383bcad3d2fe634b26efbce53e22bbb5753a520 upstream.
+
+SMB2 SET_INFO handlers call path-based VFS helpers after checking the
+access mask granted to the SMB handle. Those helpers perform their owner,
+inode permission and LSM checks using the current ksmbd worker credentials.
+
+Run the complete SET_INFO dispatch with the credentials captured when the
+handle was opened. This also removes the separate security information
+credential setup and keeps all SET_INFO classes under one credential scope.
+
+Direct override_creds() is used because it can nest with the request
+credential overrides already used by rename and link helpers.
+
+Cc: stable@vger.kernel.org
+Reported-by: Musaab Khan <musaab.khan@protonmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/smb2pdu.c |    8 +++-----
+ 1 file changed, 3 insertions(+), 5 deletions(-)
+
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -6638,6 +6638,7 @@ static int smb2_set_info_sec(struct ksmb
+  */
+ int smb2_set_info(struct ksmbd_work *work)
+ {
++      const struct cred *saved_cred;
+       struct smb2_set_info_req *req;
+       struct smb2_set_info_rsp *rsp;
+       struct ksmbd_file *fp = NULL;
+@@ -6679,6 +6680,7 @@ int smb2_set_info(struct ksmbd_work *wor
+               goto err_out;
+       }
++      saved_cred = override_creds(fp->filp->f_cred);
+       switch (req->InfoType) {
+       case SMB2_O_INFO_FILE:
+               ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n");
+@@ -6686,19 +6688,15 @@ int smb2_set_info(struct ksmbd_work *wor
+               break;
+       case SMB2_O_INFO_SECURITY:
+               ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n");
+-              if (ksmbd_override_fsids(work)) {
+-                      rc = -ENOMEM;
+-                      goto err_out;
+-              }
+               rc = smb2_set_info_sec(fp,
+                                      le32_to_cpu(req->AdditionalInformation),
+                                      (char *)req + le16_to_cpu(req->BufferOffset),
+                                      le32_to_cpu(req->BufferLength));
+-              ksmbd_revert_fsids(work);
+               break;
+       default:
+               rc = -EOPNOTSUPP;
+       }
++      revert_creds(saved_cred);
+       if (rc < 0)
+               goto err_out;
diff --git a/queue-6.18/ksmbd-serialize-query_directory-requests-per-file.patch b/queue-6.18/ksmbd-serialize-query_directory-requests-per-file.patch
new file mode 100644 (file)
index 0000000..e74b8bf
--- /dev/null
@@ -0,0 +1,86 @@
+From be6d26bf27499977c746abc163659915082348d8 Mon Sep 17 00:00:00 2001
+From: Namjae Jeon <linkinjeon@kernel.org>
+Date: Fri, 12 Jun 2026 08:00:00 +0900
+Subject: ksmbd: serialize QUERY_DIRECTORY requests per file
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+commit be6d26bf27499977c746abc163659915082348d8 upstream.
+
+smb2_query_dir() stores a pointer to its stack-allocated private data in
+the ksmbd_file readdir_data. Concurrent QUERY_DIRECTORY requests using the
+same file handle can overwrite this pointer while an iterate_dir() callback
+is still using it, resulting in a stack use-after-free.
+
+Add a per-file mutex and hold it while accessing the shared directory
+enumeration state. The lock covers scan restart, dot entry state,
+readdir_data setup and iteration, and response construction. This prevents
+another request from replacing readdir_data.private before the current
+request has finished using it and also serializes the shared file position.
+
+Cc: stable@vger.kernel.org
+Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-30527
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/smb2pdu.c   |    4 ++++
+ fs/smb/server/vfs_cache.c |    1 +
+ fs/smb/server/vfs_cache.h |    2 ++
+ 3 files changed, 7 insertions(+)
+
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -4472,6 +4472,8 @@ int smb2_query_dir(struct ksmbd_work *wo
+               ksmbd_debug(SMB, "Search pattern is %s\n", srch_ptr);
+       }
++      mutex_lock(&dir_fp->readdir_lock);
++
+       if (srch_flag & SMB2_REOPEN || srch_flag & SMB2_RESTART_SCANS) {
+               ksmbd_debug(SMB, "Restart directory scan\n");
+               generic_file_llseek(dir_fp->filp, 0, SEEK_SET);
+@@ -4576,6 +4578,7 @@ no_buf_len:
+                       goto err_out;
+       }
++      mutex_unlock(&dir_fp->readdir_lock);
+       kfree(srch_ptr);
+       ksmbd_fd_put(work, dir_fp);
+       ksmbd_revert_fsids(work);
+@@ -4583,6 +4586,7 @@ no_buf_len:
+ err_out:
+       pr_err("error while processing smb2 query dir rc = %d\n", rc);
++      mutex_unlock(&dir_fp->readdir_lock);
+       kfree(srch_ptr);
+ err_out2:
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -686,6 +686,7 @@ struct ksmbd_file *ksmbd_open_fd(struct
+       INIT_LIST_HEAD(&fp->node);
+       INIT_LIST_HEAD(&fp->lock_list);
+       spin_lock_init(&fp->f_lock);
++      mutex_init(&fp->readdir_lock);
+       atomic_set(&fp->refcount, 1);
+       fp->filp                = filp;
+--- a/fs/smb/server/vfs_cache.h
++++ b/fs/smb/server/vfs_cache.h
+@@ -8,6 +8,7 @@
+ #include <linux/file.h>
+ #include <linux/fs.h>
++#include <linux/mutex.h>
+ #include <linux/rwsem.h>
+ #include <linux/spinlock.h>
+ #include <linux/idr.h>
+@@ -113,6 +114,7 @@ struct ksmbd_file {
+       /* if ls is happening on directory, below is valid*/
+       struct ksmbd_readdir_data       readdir_data;
++      struct mutex                    readdir_lock;
+       int                             dot_dotdot[2];
+       unsigned int                    f_state;
+       bool                            reserve_lease_break;
diff --git a/queue-6.18/ksmbd-use-opener-credentials-for-ads-i-o.patch b/queue-6.18/ksmbd-use-opener-credentials-for-ads-i-o.patch
new file mode 100644 (file)
index 0000000..bbad613
--- /dev/null
@@ -0,0 +1,89 @@
+From baa5e094886fffa7e6272edcb5e08be5ce28262c Mon Sep 17 00:00:00 2001
+From: Namjae Jeon <linkinjeon@kernel.org>
+Date: Sat, 13 Jun 2026 22:00:03 +0900
+Subject: ksmbd: use opener credentials for ADS I/O
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+commit baa5e094886fffa7e6272edcb5e08be5ce28262c upstream.
+
+Alternate data streams are stored as xattrs. Unlike regular file I/O,
+their read and write paths therefore call VFS xattr helpers which recheck
+inode permissions and LSM policy using the current task credentials.
+
+Run ADS I/O with the credentials captured when the SMB handle was opened.
+
+Cc: stable@vger.kernel.org
+Reported-by: Musaab Khan <musaab.khan@protonmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/vfs.c |   11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -278,17 +278,20 @@ out:
+ static int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos,
+                                size_t count)
+ {
++      const struct cred *saved_cred;
+       ssize_t v_len;
+       char *stream_buf = NULL;
+       ksmbd_debug(VFS, "read stream data pos : %llu, count : %zd\n",
+                   *pos, count);
++      saved_cred = override_creds(fp->filp->f_cred);
+       v_len = ksmbd_vfs_getcasexattr(file_mnt_idmap(fp->filp),
+                                      fp->filp->f_path.dentry,
+                                      fp->stream.name,
+                                      fp->stream.size,
+                                      &stream_buf);
++      revert_creds(saved_cred);
+       if ((int)v_len <= 0)
+               return (int)v_len;
+@@ -411,6 +414,7 @@ int ksmbd_vfs_read(struct ksmbd_work *wo
+ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
+                                 size_t count)
+ {
++      const struct cred *saved_cred;
+       char *stream_buf = NULL, *wbuf;
+       struct mnt_idmap *idmap = file_mnt_idmap(fp->filp);
+       size_t size;
+@@ -431,6 +435,7 @@ static int ksmbd_vfs_stream_write(struct
+               count = XATTR_SIZE_MAX - *pos;
+       }
++      saved_cred = override_creds(fp->filp->f_cred);
+       v_len = ksmbd_vfs_getcasexattr(idmap,
+                                      fp->filp->f_path.dentry,
+                                      fp->stream.name,
+@@ -439,14 +444,14 @@ static int ksmbd_vfs_stream_write(struct
+       if (v_len < 0) {
+               pr_err("not found stream in xattr : %zd\n", v_len);
+               err = v_len;
+-              goto out;
++              goto out_revert;
+       }
+       if (v_len < size) {
+               wbuf = kvzalloc(size, KSMBD_DEFAULT_GFP);
+               if (!wbuf) {
+                       err = -ENOMEM;
+-                      goto out;
++                      goto out_revert;
+               }
+               if (v_len > 0)
+@@ -464,6 +469,8 @@ static int ksmbd_vfs_stream_write(struct
+                                size,
+                                0,
+                                true);
++out_revert:
++      revert_creds(saved_cred);
+       if (err < 0)
+               goto out;
+       else
diff --git a/queue-6.18/ksmbd-use-opener-credentials-for-delete-on-close.patch b/queue-6.18/ksmbd-use-opener-credentials-for-delete-on-close.patch
new file mode 100644 (file)
index 0000000..0a86409
--- /dev/null
@@ -0,0 +1,74 @@
+From 52e2f21911158ec961cd5aae19c56460db382af0 Mon Sep 17 00:00:00 2001
+From: Namjae Jeon <linkinjeon@kernel.org>
+Date: Sat, 13 Jun 2026 22:00:00 +0900
+Subject: ksmbd: use opener credentials for delete-on-close
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+commit 52e2f21911158ec961cd5aae19c56460db382af0 upstream.
+
+Delete-on-close can be completed by deferred or durable handle teardown,
+where no request work is available. Both the base-file unlink and the ADS
+xattr removal consequently run with the ksmbd worker credentials and can
+bypass filesystem permission checks.
+
+Run both operations with the credentials captured in struct file when the
+handle was opened. This preserves the authenticated user's fsuid, fsgid,
+supplementary groups and capability restrictions at final close.
+
+Cc: stable@vger.kernel.org
+Reported-by: Musaab Khan <musaab.khan@protonmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/vfs.c       |    7 +++++--
+ fs/smb/server/vfs_cache.c |    4 ++++
+ 2 files changed, 9 insertions(+), 2 deletions(-)
+
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -1078,13 +1078,15 @@ int ksmbd_vfs_remove_xattr(struct mnt_id
+ int ksmbd_vfs_unlink(struct file *filp)
+ {
++      const struct cred *saved_cred;
+       int err = 0;
+       struct dentry *dir, *dentry = filp->f_path.dentry;
+       struct mnt_idmap *idmap = file_mnt_idmap(filp);
++      saved_cred = override_creds(filp->f_cred);
+       err = mnt_want_write(filp->f_path.mnt);
+       if (err)
+-              return err;
++              goto out_revert;
+       dir = dget_parent(dentry);
+       err = ksmbd_vfs_lock_parent(dir, dentry);
+@@ -1104,7 +1106,8 @@ int ksmbd_vfs_unlink(struct file *filp)
+ out:
+       dput(dir);
+       mnt_drop_write(filp->f_path.mnt);
+-
++out_revert:
++      revert_creds(saved_cred);
+       return err;
+ }
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -292,10 +292,14 @@ static void __ksmbd_inode_close(struct k
+               up_write(&ci->m_lock);
+               if (remove_stream_xattr) {
++                      const struct cred *saved_cred;
++
++                      saved_cred = override_creds(filp->f_cred);
+                       err = ksmbd_vfs_remove_xattr(file_mnt_idmap(filp),
+                                                    &filp->f_path,
+                                                    fp->stream.name,
+                                                    true);
++                      revert_creds(saved_cred);
+                       if (err)
+                               pr_err("remove xattr failed : %s\n",
+                                      fp->stream.name);
index 1a1f029188c93c688f8dff3234e2154c0ad8a7ae..719b6a4208d4c407094224c33e7f2c7a05d5d860 100644 (file)
@@ -212,3 +212,28 @@ fs-ntfs3-fsync-files-by-syncing-parent-inodes.patch
 fs-ntfs3-zero-fill-folios-beyond-i_valid-in-ntfs_rea.patch
 fs-ntfs3-fix-missing-run-load-for-vcn0-in-attr_data_.patch
 coresight-ultrasoc-smb-fix-oob-write-in-smb_sync_perf_buffer.patch
+smb-client-resolve-swn-tcon-from-live-registrations.patch
+smb-client-fix-error-code-in-smb2_aead_req_alloc.patch
+ksmbd-add-permission-checks-for-fsctl_duplicate_extents_to_file.patch
+ksmbd-add-a-permission-check-for-fsctl_set_zero_data.patch
+ksmbd-serialize-query_directory-requests-per-file.patch
+ksmbd-fix-uaf-of-struct-file_lock-in-smb2_lock-deferred-lock-cancellation.patch
+ksmbd-require-source-read-access-for-duplicate-extents.patch
+ksmbd-add-a-write_dac-write_owner-check-to-smb2-set_info-security.patch
+ksmbd-run-set-info-with-opener-credentials.patch
+ksmbd-enforce-file_read_attributes-on-smb_find_file_posix_information.patch
+ksmbd-add-per-handle-permission-check-to-file_link_information.patch
+ksmbd-use-opener-credentials-for-delete-on-close.patch
+ksmbd-use-opener-credentials-for-ads-i-o.patch
+smb-client-fix-query-directory-replay-double-free.patch
+smb-client-fix-query_info-replay-double-free.patch
+smb-client-fix-double-free-in-smb2_ioctl-replay.patch
+smb-client-fix-change-notify-replay-double-free.patch
+smb-client-fix-double-free-in-smb2_flush-replay.patch
+smb-client-fix-double-free-in-smb2_open-replay.patch
+smb-client-fix-double-free-in-smb2_close-replay.patch
+smb-client-fix-next-buffer-leak-in-receive_encrypted_standard.patch
+smb-client-use-unaligned-reads-in-parse_posix_ctxt.patch
+smb-client-harden-posix-sid-length-parsing.patch
+smb-client-fix-atime-clamp-check-in-read-completion.patch
+smb-client-mask-server-provided-mode-to-07777-in-modefromsid.patch
diff --git a/queue-6.18/smb-client-fix-atime-clamp-check-in-read-completion.patch b/queue-6.18/smb-client-fix-atime-clamp-check-in-read-completion.patch
new file mode 100644 (file)
index 0000000..7ec0263
--- /dev/null
@@ -0,0 +1,53 @@
+From 0b043279e73880bee21d3b1f221bafda5af1b27e Mon Sep 17 00:00:00 2001
+From: Xu Rao <raoxu@uniontech.com>
+Date: Tue, 7 Jul 2026 21:30:17 +0800
+Subject: smb: client: fix atime clamp check in read completion
+
+From: Xu Rao <raoxu@uniontech.com>
+
+commit 0b043279e73880bee21d3b1f221bafda5af1b27e upstream.
+
+cifs_rreq_done() updates the inode atime to current_time(inode) after a
+netfs read.  It then preserves the CIFS rule that atime should not be
+older than mtime, because some applications break if atime is less than
+mtime.  That rule only requires clamping when atime < mtime.
+
+The current check uses the raw non-zero result of timespec64_compare().
+It therefore takes the clamp path for both atime < mtime and
+atime > mtime.  The latter is the normal case when reading an older file:
+the newly recorded atime is newer than the file mtime.  The completion
+handler then immediately moves atime back to mtime, losing the access
+time that was just recorded.  Userspace tools that rely on atime, such as
+stat, find -atime, backup tools or cold-data classifiers, can therefore
+see a recently read CIFS file as not recently accessed.
+
+This is easy to miss because the bug is silent: read I/O still succeeds,
+no error is reported, and many systems either do not check atime after
+reads or mount with policies such as relatime/noatime.  It becomes
+visible when a CIFS file has an mtime older than the current time, the
+file is read, and the local inode atime is inspected before a later
+revalidation replaces the cached timestamps.
+
+Clamp only when atime is actually older than mtime.  This matches the
+same atime/mtime rule used when applying CIFS inode attributes.
+
+Fixes: 69c3c023af25 ("cifs: Implement netfslib hooks")
+Cc: stable@vger.kernel.org
+Signed-off-by: Xu Rao <raoxu@uniontech.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/file.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/smb/client/file.c
++++ b/fs/smb/client/file.c
+@@ -302,7 +302,7 @@ static void cifs_rreq_done(struct netfs_
+       /* we do not want atime to be less than mtime, it broke some apps */
+       atime = inode_set_atime_to_ts(inode, current_time(inode));
+       mtime = inode_get_mtime(inode);
+-      if (timespec64_compare(&atime, &mtime))
++      if (timespec64_compare(&atime, &mtime) < 0)
+               inode_set_atime_to_ts(inode, inode_get_mtime(inode));
+ }
diff --git a/queue-6.18/smb-client-fix-change-notify-replay-double-free.patch b/queue-6.18/smb-client-fix-change-notify-replay-double-free.patch
new file mode 100644 (file)
index 0000000..7653830
--- /dev/null
@@ -0,0 +1,35 @@
+From 145f820dcbb2cced374f2532f8a61a44dce4a615 Mon Sep 17 00:00:00 2001
+From: Henrique Carvalho <henrique.carvalho@suse.com>
+Date: Thu, 18 Jun 2026 17:34:37 -0300
+Subject: smb: client: fix change notify replay double-free
+
+From: Henrique Carvalho <henrique.carvalho@suse.com>
+
+commit 145f820dcbb2cced374f2532f8a61a44dce4a615 upstream.
+
+A response-bearing attempt can return a replayable error and free its
+response buffer. If SMB2_notify_init() fails before the next send, cleanup
+retains the previous buffer type and frees that response again.
+
+Reset response bookkeeping before each attempt to prevent the stale free.
+
+Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set")
+Cc: stable@vger.kernel.org
+Signed-off-by: Henrique Carvalho <henrique.carvalho@suse.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smb2pdu.c |    2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/fs/smb/client/smb2pdu.c
++++ b/fs/smb/client/smb2pdu.c
+@@ -4010,6 +4010,8 @@ SMB2_change_notify(const unsigned int xi
+ replay_again:
+       /* reinitialize for possible replay */
++      resp_buftype = CIFS_NO_BUFFER;
++      memset(&rsp_iov, 0, sizeof(rsp_iov));
+       flags = 0;
+       server = cifs_pick_channel(ses);
diff --git a/queue-6.18/smb-client-fix-double-free-in-smb2_close-replay.patch b/queue-6.18/smb-client-fix-double-free-in-smb2_close-replay.patch
new file mode 100644 (file)
index 0000000..530113b
--- /dev/null
@@ -0,0 +1,35 @@
+From f96e1cdcb63ed3321142ff2fcdf784e32cda8fee Mon Sep 17 00:00:00 2001
+From: Henrique Carvalho <henrique.carvalho@suse.com>
+Date: Thu, 18 Jun 2026 17:34:35 -0300
+Subject: smb: client: fix double-free in SMB2_close() replay
+
+From: Henrique Carvalho <henrique.carvalho@suse.com>
+
+commit f96e1cdcb63ed3321142ff2fcdf784e32cda8fee upstream.
+
+A response-bearing attempt can return a replayable error and free its
+response buffer. If SMB2_close_init() fails before the next send, cleanup
+retains the previous buffer type and frees that response again.
+
+Reset response bookkeeping before each attempt to prevent the stale free.
+
+Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set")
+Cc: stable@vger.kernel.org
+Signed-off-by: Henrique Carvalho <henrique.carvalho@suse.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smb2pdu.c |    2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/fs/smb/client/smb2pdu.c
++++ b/fs/smb/client/smb2pdu.c
+@@ -3617,6 +3617,8 @@ __SMB2_close(const unsigned int xid, str
+ replay_again:
+       /* reinitialize for possible replay */
++      resp_buftype = CIFS_NO_BUFFER;
++      memset(&rsp_iov, 0, sizeof(rsp_iov));
+       flags = 0;
+       query_attrs = false;
+       server = cifs_pick_channel(ses);
diff --git a/queue-6.18/smb-client-fix-double-free-in-smb2_flush-replay.patch b/queue-6.18/smb-client-fix-double-free-in-smb2_flush-replay.patch
new file mode 100644 (file)
index 0000000..171d25e
--- /dev/null
@@ -0,0 +1,45 @@
+From 4be31c943a3a27a5a0251dbb8f5cb89059ec3d5a Mon Sep 17 00:00:00 2001
+From: Zhao Zhang <zzhan461@ucr.edu>
+Date: Thu, 18 Jun 2026 23:28:05 +0800
+Subject: smb: client: fix double-free in SMB2_flush() replay
+
+From: Zhao Zhang <zzhan461@ucr.edu>
+
+commit 4be31c943a3a27a5a0251dbb8f5cb89059ec3d5a upstream.
+
+SMB2_flush() keeps its response buffer bookkeeping across replay
+attempts. If a replayable flush response is received and the retry then
+fails before cifs_send_recv() stores a replacement response, flush_exit
+will free the stale response pointer a second time.
+
+Reinitialize resp_buftype and rsp_iov at the top of the replay loop so
+cleanup only acts on response state produced by the current attempt.
+This fixes a double-free without changing replay handling for successful
+requests.
+
+Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set")
+Cc: stable@vger.kernel.org
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Zhengchuan Liang <zcliangcn@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Assisted-by: Codex:GPT-5.4
+Acked-by: Henrique Carvalho <henrique.carvalho@suse.com>
+Signed-off-by: Zhao Zhang <zzhan461@ucr.edu>
+Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smb2pdu.c |    2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/fs/smb/client/smb2pdu.c
++++ b/fs/smb/client/smb2pdu.c
+@@ -4351,6 +4351,8 @@ SMB2_flush(const unsigned int xid, struc
+ replay_again:
+       /* reinitialize for possible replay */
++      resp_buftype = CIFS_NO_BUFFER;
++      memset(&rsp_iov, 0, sizeof(rsp_iov));
+       flags = 0;
+       server = cifs_pick_channel(ses);
diff --git a/queue-6.18/smb-client-fix-double-free-in-smb2_ioctl-replay.patch b/queue-6.18/smb-client-fix-double-free-in-smb2_ioctl-replay.patch
new file mode 100644 (file)
index 0000000..5f9934e
--- /dev/null
@@ -0,0 +1,35 @@
+From f9bbadb6c94583e3b4af1afc449bfceb1d1ddec9 Mon Sep 17 00:00:00 2001
+From: Henrique Carvalho <henrique.carvalho@suse.com>
+Date: Thu, 18 Jun 2026 17:34:34 -0300
+Subject: smb: client: fix double-free in SMB2_ioctl() replay
+
+From: Henrique Carvalho <henrique.carvalho@suse.com>
+
+commit f9bbadb6c94583e3b4af1afc449bfceb1d1ddec9 upstream.
+
+A response-bearing attempt can return a replayable error and free its
+response buffer. If SMB2_ioctl_init() fails before the next send, cleanup
+retains the previous buffer type and frees that response again.
+
+Reset response bookkeeping before each attempt to prevent the stale free.
+
+Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set")
+Cc: stable@vger.kernel.org
+Signed-off-by: Henrique Carvalho <henrique.carvalho@suse.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smb2pdu.c |    2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/fs/smb/client/smb2pdu.c
++++ b/fs/smb/client/smb2pdu.c
+@@ -3425,6 +3425,8 @@ SMB2_ioctl(const unsigned int xid, struc
+ replay_again:
+       /* reinitialize for possible replay */
++      resp_buftype = CIFS_NO_BUFFER;
++      memset(&rsp_iov, 0, sizeof(rsp_iov));
+       flags = 0;
+       server = cifs_pick_channel(ses);
diff --git a/queue-6.18/smb-client-fix-double-free-in-smb2_open-replay.patch b/queue-6.18/smb-client-fix-double-free-in-smb2_open-replay.patch
new file mode 100644 (file)
index 0000000..fdc1c38
--- /dev/null
@@ -0,0 +1,35 @@
+From b55e182f2324bc6a604c21a47aa6c448f719a532 Mon Sep 17 00:00:00 2001
+From: Henrique Carvalho <henrique.carvalho@suse.com>
+Date: Thu, 18 Jun 2026 17:34:33 -0300
+Subject: smb: client: fix double-free in SMB2_open() replay
+
+From: Henrique Carvalho <henrique.carvalho@suse.com>
+
+commit b55e182f2324bc6a604c21a47aa6c448f719a532 upstream.
+
+A response-bearing attempt can return a replayable error and free its
+response buffer. If SMB2_open_init() fails before the next send, cleanup
+retains the previous buffer type and frees that response again.
+
+Reset response bookkeeping before each attempt to prevent the stale free.
+
+Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set")
+Cc: stable@vger.kernel.org
+Signed-off-by: Henrique Carvalho <henrique.carvalho@suse.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smb2pdu.c |    2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/fs/smb/client/smb2pdu.c
++++ b/fs/smb/client/smb2pdu.c
+@@ -3205,6 +3205,8 @@ SMB2_open(const unsigned int xid, struct
+ replay_again:
+       /* reinitialize for possible replay */
++      resp_buftype = CIFS_NO_BUFFER;
++      memset(&rsp_iov, 0, sizeof(rsp_iov));
+       flags = 0;
+       server = cifs_pick_channel(ses);
+       oparms->replay = !!(retries);
diff --git a/queue-6.18/smb-client-fix-error-code-in-smb2_aead_req_alloc.patch b/queue-6.18/smb-client-fix-error-code-in-smb2_aead_req_alloc.patch
new file mode 100644 (file)
index 0000000..a84bb09
--- /dev/null
@@ -0,0 +1,45 @@
+From 61f28012e5650c619223decdb7970e0d3162e949 Mon Sep 17 00:00:00 2001
+From: Dan Carpenter <error27@gmail.com>
+Date: Thu, 11 Jun 2026 10:35:28 +0300
+Subject: smb/client: Fix error code in smb2_aead_req_alloc()
+
+From: Dan Carpenter <error27@gmail.com>
+
+commit 61f28012e5650c619223decdb7970e0d3162e949 upstream.
+
+The "*num_sgs" variable is a u32 so "ERR_PTR(*num_sgs)" doesn't work.
+We would have to do something similar to the previous line where it's
+cast to int and then long.  However, it's simpler to store the return in
+an int ret variable.
+
+This bug would eventually result in a crash when dereference the invalid
+error pointer.
+
+Fixes: d08089f649a0 ("cifs: Change the I/O paths to use an iterator rather than a page list")
+Cc: stable@kernel.org
+Signed-off-by: Dan Carpenter <error27@gmail.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smb2ops.c |    8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/fs/smb/client/smb2ops.c
++++ b/fs/smb/client/smb2ops.c
+@@ -4314,11 +4314,13 @@ static void *smb2_aead_req_alloc(struct
+       unsigned int req_size = sizeof(**req) + crypto_aead_reqsize(tfm);
+       unsigned int iv_size = crypto_aead_ivsize(tfm);
+       unsigned int len;
++      int ret;
+       u8 *p;
+-      *num_sgs = cifs_get_num_sgs(rqst, num_rqst, sig);
+-      if (IS_ERR_VALUE((long)(int)*num_sgs))
+-              return ERR_PTR(*num_sgs);
++      ret = cifs_get_num_sgs(rqst, num_rqst, sig);
++      if (ret < 0)
++              return ERR_PTR(ret);
++      *num_sgs = ret;
+       len = iv_size;
+       len += crypto_aead_alignmask(tfm) & ~(crypto_tfm_ctx_alignment() - 1);
diff --git a/queue-6.18/smb-client-fix-next-buffer-leak-in-receive_encrypted_standard.patch b/queue-6.18/smb-client-fix-next-buffer-leak-in-receive_encrypted_standard.patch
new file mode 100644 (file)
index 0000000..5cf8ab9
--- /dev/null
@@ -0,0 +1,52 @@
+From 1c6267a1d5cf4c73b656f8181b310cbbb3e4767b Mon Sep 17 00:00:00 2001
+From: Haoxiang Li <haoxiang_li2024@163.com>
+Date: Tue, 23 Jun 2026 09:45:38 +0800
+Subject: smb: client: Fix next buffer leak in receive_encrypted_standard()
+
+From: Haoxiang Li <haoxiang_li2024@163.com>
+
+commit 1c6267a1d5cf4c73b656f8181b310cbbb3e4767b upstream.
+
+receive_encrypted_standard() allocates next_buffer before checking
+whether the number of compound PDUs already reached MAX_COMPOUND. If
+the limit check fails, the function returns immediately and the newly
+allocated next_buffer is not assigned to server->smallbuf/server->bigbuf,
+making it leaked.
+
+Move the MAX_COMPOUND check before allocating next_buffer.
+
+Fixes: b24df3e30cbf ("cifs: update receive_encrypted_standard to handle compounded responses")
+Cc: stable@vger.kernel.org
+Signed-off-by: Haoxiang Li <haoxiang_li2024@163.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smb2ops.c |   10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+--- a/fs/smb/client/smb2ops.c
++++ b/fs/smb/client/smb2ops.c
+@@ -5096,6 +5096,12 @@ receive_encrypted_standard(struct TCP_Se
+ one_more:
+       shdr = (struct smb2_hdr *)buf;
+       next_cmd = le32_to_cpu(shdr->NextCommand);
++
++      if (*num_mids >= MAX_COMPOUND) {
++              cifs_server_dbg(VFS, "too many PDUs in compound\n");
++              return -1;
++      }
++
+       if (next_cmd) {
+               if (WARN_ON_ONCE(next_cmd > pdu_length))
+                       return -1;
+@@ -5119,10 +5125,6 @@ one_more:
+               mid_entry->resp_buf_size = server->pdu_size;
+       }
+-      if (*num_mids >= MAX_COMPOUND) {
+-              cifs_server_dbg(VFS, "too many PDUs in compound\n");
+-              return -1;
+-      }
+       bufs[*num_mids] = buf;
+       mids[(*num_mids)++] = mid_entry;
diff --git a/queue-6.18/smb-client-fix-query-directory-replay-double-free.patch b/queue-6.18/smb-client-fix-query-directory-replay-double-free.patch
new file mode 100644 (file)
index 0000000..4bb2f48
--- /dev/null
@@ -0,0 +1,35 @@
+From 9647492b5e41954be59d5157eddbcd4cdc1656f7 Mon Sep 17 00:00:00 2001
+From: Henrique Carvalho <henrique.carvalho@suse.com>
+Date: Thu, 18 Jun 2026 17:34:38 -0300
+Subject: smb: client: fix query directory replay double-free
+
+From: Henrique Carvalho <henrique.carvalho@suse.com>
+
+commit 9647492b5e41954be59d5157eddbcd4cdc1656f7 upstream.
+
+A response-bearing attempt can return a replayable error and free its
+response buffer. If SMB2_query_directory_init() fails before the next send,
+cleanup retains the previous buffer type and frees that response again.
+
+Reset response bookkeeping before each attempt to prevent the stale free.
+
+Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set")
+Cc: stable@vger.kernel.org
+Signed-off-by: Henrique Carvalho <henrique.carvalho@suse.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smb2pdu.c |    2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/fs/smb/client/smb2pdu.c
++++ b/fs/smb/client/smb2pdu.c
+@@ -5668,6 +5668,8 @@ send_set_info(const unsigned int xid, st
+ replay_again:
+       /* reinitialize for possible replay */
++      resp_buftype = CIFS_NO_BUFFER;
++      memset(&rsp_iov, 0, sizeof(rsp_iov));
+       flags = 0;
+       server = cifs_pick_channel(ses);
diff --git a/queue-6.18/smb-client-fix-query_info-replay-double-free.patch b/queue-6.18/smb-client-fix-query_info-replay-double-free.patch
new file mode 100644 (file)
index 0000000..bfa65ee
--- /dev/null
@@ -0,0 +1,35 @@
+From 2a88561d66eb855813cf004a0abe648bbb17de5e Mon Sep 17 00:00:00 2001
+From: Henrique Carvalho <henrique.carvalho@suse.com>
+Date: Thu, 18 Jun 2026 17:34:36 -0300
+Subject: smb: client: fix query_info() replay double-free
+
+From: Henrique Carvalho <henrique.carvalho@suse.com>
+
+commit 2a88561d66eb855813cf004a0abe648bbb17de5e upstream.
+
+A response-bearing attempt can return a replayable error and free its
+response buffer. If SMB2_query_info_init() fails before the next send,
+cleanup retains the previous buffer type and frees that response again.
+
+Reset response bookkeeping before each attempt to prevent the stale free.
+
+Fixes: 4f1fffa23769 ("cifs: commands that are retried should have replay flag set")
+Cc: stable@vger.kernel.org
+Signed-off-by: Henrique Carvalho <henrique.carvalho@suse.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smb2pdu.c |    2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/fs/smb/client/smb2pdu.c
++++ b/fs/smb/client/smb2pdu.c
+@@ -3821,6 +3821,8 @@ query_info(const unsigned int xid, struc
+ replay_again:
+       /* reinitialize for possible replay */
++      resp_buftype = CIFS_NO_BUFFER;
++      memset(&rsp_iov, 0, sizeof(rsp_iov));
+       flags = 0;
+       allocated = false;
+       server = cifs_pick_channel(ses);
diff --git a/queue-6.18/smb-client-harden-posix-sid-length-parsing.patch b/queue-6.18/smb-client-harden-posix-sid-length-parsing.patch
new file mode 100644 (file)
index 0000000..55fafb1
--- /dev/null
@@ -0,0 +1,40 @@
+From 7ad2bcf2441430bb2e918fb3ef9a90d775a6e422 Mon Sep 17 00:00:00 2001
+From: Zihan Xi <xizh2024@lzu.edu.cn>
+Date: Sun, 28 Jun 2026 17:19:24 +0800
+Subject: smb: client: harden POSIX SID length parsing
+
+From: Zihan Xi <xizh2024@lzu.edu.cn>
+
+commit 7ad2bcf2441430bb2e918fb3ef9a90d775a6e422 upstream.
+
+posix_info_sid_size() reads sid[1] to obtain the subauthority count,
+but its existing boundary check still accepts buffers with only one
+remaining byte. Require two bytes before reading sid[1] so all client
+paths that reuse the helper reject truncated POSIX SIDs safely.
+
+Fixes: 349e13ad30b4 ("cifs: add smb2 POSIX info level")
+Cc: stable@vger.kernel.org
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Yifan Wu <yifanwucs@gmail.com>
+Reported-by: Juefei Pu <tomapufckgml@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Assisted-by: Codex:gpt-5.4
+Signed-off-by: Zihan Xi <xizh2024@lzu.edu.cn>
+Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smb2pdu.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/smb/client/smb2pdu.c
++++ b/fs/smb/client/smb2pdu.c
+@@ -5230,7 +5230,7 @@ int posix_info_sid_size(const void *beg,
+       size_t subauth;
+       int total;
+-      if (beg + 1 > end)
++      if (beg + 2 > end)
+               return -1;
+       subauth = *(u8 *)(beg+1);
diff --git a/queue-6.18/smb-client-mask-server-provided-mode-to-07777-in-modefromsid.patch b/queue-6.18/smb-client-mask-server-provided-mode-to-07777-in-modefromsid.patch
new file mode 100644 (file)
index 0000000..fd3e856
--- /dev/null
@@ -0,0 +1,34 @@
+From e3d9c7160d483fc8f9e225aafad8ecbbc43f3151 Mon Sep 17 00:00:00 2001
+From: Norbert Manthey <nmanthey@amazon.de>
+Date: Thu, 9 Jul 2026 15:54:39 +0000
+Subject: smb: client: mask server-provided mode to 07777 in modefromsid
+
+From: Norbert Manthey <nmanthey@amazon.de>
+
+commit e3d9c7160d483fc8f9e225aafad8ecbbc43f3151 upstream.
+
+When modefromsid is active, parse_dacl() applies the server-provided
+sub_auth[2] value from the NFS mode SID to cf_mode without masking to
+07777. Apply the correct masking, same as in the read path.
+
+Fixes: e2f8fbfb8d09c ("cifs: get mode bits from special sid on stat")
+Signed-off-by: Norbert Manthey <nmanthey@amazon.de>
+Assisted-by: Kiro:claude-opus-4.6
+Cc: stable@vger.kernel.org
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/cifsacl.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/smb/client/cifsacl.c
++++ b/fs/smb/client/cifsacl.c
+@@ -889,7 +889,7 @@ static void parse_dacl(struct smb_acl *p
+                                */
+                               fattr->cf_mode &= ~07777;
+                               fattr->cf_mode |=
+-                                      le32_to_cpu(ppace[i]->sid.sub_auth[2]);
++                                      le32_to_cpu(ppace[i]->sid.sub_auth[2]) & 07777;
+                               break;
+                       } else {
+                               if (compare_sids(&(ppace[i]->sid), pownersid) == 0) {
diff --git a/queue-6.18/smb-client-resolve-swn-tcon-from-live-registrations.patch b/queue-6.18/smb-client-resolve-swn-tcon-from-live-registrations.patch
new file mode 100644 (file)
index 0000000..ea96f81
--- /dev/null
@@ -0,0 +1,600 @@
+From ec457f9afe5ae9538bdcd58fd4cb442b9787e183 Mon Sep 17 00:00:00 2001
+From: Michael Bommarito <michael.bommarito@gmail.com>
+Date: Sun, 17 May 2026 20:11:49 -0400
+Subject: smb: client: resolve SWN tcon from live registrations
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+commit ec457f9afe5ae9538bdcd58fd4cb442b9787e183 upstream.
+
+cifs_swn_notify() looks up a witness registration by id under
+cifs_swnreg_idr_mutex, drops the mutex, and then uses the registration's
+cached tcon pointer.  That pointer is not a lifetime reference, and it is
+not a stable representative once cifs_get_swn_reg() lets multiple tcons
+for the same net/share name share one registration id.
+
+A same-share second mount can keep the cifs_swn_reg alive after the first
+tcon unregisters and is freed.  The registration then still points at the
+freed first tcon, so taking tc_lock or incrementing tc_count through
+swnreg->tcon only moves the use-after-free earlier.  Taking tc_lock while
+holding cifs_swnreg_idr_mutex also violates the documented CIFS lock
+order.
+
+Fix this by making the registration store only the stable witness
+identity: id, net name, share name, and notify flags.  When a notify
+arrives, copy that identity under cifs_swnreg_idr_mutex, drop the mutex,
+then find and pin a live witness tcon that currently matches the net/share
+pair under the normal cifs_tcp_ses_lock -> tc_lock order.  The notification
+path uses that pinned tcon directly and drops the reference when done.
+
+Registration and unregister messages now use the live tcon passed by the
+caller instead of a cached tcon in the registration.  The final unregister
+send is folded into cifs_swn_unregister() while the registration is still
+protected by cifs_swnreg_idr_mutex.  This removes the previous
+find/drop/reacquire raw-pointer window.  The release path only removes the
+idr entry and frees the stable identity strings.
+
+This preserves the intended one-registration/many-tcon behavior: a
+registration id represents a net/share pair, and notify handling acts on a
+live representative selected at use time.  It also preserves CLIENT_MOVE
+ordering for the representative tcon because the old-IP unregister is sent
+before cifs_swn_register() sends the new-IP register.
+
+Fixes: fed979a7e082 ("cifs: Set witness notification handler for messages from userspace daemon")
+Cc: stable@vger.kernel.org
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/cifs_swn.c |  314 ++++++++++++++++++++++++++++++++++++++---------
+ fs/smb/client/trace.h    |    2 
+ 2 files changed, 262 insertions(+), 54 deletions(-)
+
+--- a/fs/smb/client/cifs_swn.c
++++ b/fs/smb/client/cifs_swn.c
+@@ -28,10 +28,54 @@ struct cifs_swn_reg {
+       bool net_name_notify;
+       bool share_name_notify;
+       bool ip_notify;
++};
+-      struct cifs_tcon *tcon;
++struct cifs_swn_reg_info {
++      int id;
++      unsigned int ref_count;
++      const char *net_name;
++      const char *share_name;
++      bool net_name_notify;
++      bool share_name_notify;
++      bool ip_notify;
+ };
++static void cifs_swn_snapshot_reg(struct cifs_swn_reg *swnreg,
++                                struct cifs_swn_reg_info *info)
++{
++      info->id = swnreg->id;
++      info->ref_count = kref_read(&swnreg->ref_count);
++      info->net_name = swnreg->net_name;
++      info->share_name = swnreg->share_name;
++      info->net_name_notify = swnreg->net_name_notify;
++      info->share_name_notify = swnreg->share_name_notify;
++      info->ip_notify = swnreg->ip_notify;
++}
++
++static int cifs_swn_dup_reg(struct cifs_swn_reg *swnreg,
++                          struct cifs_swn_reg_info *info)
++{
++      cifs_swn_snapshot_reg(swnreg, info);
++
++      info->net_name = kstrdup(swnreg->net_name, GFP_KERNEL);
++      if (!info->net_name)
++              return -ENOMEM;
++
++      info->share_name = kstrdup(swnreg->share_name, GFP_KERNEL);
++      if (!info->share_name) {
++              kfree(info->net_name);
++              return -ENOMEM;
++      }
++
++      return 0;
++}
++
++static void cifs_swn_free_reg_info(struct cifs_swn_reg_info *info)
++{
++      kfree(info->net_name);
++      kfree(info->share_name);
++}
++
+ static int cifs_swn_auth_info_krb(struct cifs_tcon *tcon, struct sk_buff *skb)
+ {
+       int ret;
+@@ -73,7 +117,8 @@ static int cifs_swn_auth_info_ntlm(struc
+  * The authentication information to connect to the witness service is bundled
+  * into the message.
+  */
+-static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg)
++static int cifs_swn_send_register_message(struct cifs_swn_reg_info *swnreg,
++                                        struct cifs_tcon *tcon)
+ {
+       struct sk_buff *skb;
+       struct genlmsghdr *hdr;
+@@ -109,10 +154,10 @@ static int cifs_swn_send_register_messag
+        * told to switch to it (client move message). In these cases we unregister from the
+        * server address and register to the new address when we receive the notification.
+        */
+-      if (swnreg->tcon->ses->server->use_swn_dstaddr)
+-              addr = &swnreg->tcon->ses->server->swn_dstaddr;
++      if (tcon->ses->server->use_swn_dstaddr)
++              addr = &tcon->ses->server->swn_dstaddr;
+       else
+-              addr = &swnreg->tcon->ses->server->dstaddr;
++              addr = &tcon->ses->server->dstaddr;
+       ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), addr);
+       if (ret < 0)
+@@ -136,10 +181,10 @@ static int cifs_swn_send_register_messag
+                       goto nlmsg_fail;
+       }
+-      authtype = cifs_select_sectype(swnreg->tcon->ses->server, swnreg->tcon->ses->sectype);
++      authtype = cifs_select_sectype(tcon->ses->server, tcon->ses->sectype);
+       switch (authtype) {
+       case Kerberos:
+-              ret = cifs_swn_auth_info_krb(swnreg->tcon, skb);
++              ret = cifs_swn_auth_info_krb(tcon, skb);
+               if (ret < 0) {
+                       cifs_dbg(VFS, "%s: Failed to get kerberos auth info: %d\n", __func__, ret);
+                       goto nlmsg_fail;
+@@ -147,7 +192,7 @@ static int cifs_swn_send_register_messag
+               break;
+       case NTLMv2:
+       case RawNTLMSSP:
+-              ret = cifs_swn_auth_info_ntlm(swnreg->tcon, skb);
++              ret = cifs_swn_auth_info_ntlm(tcon, skb);
+               if (ret < 0) {
+                       cifs_dbg(VFS, "%s: Failed to get NTLM auth info: %d\n", __func__, ret);
+                       goto nlmsg_fail;
+@@ -176,7 +221,8 @@ nlmsg_fail:
+ /*
+  * Sends an uregister message to the userspace daemon based on the registration
+  */
+-static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg)
++static int cifs_swn_send_unregister_message(struct cifs_swn_reg_info *swnreg,
++                                          struct cifs_tcon *tcon)
+ {
+       struct sk_buff *skb;
+       struct genlmsghdr *hdr;
+@@ -205,7 +251,7 @@ static int cifs_swn_send_unregister_mess
+               goto nlmsg_fail;
+       ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage),
+-                      &swnreg->tcon->ses->server->dstaddr);
++                      &tcon->ses->server->dstaddr);
+       if (ret < 0)
+               goto nlmsg_fail;
+@@ -242,6 +288,88 @@ nlmsg_fail:
+ }
+ /*
++ * Allocation-free mirror of extract_hostname() + extract_sharename() from
++ * fs/smb/client/unc.c.  Those helpers kmalloc(GFP_KERNEL); this runs under
++ * cifs_tcp_ses_lock and tcon->tc_lock, both spinlocks, so we mirror their
++ * parsing in place against the caller's stable net_name/share_name strings.
++ * Keep in sync with unc.c.
++ */
++static bool cifs_swn_tcon_matches(struct cifs_tcon *tcon,
++                                const char *net_name,
++                                const char *share_name)
++{
++      const char *unc = tcon->tree_name;
++      const char *host, *share, *delim;
++      size_t host_len, share_len;
++
++      if (!tcon->use_witness)
++              return false;
++
++      /* extract_hostname: require strlen(unc) >= 3 */
++      if (strnlen(unc, 3) < 3)
++              return false;
++      /* extract_hostname: skip all leading '\' characters */
++      for (host = unc; *host == '\\'; host++)
++              ;
++      if (!*host)
++              return false;
++      delim = strchr(host, '\\');
++      if (!delim)
++              return false;
++      host_len = delim - host;
++      if (strlen(net_name) != host_len ||
++          strncasecmp(host, net_name, host_len))
++              return false;
++
++      /* extract_sharename: start at unc + 2, then first '\' onward */
++      share = unc + 2;
++      delim = strchr(share, '\\');
++      if (!delim)
++              return false;
++      share = delim + 1;
++      share_len = strlen(share);
++
++      return strlen(share_name) == share_len &&
++             !strncasecmp(share, share_name, share_len);
++}
++
++/*
++ * One SWN registration id represents one net/share name pair.  Multiple
++ * mounted tcons can therefore share the id.  Pick a live representative at
++ * use time instead of caching the first tcon pointer in the registration.
++ */
++static struct cifs_tcon *cifs_swn_get_tcon(struct cifs_swn_reg_info *swnreg)
++{
++      struct TCP_Server_Info *server;
++      struct cifs_ses *ses;
++      struct cifs_tcon *tcon;
++
++      spin_lock(&cifs_tcp_ses_lock);
++      list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
++              list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
++                      list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
++                              spin_lock(&tcon->tc_lock);
++                              if (tcon->status == TID_EXITING ||
++                                  !cifs_swn_tcon_matches(tcon, swnreg->net_name,
++                                                         swnreg->share_name)) {
++                                      spin_unlock(&tcon->tc_lock);
++                                      continue;
++                              }
++                              ++tcon->tc_count;
++                              trace_smb3_tcon_ref(tcon->debug_id,
++                                                  tcon->tc_count,
++                                                  netfs_trace_tcon_ref_get_swn_notify);
++                              spin_unlock(&tcon->tc_lock);
++                              spin_unlock(&cifs_tcp_ses_lock);
++                              return tcon;
++                      }
++              }
++      }
++      spin_unlock(&cifs_tcp_ses_lock);
++      return NULL;
++}
++
++/*
+  * Try to find a matching registration for the tcon's server name and share name.
+  * Calls to this function must be protected by cifs_swnreg_idr_mutex.
+  * TODO Try to avoid memory allocations
+@@ -347,8 +475,6 @@ static struct cifs_swn_reg *cifs_get_swn
+       reg->net_name_notify = true;
+       reg->share_name_notify = true;
+       reg->ip_notify = (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT);
+-
+-      reg->tcon = tcon;
+ unlock:
+       mutex_unlock(&cifs_swnreg_idr_mutex);
+@@ -368,11 +494,6 @@ fail_unlock:
+ static void cifs_swn_reg_release(struct kref *ref)
+ {
+       struct cifs_swn_reg *swnreg = container_of(ref, struct cifs_swn_reg, ref_count);
+-      int ret;
+-
+-      ret = cifs_swn_send_unregister_message(swnreg);
+-      if (ret < 0)
+-              cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n", __func__, ret);
+       idr_remove(&cifs_swnreg_idr, swnreg->id);
+       kfree(swnreg->net_name);
+@@ -380,23 +501,33 @@ static void cifs_swn_reg_release(struct
+       kfree(swnreg);
+ }
+-static void cifs_put_swn_reg(struct cifs_swn_reg *swnreg)
++static void cifs_put_swn_reg_locked(struct cifs_swn_reg *swnreg,
++                                  struct cifs_tcon *tcon)
+ {
+-      mutex_lock(&cifs_swnreg_idr_mutex);
++      if (kref_read(&swnreg->ref_count) == 1) {
++              struct cifs_swn_reg_info swnreg_info;
++              int ret;
++
++              cifs_swn_snapshot_reg(swnreg, &swnreg_info);
++              ret = cifs_swn_send_unregister_message(&swnreg_info, tcon);
++              if (ret < 0)
++                      cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n",
++                               __func__, ret);
++      }
++
+       kref_put(&swnreg->ref_count, cifs_swn_reg_release);
+-      mutex_unlock(&cifs_swnreg_idr_mutex);
+ }
+-static int cifs_swn_resource_state_changed(struct cifs_swn_reg *swnreg, const char *name, int state)
++static int cifs_swn_resource_state_changed(struct cifs_tcon *tcon, const char *name, int state)
+ {
+       switch (state) {
+       case CIFS_SWN_RESOURCE_STATE_UNAVAILABLE:
+               cifs_dbg(FYI, "%s: resource name '%s' become unavailable\n", __func__, name);
+-              cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true);
++              cifs_signal_cifsd_for_reconnect(tcon->ses->server, true);
+               break;
+       case CIFS_SWN_RESOURCE_STATE_AVAILABLE:
+               cifs_dbg(FYI, "%s: resource name '%s' become available\n", __func__, name);
+-              cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true);
++              cifs_signal_cifsd_for_reconnect(tcon->ses->server, true);
+               break;
+       case CIFS_SWN_RESOURCE_STATE_UNKNOWN:
+               cifs_dbg(FYI, "%s: resource name '%s' changed to unknown state\n", __func__, name);
+@@ -502,7 +633,7 @@ unlock:
+       return ret;
+ }
+-static int cifs_swn_client_move(struct cifs_swn_reg *swnreg, struct sockaddr_storage *addr)
++static int cifs_swn_client_move(struct cifs_tcon *tcon, struct sockaddr_storage *addr)
+ {
+       struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr;
+       struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)addr;
+@@ -512,14 +643,17 @@ static int cifs_swn_client_move(struct c
+       else if (addr->ss_family == AF_INET6)
+               cifs_dbg(FYI, "%s: move to %pI6\n", __func__, &ipv6->sin6_addr);
+-      return cifs_swn_reconnect(swnreg->tcon, addr);
++      return cifs_swn_reconnect(tcon, addr);
+ }
+ int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info)
+ {
+       struct cifs_swn_reg *swnreg;
++      struct cifs_swn_reg_info swnreg_info;
++      struct cifs_tcon *tcon;
+       char name[256];
+       int type;
++      int ret = 0;
+       if (info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]) {
+               int swnreg_id;
+@@ -527,21 +661,34 @@ int cifs_swn_notify(struct sk_buff *skb,
+               swnreg_id = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]);
+               mutex_lock(&cifs_swnreg_idr_mutex);
+               swnreg = idr_find(&cifs_swnreg_idr, swnreg_id);
+-              mutex_unlock(&cifs_swnreg_idr_mutex);
+               if (swnreg == NULL) {
++                      mutex_unlock(&cifs_swnreg_idr_mutex);
+                       cifs_dbg(FYI, "%s: registration id %d not found\n", __func__, swnreg_id);
+                       return -EINVAL;
+               }
++              ret = cifs_swn_dup_reg(swnreg, &swnreg_info);
++              mutex_unlock(&cifs_swnreg_idr_mutex);
++              if (ret)
++                      return ret;
+       } else {
+               cifs_dbg(FYI, "%s: missing registration id attribute\n", __func__);
+               return -EINVAL;
+       }
++      tcon = cifs_swn_get_tcon(&swnreg_info);
++      if (!tcon) {
++              cifs_dbg(FYI, "%s: registration id %d has no live tcon\n",
++                       __func__, swnreg_info.id);
++              ret = -ENODEV;
++              goto free_info;
++      }
++
+       if (info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]) {
+               type = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]);
+       } else {
+               cifs_dbg(FYI, "%s: missing notification type attribute\n", __func__);
+-              return -EINVAL;
++              ret = -EINVAL;
++              goto out;
+       }
+       switch (type) {
+@@ -553,15 +700,18 @@ int cifs_swn_notify(struct sk_buff *skb,
+                                       sizeof(name));
+               } else {
+                       cifs_dbg(FYI, "%s: missing resource name attribute\n", __func__);
+-                      return -EINVAL;
++                      ret = -EINVAL;
++                      goto out;
+               }
+               if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]) {
+                       state = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]);
+               } else {
+                       cifs_dbg(FYI, "%s: missing resource state attribute\n", __func__);
+-                      return -EINVAL;
++                      ret = -EINVAL;
++                      goto out;
+               }
+-              return cifs_swn_resource_state_changed(swnreg, name, state);
++              ret = cifs_swn_resource_state_changed(tcon, name, state);
++              break;
+       }
+       case CIFS_SWN_NOTIFICATION_CLIENT_MOVE: {
+               struct sockaddr_storage addr;
+@@ -570,28 +720,36 @@ int cifs_swn_notify(struct sk_buff *skb,
+                       nla_memcpy(&addr, info->attrs[CIFS_GENL_ATTR_SWN_IP], sizeof(addr));
+               } else {
+                       cifs_dbg(FYI, "%s: missing IP address attribute\n", __func__);
+-                      return -EINVAL;
++                      ret = -EINVAL;
++                      goto out;
+               }
+-              return cifs_swn_client_move(swnreg, &addr);
++              ret = cifs_swn_client_move(tcon, &addr);
++              break;
+       }
+       default:
+               cifs_dbg(FYI, "%s: unknown notification type %d\n", __func__, type);
+               break;
+       }
+-      return 0;
++out:
++      cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_swn_notify);
++free_info:
++      cifs_swn_free_reg_info(&swnreg_info);
++      return ret;
+ }
+ int cifs_swn_register(struct cifs_tcon *tcon)
+ {
+       struct cifs_swn_reg *swnreg;
++      struct cifs_swn_reg_info swnreg_info;
+       int ret;
+       swnreg = cifs_get_swn_reg(tcon);
+       if (IS_ERR(swnreg))
+               return PTR_ERR(swnreg);
+-      ret = cifs_swn_send_register_message(swnreg);
++      cifs_swn_snapshot_reg(swnreg, &swnreg_info);
++      ret = cifs_swn_send_register_message(&swnreg_info, tcon);
+       if (ret < 0) {
+               cifs_dbg(VFS, "%s: Failed to send swn register message: %d\n", __func__, ret);
+               /* Do not put the swnreg or return error, the echo task will retry */
+@@ -612,35 +770,68 @@ int cifs_swn_unregister(struct cifs_tcon
+               return PTR_ERR(swnreg);
+       }
++      cifs_put_swn_reg_locked(swnreg, tcon);
+       mutex_unlock(&cifs_swnreg_idr_mutex);
+-      cifs_put_swn_reg(swnreg);
+-
+       return 0;
+ }
+-void cifs_swn_dump(struct seq_file *m)
++/*
++ * Snapshot one registration under cifs_swnreg_idr_mutex and return.  Callers
++ * intentionally do the per-registration network/genlmsg work without the
++ * mutex held, both to keep the critical section short and to avoid nesting
++ * cifs_swnreg_idr_mutex inside the higher tc_lock when a live tcon is then
++ * pinned for the send.
++ */
++static int cifs_swn_get_next_reg_info(int *id, struct cifs_swn_reg_info *info)
+ {
+       struct cifs_swn_reg *swnreg;
++      int ret = 0;
++
++      mutex_lock(&cifs_swnreg_idr_mutex);
++      swnreg = idr_get_next(&cifs_swnreg_idr, id);
++      if (swnreg) {
++              ret = cifs_swn_dup_reg(swnreg, info);
++              if (!ret) {
++                      *id = swnreg->id + 1;
++                      ret = 1;
++              }
++      }
++      mutex_unlock(&cifs_swnreg_idr_mutex);
++
++      return ret;
++}
++
++void cifs_swn_dump(struct seq_file *m)
++{
++      struct cifs_swn_reg_info swnreg_info;
++      struct cifs_tcon *tcon;
+       struct sockaddr_in *sa;
+       struct sockaddr_in6 *sa6;
+-      int id;
++      int id = 0;
++      int ret;
+       seq_puts(m, "Witness registrations:");
+-      mutex_lock(&cifs_swnreg_idr_mutex);
+-      idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
++      while ((ret = cifs_swn_get_next_reg_info(&id, &swnreg_info)) > 0) {
+               seq_printf(m, "\nId: %u Refs: %u Network name: '%s'%s Share name: '%s'%s Ip address: ",
+-                              id, kref_read(&swnreg->ref_count),
+-                              swnreg->net_name, swnreg->net_name_notify ? "(y)" : "(n)",
+-                              swnreg->share_name, swnreg->share_name_notify ? "(y)" : "(n)");
+-              switch (swnreg->tcon->ses->server->dstaddr.ss_family) {
++                         swnreg_info.id, swnreg_info.ref_count,
++                         swnreg_info.net_name, swnreg_info.net_name_notify ? "(y)" : "(n)",
++                         swnreg_info.share_name, swnreg_info.share_name_notify ? "(y)" : "(n)");
++
++              tcon = cifs_swn_get_tcon(&swnreg_info);
++              if (!tcon) {
++                      seq_puts(m, "(no live tcon)");
++                      goto next;
++              }
++
++              switch (tcon->ses->server->dstaddr.ss_family) {
+               case AF_INET:
+-                      sa = (struct sockaddr_in *) &swnreg->tcon->ses->server->dstaddr;
++                      sa = (struct sockaddr_in *)&tcon->ses->server->dstaddr;
+                       seq_printf(m, "%pI4", &sa->sin_addr.s_addr);
+                       break;
+               case AF_INET6:
+-                      sa6 = (struct sockaddr_in6 *) &swnreg->tcon->ses->server->dstaddr;
++                      sa6 = (struct sockaddr_in6 *)&tcon->ses->server->dstaddr;
+                       seq_printf(m, "%pI6", &sa6->sin6_addr.s6_addr);
+                       if (sa6->sin6_scope_id)
+                               seq_printf(m, "%%%u", sa6->sin6_scope_id);
+@@ -648,23 +839,38 @@ void cifs_swn_dump(struct seq_file *m)
+               default:
+                       seq_puts(m, "(unknown)");
+               }
+-              seq_printf(m, "%s", swnreg->ip_notify ? "(y)" : "(n)");
++              cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_swn_notify);
++next:
++              seq_printf(m, "%s", swnreg_info.ip_notify ? "(y)" : "(n)");
++              cifs_swn_free_reg_info(&swnreg_info);
+       }
+-      mutex_unlock(&cifs_swnreg_idr_mutex);
++      if (ret < 0)
++              seq_printf(m, "\nFailed to snapshot witness registration: %d", ret);
+       seq_puts(m, "\n");
+ }
+ void cifs_swn_check(void)
+ {
+-      struct cifs_swn_reg *swnreg;
+-      int id;
++      struct cifs_swn_reg_info swnreg_info;
++      struct cifs_tcon *tcon;
++      int id = 0;
+       int ret;
+-      mutex_lock(&cifs_swnreg_idr_mutex);
+-      idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
+-              ret = cifs_swn_send_register_message(swnreg);
++      while ((ret = cifs_swn_get_next_reg_info(&id, &swnreg_info)) > 0) {
++              tcon = cifs_swn_get_tcon(&swnreg_info);
++              if (!tcon) {
++                      cifs_dbg(FYI, "%s: registration id %d has no live tcon\n",
++                               __func__, swnreg_info.id);
++                      goto free_info;
++              }
++
++              ret = cifs_swn_send_register_message(&swnreg_info, tcon);
+               if (ret < 0)
+                       cifs_dbg(FYI, "%s: Failed to send register message: %d\n", __func__, ret);
++              cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_swn_notify);
++free_info:
++              cifs_swn_free_reg_info(&swnreg_info);
+       }
+-      mutex_unlock(&cifs_swnreg_idr_mutex);
++      if (ret < 0)
++              cifs_dbg(FYI, "%s: Failed to snapshot registration: %d\n", __func__, ret);
+ }
+--- a/fs/smb/client/trace.h
++++ b/fs/smb/client/trace.h
+@@ -52,6 +52,7 @@
+       EM(netfs_trace_tcon_ref_get_find,               "GET Find  ") \
+       EM(netfs_trace_tcon_ref_get_find_sess_tcon,     "GET FndSes") \
+       EM(netfs_trace_tcon_ref_get_reconnect_server,   "GET Reconn") \
++      EM(netfs_trace_tcon_ref_get_swn_notify,         "GET SwnNot") \
+       EM(netfs_trace_tcon_ref_new,                    "NEW       ") \
+       EM(netfs_trace_tcon_ref_new_ipc,                "NEW Ipc   ") \
+       EM(netfs_trace_tcon_ref_new_reconnect_server,   "NEW Reconn") \
+@@ -63,6 +64,7 @@
+       EM(netfs_trace_tcon_ref_put_mnt_ctx,            "PUT MntCtx") \
+       EM(netfs_trace_tcon_ref_put_dfs_refer,          "PUT DfsRfr") \
+       EM(netfs_trace_tcon_ref_put_reconnect_server,   "PUT Reconn") \
++      EM(netfs_trace_tcon_ref_put_swn_notify,         "PUT SwnNot") \
+       EM(netfs_trace_tcon_ref_put_tlink,              "PUT Tlink ") \
+       EM(netfs_trace_tcon_ref_see_cancelled_close,    "SEE Cn-Cls") \
+       EM(netfs_trace_tcon_ref_see_fscache_collision,  "SEE FV-CO!") \
diff --git a/queue-6.18/smb-client-use-unaligned-reads-in-parse_posix_ctxt.patch b/queue-6.18/smb-client-use-unaligned-reads-in-parse_posix_ctxt.patch
new file mode 100644 (file)
index 0000000..88270c4
--- /dev/null
@@ -0,0 +1,38 @@
+From b86467cd2691192ad4809a5a6e922fc24b8e9839 Mon Sep 17 00:00:00 2001
+From: Zihan Xi <xizh2024@lzu.edu.cn>
+Date: Wed, 1 Jul 2026 18:23:21 +0800
+Subject: smb: client: use unaligned reads in parse_posix_ctxt()
+
+From: Zihan Xi <xizh2024@lzu.edu.cn>
+
+commit b86467cd2691192ad4809a5a6e922fc24b8e9839 upstream.
+
+The server controls create-context DataOffset, so the POSIX context data
+pointer may be misaligned on strict-alignment architectures. Use
+get_unaligned_le32() when reading nlink, reparse_tag, and mode.
+
+Fixes: 69dda3059e7a ("cifs: add SMB2_open() arg to return POSIX data")
+Cc: stable@vger.kernel.org
+Signed-off-by: Zihan Xi <xizh2024@lzu.edu.cn>
+Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smb2pdu.c |    6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/fs/smb/client/smb2pdu.c
++++ b/fs/smb/client/smb2pdu.c
+@@ -2297,9 +2297,9 @@ parse_posix_ctxt(struct create_context *
+       memset(posix, 0, sizeof(*posix));
+-      posix->nlink = le32_to_cpu(*(__le32 *)(beg + 0));
+-      posix->reparse_tag = le32_to_cpu(*(__le32 *)(beg + 4));
+-      posix->mode = le32_to_cpu(*(__le32 *)(beg + 8));
++      posix->nlink = get_unaligned_le32(beg);
++      posix->reparse_tag = get_unaligned_le32(beg + 4);
++      posix->mode = get_unaligned_le32(beg + 8);
+       sid = beg + 12;
+       sid_len = posix_info_sid_size(sid, end);