--- /dev/null
+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
+@@ -8487,6 +8487,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)
--- /dev/null
+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
+@@ -6615,6 +6615,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);
+ }
--- /dev/null
+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
+@@ -6564,6 +6564,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 -EMSGSIZE;
+
--- /dev/null
+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
+@@ -8562,6 +8562,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);
--- /dev/null
+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
+@@ -5337,6 +5337,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)
--- /dev/null
+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
+@@ -7746,29 +7746,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);
--- /dev/null
+From 54bab9ba5a9f156ffa9324fcbe5a356fd0242f95 Mon Sep 17 00:00:00 2001
+From: Namjae Jeon <linkinjeon@kernel.org>
+Date: Sun, 7 Jun 2026 20:15:51 +0900
+Subject: ksmbd: prevent path traversal bypass by restricting caseless retry
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+commit 54bab9ba5a9f156ffa9324fcbe5a356fd0242f95 upstream.
+
+ksmbd_vfs_path_lookup() enforces LOOKUP_BENEATH to restrict path
+resolution within the share root. When a crafted path attempts to
+escape the share boundary using parent-directory components ('..'),
+vfs_path_parent_lookup() detects this and immediately fails,
+returning -EXDEV.
+
+However, a bug exists in __ksmbd_vfs_kern_path() under caseless mode.
+The function fails to intercept the -EXDEV error and erroneously
+falls through to the caseless retry logic, which is intended only
+for genuinely missing files. During this retry process, the path
+is reconstructed, leading to an unintended LOOKUP_BENEATH bypass
+that allows write-capable users to create zero-length files or
+directories outside the exported share.
+
+Fix this by ensuring that the execution only proceeds to the caseless
+lookup retry when the error is specifically -ENOENT. Any other errors,
+such as -EXDEV from a path traversal attempt, must be returned immediately.
+
+Cc: stable@vger.kernel.org
+Reported-by: Y s65 <yu4ys@outlook.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 | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -1149,7 +1149,7 @@ int __ksmbd_vfs_kern_path(struct ksmbd_w
+
+ retry:
+ err = ksmbd_vfs_path_lookup(share_conf, filepath, flags, path, for_remove);
+- if (!err || !caseless)
++ if (!err || err != -ENOENT || !caseless)
+ return err;
+
+ path_len = strlen(filepath);
--- /dev/null
+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
+@@ -8580,6 +8580,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);
--- /dev/null
+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
+@@ -6630,6 +6630,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;
+@@ -6671,6 +6672,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");
+@@ -6678,19 +6680,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;
--- /dev/null
+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
+@@ -4470,6 +4470,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);
+@@ -4574,6 +4576,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);
+@@ -4581,6 +4584,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
+@@ -790,6 +790,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;
--- /dev/null
+From c1016dd1d8b2bcd1158bbaabe94a31bb7e7431fb Mon Sep 17 00:00:00 2001
+From: Namjae Jeon <linkinjeon@kernel.org>
+Date: Mon, 15 Jun 2026 10:00:00 +0900
+Subject: ksmbd: track the connection owning a byte-range lock
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+commit c1016dd1d8b2bcd1158bbaabe94a31bb7e7431fb upstream.
+
+SMB2_LOCK adds each granted byte-range lock to both the file lock list
+and the lock list of the connection which handled the request. The
+final close and durable handle paths, however, remove the connection
+list entry while holding fp->conn->llist_lock.
+
+With SMB3 multichannel, the connection handling the LOCK request can be
+different from the connection which opened the file. The entry can
+therefore be removed under a different spinlock from the one protecting
+the list it belongs to. A concurrent traversal can then access freed
+struct ksmbd_lock and struct file_lock objects.
+
+Record the connection owning each lock's clist entry and hold a
+reference to it while the entry is linked. Use that connection and its
+llist_lock for unlock, rollback, close, and durable preserve. Durable
+reconnect assigns the new connection as the owner when publishing the
+locks again.
+
+Fixes: f5a544e3bab7 ("ksmbd: add support for SMB3 multichannel")
+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 | 10 ++++++++--
+ fs/smb/server/vfs_cache.c | 23 +++++++++++++++++------
+ fs/smb/server/vfs_cache.h | 1 +
+ 3 files changed, 26 insertions(+), 8 deletions(-)
+
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -7644,9 +7644,11 @@ int smb2_lock(struct ksmbd_work *work)
+ nolock = 0;
+ list_del(&cmp_lock->flist);
+ list_del(&cmp_lock->clist);
++ cmp_lock->conn = NULL;
+ spin_unlock(&conn->llist_lock);
+ up_read(&conn_list_lock);
+
++ ksmbd_conn_put(conn);
+ locks_free_lock(cmp_lock->fl);
+ kfree(cmp_lock);
+ goto out_check_cl;
+@@ -7781,6 +7783,7 @@ skip:
+ goto out2;
+ } else if (!rc) {
+ list_add(&smb_lock->llist, &rollback_list);
++ smb_lock->conn = ksmbd_conn_get(work->conn);
+ spin_lock(&work->conn->llist_lock);
+ list_add_tail(&smb_lock->clist,
+ &work->conn->lock_list);
+@@ -7835,11 +7838,14 @@ out:
+ }
+
+ list_del(&smb_lock->llist);
+- spin_lock(&work->conn->llist_lock);
++ conn = smb_lock->conn;
++ spin_lock(&conn->llist_lock);
+ if (!list_empty(&smb_lock->flist))
+ list_del(&smb_lock->flist);
+ list_del(&smb_lock->clist);
+- spin_unlock(&work->conn->llist_lock);
++ smb_lock->conn = NULL;
++ spin_unlock(&conn->llist_lock);
++ ksmbd_conn_put(conn);
+
+ locks_free_lock(smb_lock->fl);
+ if (rlock)
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -484,10 +484,14 @@ static void __ksmbd_close_fd(struct ksmb
+ * there are not accesses to fp->lock_list.
+ */
+ list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) {
+- if (!list_empty(&smb_lock->clist) && fp->conn) {
+- spin_lock(&fp->conn->llist_lock);
+- list_del(&smb_lock->clist);
+- spin_unlock(&fp->conn->llist_lock);
++ struct ksmbd_conn *conn = smb_lock->conn;
++
++ if (conn) {
++ spin_lock(&conn->llist_lock);
++ list_del_init(&smb_lock->clist);
++ smb_lock->conn = NULL;
++ spin_unlock(&conn->llist_lock);
++ ksmbd_conn_put(conn);
+ }
+
+ list_del(&smb_lock->flist);
+@@ -1303,9 +1307,15 @@ static bool session_fd_check(struct ksmb
+ up_write(&ci->m_lock);
+
+ list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) {
+- spin_lock(&conn->llist_lock);
++ struct ksmbd_conn *lock_conn = smb_lock->conn;
++
++ if (!lock_conn)
++ continue;
++ spin_lock(&lock_conn->llist_lock);
+ list_del_init(&smb_lock->clist);
+- spin_unlock(&conn->llist_lock);
++ smb_lock->conn = NULL;
++ spin_unlock(&lock_conn->llist_lock);
++ ksmbd_conn_put(lock_conn);
+ }
+
+ fp->conn = NULL;
+@@ -1435,6 +1445,7 @@ int ksmbd_reopen_durable_fd(struct ksmbd
+ }
+
+ list_for_each_entry(smb_lock, &fp->lock_list, flist) {
++ smb_lock->conn = ksmbd_conn_get(conn);
+ spin_lock(&conn->llist_lock);
+ list_add_tail(&smb_lock->clist, &conn->lock_list);
+ spin_unlock(&conn->llist_lock);
+--- a/fs/smb/server/vfs_cache.h
++++ b/fs/smb/server/vfs_cache.h
+@@ -32,6 +32,7 @@ struct ksmbd_session;
+
+ struct ksmbd_lock {
+ struct file_lock *fl;
++ struct ksmbd_conn *conn;
+ struct list_head clist;
+ struct list_head flist;
+ struct list_head llist;
--- /dev/null
+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
+@@ -254,17 +254,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;
+
+@@ -388,6 +391,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;
+@@ -408,6 +412,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,
+@@ -416,14 +421,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)
+@@ -441,6 +446,8 @@ static int ksmbd_vfs_stream_write(struct
+ size,
+ 0,
+ true);
++out_revert:
++ revert_creds(saved_cred);
+ if (err < 0)
+ goto out;
+ else
--- /dev/null
+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
+@@ -1009,13 +1009,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);
+ dentry = start_removing_dentry(dir, dentry);
+@@ -1034,7 +1036,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
+@@ -385,10 +385,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);
--- /dev/null
+From 954d196bebb2b50151cb96454c72dc113b2af1ac Mon Sep 17 00:00:00 2001
+From: Haofeng Li <lihaofeng@kylinos.cn>
+Date: Tue, 23 Jun 2026 09:30:26 +0800
+Subject: ksmbd: validate NTLMv2 response before updating session key
+
+From: Haofeng Li <lihaofeng@kylinos.cn>
+
+commit 954d196bebb2b50151cb96454c72dc113b2af1ac upstream.
+
+ksmbd_auth_ntlmv2() derives the NTLMv2 session key into
+sess->sess_key before it verifies the NTLMv2 response.
+ksmbd_decode_ntlmssp_auth_blob() then continues into KEY_XCH even
+when ksmbd_auth_ntlmv2() failed.
+
+With SMB3 multichannel binding, the failed authentication operates on
+an existing session and the session setup error path does not expire
+binding sessions. A client can send a binding session setup with a
+bad NT proof and KEY_XCH and still modify sess->sess_key before
+STATUS_LOGON_FAILURE is returned.
+
+Relevant path:
+
+ smb2_sess_setup()
+ -> conn->binding = true
+ -> ntlm_authenticate()
+ -> session_user()
+ -> ksmbd_decode_ntlmssp_auth_blob()
+ -> ksmbd_auth_ntlmv2()
+ -> calc_ntlmv2_hash()
+ -> hmac_md5_usingrawkey(..., sess->sess_key)
+ -> crypto_memneq() returns mismatch
+ -> KEY_XCH arc4_crypt(..., sess->sess_key, ...)
+ -> out_err without expiring the binding session
+
+Derive the base session key into a local buffer and copy it to
+sess->sess_key only after the proof matches. Return immediately on
+authentication failure so KEY_XCH is only processed after successful
+authentication.
+
+Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3")
+Fixes: f9929ef6a2a5 ("ksmbd: add support for key exchange")
+Cc: stable@vger.kernel.org
+Signed-off-by: Haofeng Li <lihaofeng@kylinos.cn>
+Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
+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/auth.c | 20 ++++++++++++++++----
+ 1 file changed, 16 insertions(+), 4 deletions(-)
+
+--- a/fs/smb/server/auth.c
++++ b/fs/smb/server/auth.c
+@@ -142,6 +142,7 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn
+ {
+ char ntlmv2_hash[CIFS_ENCPWD_SIZE];
+ char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE];
++ char sess_key[SMB2_NTLMV2_SESSKEY_SIZE];
+ struct hmac_md5_ctx ctx;
+ int rc;
+
+@@ -164,12 +165,21 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn
+ /* Generate the session key */
+ hmac_md5_usingrawkey(ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE,
+ ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE,
+- sess->sess_key);
++ sess_key);
+
+ if (crypto_memneq(ntlmv2->ntlmv2_hash, ntlmv2_rsp,
+- CIFS_HMAC_MD5_HASH_SIZE))
+- return -EINVAL;
+- return 0;
++ CIFS_HMAC_MD5_HASH_SIZE)) {
++ rc = -EINVAL;
++ goto out;
++ }
++
++ memcpy(sess->sess_key, sess_key, sizeof(sess_key));
++ rc = 0;
++out:
++ memzero_explicit(ntlmv2_hash, sizeof(ntlmv2_hash));
++ memzero_explicit(ntlmv2_rsp, sizeof(ntlmv2_rsp));
++ memzero_explicit(sess_key, sizeof(sess_key));
++ return rc;
+ }
+
+ /**
+@@ -226,6 +236,8 @@ int ksmbd_decode_ntlmssp_auth_blob(struc
+ nt_len - CIFS_ENCPWD_SIZE,
+ domain_name, conn->ntlmssp.cryptkey);
+ kfree(domain_name);
++ if (ret)
++ return ret;
+
+ /* The recovered secondary session key */
+ if (conn->ntlmssp.client_flags & NTLMSSP_NEGOTIATE_KEY_XCH) {
bluetooth-l2cap-cancel-pending_rx_work-before-taking-conn-lock.patch
bluetooth-l2cap-validate-option-length-before-reading-conf-opt-value.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-prevent-path-traversal-bypass-by-restricting-caseless-retry.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
+ksmbd-track-the-connection-owning-a-byte-range-lock.patch
+ksmbd-validate-ntlmv2-response-before-updating-session-key.patch
+smb-client-fix-chown-chgrp-with-smb3-posix-extensions.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
--- /dev/null
+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
+@@ -301,7 +301,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));
+ }
+
--- /dev/null
+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
+@@ -4087,6 +4087,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);
+
--- /dev/null
+From 760ef2c579c2609cf17fb1cd5392f64d42d43d33 Mon Sep 17 00:00:00 2001
+From: Ralph Boehme <slow@samba.org>
+Date: Sun, 7 Jun 2026 15:23:18 +0200
+Subject: smb/client: fix chown/chgrp with SMB3 POSIX Extensions
+
+From: Ralph Boehme <slow@samba.org>
+
+commit 760ef2c579c2609cf17fb1cd5392f64d42d43d33 upstream.
+
+Ownership (chown) and group (chgrp) modifications were being ignored when
+mounting with SMB3 POSIX Extensions unless CIFS_MOUNT_CIFS_ACL or
+CIFS_MOUNT_MODE_FROM_SID were also explicitly set.
+
+Fix this by checking for posix_extensions in cifs_setattr_nounix() when
+updating UID and GID, ensuring that id_mode_to_cifs_acl() is called to map
+and set the ownership/group information on the server.
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Ralph Boehme <slow@samba.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/inode.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/fs/smb/client/inode.c
++++ b/fs/smb/client/inode.c
+@@ -3348,7 +3348,8 @@ cifs_setattr_nounix(struct dentry *diren
+ if (attrs->ia_valid & ATTR_GID)
+ gid = attrs->ia_gid;
+
+- if (sbflags & (CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_MODE_FROM_SID)) {
++ if ((sbflags & (CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_MODE_FROM_SID)) ||
++ cifs_sb_master_tcon(cifs_sb)->posix_extensions) {
+ if (uid_valid(uid) || gid_valid(gid)) {
+ mode = NO_CHANGE_64;
+ rc = id_mode_to_cifs_acl(inode, full_path, &mode,
--- /dev/null
+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
+@@ -3703,6 +3703,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);
--- /dev/null
+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
+@@ -4431,6 +4431,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);
+
--- /dev/null
+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
+@@ -3505,6 +3505,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);
+
--- /dev/null
+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
+@@ -3280,6 +3280,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);
--- /dev/null
+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
+@@ -4359,11 +4359,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);
--- /dev/null
+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
+@@ -5111,6 +5111,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;
+@@ -5134,10 +5140,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;
+
--- /dev/null
+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
+@@ -5683,6 +5683,8 @@ SMB2_query_directory(const unsigned int
+
+ 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);
+
--- /dev/null
+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
+@@ -3911,6 +3911,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);
--- /dev/null
+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
+@@ -5380,7 +5380,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);
--- /dev/null
+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) {
--- /dev/null
+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
+@@ -181,6 +181,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") \
+@@ -192,6 +193,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!") \
--- /dev/null
+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
+@@ -2371,9 +2371,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);