]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
7.1-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 31 Jul 2026 06:48:18 +0000 (08:48 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 31 Jul 2026 06:48:18 +0000 (08:48 +0200)
added patches:
cifs-consolidate-time_last_write-stamp-into-_cifsfileinfo_put.patch
cifs-fix-time_last_write-stamp-placement-in-setattr-truncate-paths.patch
s390-ptff-export-ptff_function_mask.patch

queue-7.1/cifs-consolidate-time_last_write-stamp-into-_cifsfileinfo_put.patch [new file with mode: 0644]
queue-7.1/cifs-fix-time_last_write-stamp-placement-in-setattr-truncate-paths.patch [new file with mode: 0644]
queue-7.1/s390-ptff-export-ptff_function_mask.patch [new file with mode: 0644]
queue-7.1/series

diff --git a/queue-7.1/cifs-consolidate-time_last_write-stamp-into-_cifsfileinfo_put.patch b/queue-7.1/cifs-consolidate-time_last_write-stamp-into-_cifsfileinfo_put.patch
new file mode 100644 (file)
index 0000000..174773e
--- /dev/null
@@ -0,0 +1,195 @@
+From 0e3ea5445c228048f937ad5a944c27859a78f971 Mon Sep 17 00:00:00 2001
+From: Frank Sorenson <sorenson@redhat.com>
+Date: Fri, 24 Jul 2026 11:30:35 -0500
+Subject: cifs: consolidate time_last_write stamp into _cifsFileInfo_put()
+
+From: Frank Sorenson <sorenson@redhat.com>
+
+commit 0e3ea5445c228048f937ad5a944c27859a78f971 upstream.
+
+The time_last_write stamp was scattered across cifs_close(),
+smb2_deferred_work_close(), and the three drain functions in misc.c.
+This missed the case where background I/O holds the final reference
+after userspace close() returns, and required explicit maintenance at
+each close-path site.
+
+Move the smp_store_release() into _cifsFileInfo_put(), immediately
+before releasing open_file_lock.  This single location covers all
+close paths unconditionally: normal close, background I/O dropping the
+final reference, deferred close via timer or external drain.  The
+spinlock's store-release/load-acquire pairing with is_inode_writable()
+already provides the ordering guarantee documented in
+is_size_safe_to_change().
+
+Remove the now-redundant stamps from cifs_close(),
+smb2_deferred_work_close(), and all six stamp sites in the misc.c
+deferred-close drain functions.
+
+Fixes: e8a8d54c2d50 ("cifs: prevent readdir from changing file size due to stale directory metadata")
+Signed-off-by: Frank Sorenson <sorenson@redhat.com>
+Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Cc: Jiri Slaby <jirislaby@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/file.c |   37 ++++++++++++++++---------------------
+ fs/smb/client/misc.c |   51 ++++++---------------------------------------------
+ 2 files changed, 22 insertions(+), 66 deletions(-)
+
+--- a/fs/smb/client/file.c
++++ b/fs/smb/client/file.c
+@@ -915,6 +915,14 @@ void _cifsFileInfo_put(struct cifsFileIn
+               cifs_set_oplock_level(cifsi, 0);
+       }
++      if (OPEN_FMODE(cifs_file->f_flags) & FMODE_WRITE) {
++              /* Stamp while open_file_lock is held; covers all close paths
++               * including background I/O. Pairs with smp_load_acquire() in
++               * is_size_safe_to_change().
++               */
++              smp_store_release(&cifsi->time_last_write, jiffies);
++      }
++
+       spin_unlock(&cifsi->open_file_lock);
+       spin_unlock(&tcon->open_file_lock);
+@@ -1429,15 +1437,6 @@ void smb2_deferred_work_close(struct wor
+       cifs_del_deferred_close(cfile);
+       cfile->deferred_close_scheduled = false;
+       spin_unlock(&cinode->deferred_lock);
+-      /*
+-       * Refresh time_last_write immediately before the actual server close
+-       * so the protection window is anchored to the real close time, not
+-       * the earlier userspace close time stored by cifs_close().
+-       */
+-      if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) {
+-              /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
+-              smp_store_release(&cinode->time_last_write, jiffies);
+-      }
+       _cifsFileInfo_put(cfile, true, false);
+ }
+@@ -1467,10 +1466,6 @@ int cifs_close(struct inode *inode, stru
+       if (file->private_data != NULL) {
+               cfile = file->private_data;
+               file->private_data = NULL;
+-              if (file->f_mode & FMODE_WRITE) {
+-                      /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
+-                      smp_store_release(&cinode->time_last_write, jiffies);
+-              }
+               dclose = kmalloc_obj(struct cifs_deferred_close);
+               if ((cfile->status_file_deleted == false) &&
+                   (smb2_can_defer_close(inode, dclose))) {
+@@ -3222,13 +3217,13 @@ bool is_size_safe_to_change(struct cifsI
+        * No writable handles open. Check whether we are within the attribute
+        * cache validity window of a recent local modification.
+        *
+-       * For the close() path: cifs_close() calls smp_store_release() on
+-       * time_last_write before _cifsFileInfo_put() removes the handle under
+-       * open_file_lock. That spin_unlock() is a store-release that pairs
+-       * with the spin_lock() (load-acquire) in is_inode_writable() above,
+-       * so if is_inode_writable() returned false the smp_load_acquire()
+-       * below is guaranteed to observe any time_last_write update from a
+-       * concurrent close().
++       * For the close() path: _cifsFileInfo_put() stamps time_last_write
++       * (via smp_store_release()) before releasing open_file_lock. That
++       * spin_unlock() is a store-release that pairs with the spin_lock()
++       * (load-acquire) in is_inode_writable() above, so if
++       * is_inode_writable() returned false the smp_load_acquire() below is
++       * guaranteed to observe any time_last_write update from a concurrent
++       * close(), covering all close paths including background I/O.
+        *
+        * For the setattr/truncate paths: those callers use smp_store_release()
+        * directly; the smp_load_acquire() below pairs with that store. There
+@@ -3243,7 +3238,7 @@ bool is_size_safe_to_change(struct cifsI
+        * jiffies is still close to INITIAL_JIFFIES on 32-bit systems.
+        */
+       if (from_readdir) {
+-              /* Pairs with smp_store_release() at close and truncate sites. */
++              /* Pairs with smp_store_release() in _cifsFileInfo_put() and setattr. */
+               tlw = smp_load_acquire(&cifsInode->time_last_write);
+               if (tlw && time_before(jiffies, tlw + cifs_sb->ctx->acregmax))
+                       return false;
+--- a/fs/smb/client/misc.c
++++ b/fs/smb/client/misc.c
+@@ -521,24 +521,11 @@ cifs_close_deferred_file(struct cifsInod
+       }
+       spin_unlock(&cifs_inode->open_file_lock);
+-      if (failed_cfile) {
+-              if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) {
+-                      /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
+-                      smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write,
+-                                        jiffies);
+-              }
++      if (failed_cfile)
+               _cifsFileInfo_put(failed_cfile, false, false);
+-      }
+       list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
+-              struct cifsFileInfo *cfile = tmp_list->cfile;
+-
+-              if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) {
+-                      /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
+-                      smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write,
+-                                        jiffies);
+-              }
+-              _cifsFileInfo_put(cfile, false, false);
++              _cifsFileInfo_put(tmp_list->cfile, false, false);
+               list_del(&tmp_list->list);
+               kfree(tmp_list);
+       }
+@@ -572,24 +559,11 @@ cifs_close_all_deferred_files(struct cif
+       }
+       spin_unlock(&tcon->open_file_lock);
+-      if (failed_cfile) {
+-              if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) {
+-                      /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
+-                      smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write,
+-                                        jiffies);
+-              }
++      if (failed_cfile)
+               _cifsFileInfo_put(failed_cfile, true, false);
+-      }
+       list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
+-              struct cifsFileInfo *cfile = tmp_list->cfile;
+-
+-              if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) {
+-                      /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
+-                      smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write,
+-                                        jiffies);
+-              }
+-              _cifsFileInfo_put(cfile, true, false);
++              _cifsFileInfo_put(tmp_list->cfile, true, false);
+               list_del(&tmp_list->list);
+               kfree(tmp_list);
+       }
+@@ -659,24 +633,11 @@ void cifs_close_deferred_file_under_dent
+       }
+       spin_unlock(&tcon->open_file_lock);
+-      if (failed_cfile) {
+-              if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) {
+-                      /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
+-                      smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write,
+-                                        jiffies);
+-              }
++      if (failed_cfile)
+               _cifsFileInfo_put(failed_cfile, true, false);
+-      }
+       list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
+-              struct cifsFileInfo *cfile = tmp_list->cfile;
+-
+-              if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) {
+-                      /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
+-                      smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write,
+-                                        jiffies);
+-              }
+-              _cifsFileInfo_put(cfile, true, false);
++              _cifsFileInfo_put(tmp_list->cfile, true, false);
+               list_del(&tmp_list->list);
+               kfree(tmp_list);
+       }
diff --git a/queue-7.1/cifs-fix-time_last_write-stamp-placement-in-setattr-truncate-paths.patch b/queue-7.1/cifs-fix-time_last_write-stamp-placement-in-setattr-truncate-paths.patch
new file mode 100644 (file)
index 0000000..e91f814
--- /dev/null
@@ -0,0 +1,100 @@
+From ecababf08905958ba8c125979c4e39fc2f1a8a05 Mon Sep 17 00:00:00 2001
+From: Frank Sorenson <sorenson@redhat.com>
+Date: Fri, 24 Jul 2026 11:30:36 -0500
+Subject: cifs: fix time_last_write stamp placement in setattr/truncate paths
+
+From: Frank Sorenson <sorenson@redhat.com>
+
+commit ecababf08905958ba8c125979c4e39fc2f1a8a05 upstream.
+
+cifs_file_set_size() calls cifs_setsize() on success, which calls
+i_size_write(), updating i_size to the new value.  The subsequent
+check attrs->ia_size != i_size_read() in both cifs_setattr_unix()
+and cifs_setattr_nounix() therefore always evaluates false after a
+successful cifs_file_set_size(), making the smp_store_release() of
+time_last_write dead code.  The truncate path was unprotected against
+stale readdir size updates.
+
+Move the stamp to before the cifs_file_set_size() RPC call, guarded
+by attrs->ia_size != i_size_read() to exclude no-op same-size
+ftruncate(2) calls from stamping time_last_write unnecessarily.
+
+On the error path the stamp remains rather than being restored:
+restoring a stale snapshot (prev_tlw) could silently erase a
+concurrent _cifsFileInfo_put() close stamp if that close arrived
+between the READ_ONCE and the smp_store_release.  readdir is
+suppressed until the stamp expires, which extends beyond one acregmax
+if the caller retries failed truncations.  stat() is unaffected: the
+cifs_revalidate_dentry_attr() path calls cifs_fattr_to_inode() with
+from_readdir=false, which bypasses the time_last_write check in
+is_size_safe_to_change() entirely and always writes the authoritative
+QUERY_INFO result to i_size.
+
+Remove the now-unreachable stamp from the dead block in both functions.
+
+Fixes: e8a8d54c2d50 ("cifs: prevent readdir from changing file size due to stale directory metadata")
+Signed-off-by: Frank Sorenson <sorenson@redhat.com>
+Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Cc: Jiri Slaby <jirislaby@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/inode.c |   26 ++++++++++++++++++++++----
+ 1 file changed, 22 insertions(+), 4 deletions(-)
+
+--- a/fs/smb/client/inode.c
++++ b/fs/smb/client/inode.c
+@@ -3182,6 +3182,17 @@ cifs_setattr_unix(struct dentry *direntr
+       rc = 0;
+       if (attrs->ia_valid & ATTR_SIZE) {
++              if (attrs->ia_size != i_size_read(inode)) {
++                      /* Stamp before RPC. On failure the stamp remains: restoring a
++                       * stale snapshot could silently erase a concurrent
++                       * _cifsFileInfo_put() close stamp.  readdir is suppressed
++                       * until the stamp expires; stat() bypasses this via the
++                       * from_readdir=false path in is_size_safe_to_change() and
++                       * always returns an authoritative QUERY_INFO result.
++                       * Pairs with smp_load_acquire() in is_size_safe_to_change().
++                       */
++                      smp_store_release(&cifsInode->time_last_write, jiffies);
++              }
+               rc = cifs_file_set_size(xid, direntry, full_path,
+                                       open_file, attrs->ia_size);
+               if (rc != 0)
+@@ -3271,8 +3282,6 @@ cifs_setattr_unix(struct dentry *direntr
+       if ((attrs->ia_valid & ATTR_SIZE) &&
+           attrs->ia_size != i_size_read(inode)) {
+-              /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
+-              smp_store_release(&cifsInode->time_last_write, jiffies);
+               truncate_setsize(inode, attrs->ia_size);
+               netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true);
+               fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size);
+@@ -3362,6 +3371,17 @@ cifs_setattr_nounix(struct dentry *diren
+       }
+       if (attrs->ia_valid & ATTR_SIZE) {
++              if (attrs->ia_size != i_size_read(inode)) {
++                      /* Stamp before RPC. On failure the stamp remains: restoring a
++                       * stale snapshot could silently erase a concurrent
++                       * _cifsFileInfo_put() close stamp.  readdir is suppressed
++                       * until the stamp expires; stat() bypasses this via the
++                       * from_readdir=false path in is_size_safe_to_change() and
++                       * always returns an authoritative QUERY_INFO result.
++                       * Pairs with smp_load_acquire() in is_size_safe_to_change().
++                       */
++                      smp_store_release(&cifsInode->time_last_write, jiffies);
++              }
+               rc = cifs_file_set_size(xid, direntry, full_path,
+                                       cfile, attrs->ia_size);
+               if (rc != 0)
+@@ -3474,8 +3494,6 @@ cifs_setattr_nounix(struct dentry *diren
+       if ((attrs->ia_valid & ATTR_SIZE) &&
+           attrs->ia_size != i_size_read(inode)) {
+-              /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
+-              smp_store_release(&cifsInode->time_last_write, jiffies);
+               truncate_setsize(inode, attrs->ia_size);
+               netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true);
+               fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size);
diff --git a/queue-7.1/s390-ptff-export-ptff_function_mask.patch b/queue-7.1/s390-ptff-export-ptff_function_mask.patch
new file mode 100644 (file)
index 0000000..2f30909
--- /dev/null
@@ -0,0 +1,31 @@
+From 9de445d8296a7f2b011ebb5834fdc94dcda5c778 Mon Sep 17 00:00:00 2001
+From: Sven Schnelle <svens@linux.ibm.com>
+Date: Tue, 14 Jul 2026 15:03:41 +0200
+Subject: s390/ptff: Export ptff_function_mask[]
+
+From: Sven Schnelle <svens@linux.ibm.com>
+
+commit 9de445d8296a7f2b011ebb5834fdc94dcda5c778 upstream.
+
+Export the ptff_function_mask to make ptff_query() usable in modules.
+
+Signed-off-by: Sven Schnelle <svens@linux.ibm.com>
+Acked-by: Heiko Carstens <hca@linux.ibm.com>
+Link: https://patch.msgid.link/20260714130342.1971700-2-svens@linux.ibm.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Cc: Jiri Slaby <jirislaby@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/s390/kernel/time.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/s390/kernel/time.c
++++ b/arch/s390/kernel/time.c
+@@ -65,6 +65,7 @@ ATOMIC_NOTIFIER_HEAD(s390_epoch_delta_no
+ EXPORT_SYMBOL(s390_epoch_delta_notifier);
+ unsigned char ptff_function_mask[16];
++EXPORT_SYMBOL(ptff_function_mask);
+ static unsigned long lpar_offset;
+ static unsigned long initial_leap_seconds;
index c11342a745f89ba84b64eec8af1a03678072c97c..2a87ae2852d73eb98efb617306e70f01ca3b0171 100644 (file)
@@ -741,3 +741,6 @@ sched_ext-preserve-rq-tracking-across-local-dsq-dispatch.patch
 selftests-drv-net-cope-with-slow-env-in-so_txtime.py-test.patch
 selftests-drv-net-so_txtime-relax-variance-bounds.patch
 kvm-svm-bump-asid_generation-on-cpu-online-to-avoid-asid-collision-after-hotplug.patch
+s390-ptff-export-ptff_function_mask.patch
+cifs-fix-time_last_write-stamp-placement-in-setattr-truncate-paths.patch
+cifs-consolidate-time_last_write-stamp-into-_cifsfileinfo_put.patch