]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.12-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 7 Jul 2026 14:36:22 +0000 (16:36 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 7 Jul 2026 14:36:22 +0000 (16:36 +0200)
added patches:
apparmor-advertise-the-tcp-fast-open-fix-is-applied.patch
device-property-initialize-the-remaining-fields-of-fwnode_handle-in-fwnode_init.patch
f2fs-atomic-fix-uaf-issue-on-f2fs_inode_info.atomic_inode.patch
f2fs-bound-i_inline_xattr_size-for-non-inline-xattr-inodes.patch
f2fs-fix-listxattr-handling-of-corrupted-xattr-entries.patch
f2fs-fix-potential-deadlock-in-f2fs_balance_fs.patch
f2fs-fix-potential-deadlock-in-gc_merge-path-of-f2fs_balance_fs.patch
f2fs-validate-orphan-inode-entry-count.patch
fbdev-fbcon-fix-out-of-bounds-read-in-err_out-of-fbcon_do_set_font.patch
locking-rtmutex-make-sure-we-wake-anything-on-the-wake_q-when-we-release-the-lock-wait_lock.patch
nfs_common-rename-functions-that-invalidate-localio-nfs_clients.patch
nfsd-add-nfsd_file_-get-put-to-nfs_to-nfsd_localio_operations.patch
nfsv4-flexfiles-add-data-structure-support-for-striped-layouts.patch
nfsv4-flexfiles-reject-zero-filehandle-version-count.patch
nfsv4-flexfiles-remove-cred-local-variable-dependency.patch

16 files changed:
queue-6.12/apparmor-advertise-the-tcp-fast-open-fix-is-applied.patch [new file with mode: 0644]
queue-6.12/device-property-initialize-the-remaining-fields-of-fwnode_handle-in-fwnode_init.patch [new file with mode: 0644]
queue-6.12/f2fs-atomic-fix-uaf-issue-on-f2fs_inode_info.atomic_inode.patch [new file with mode: 0644]
queue-6.12/f2fs-bound-i_inline_xattr_size-for-non-inline-xattr-inodes.patch [new file with mode: 0644]
queue-6.12/f2fs-fix-listxattr-handling-of-corrupted-xattr-entries.patch [new file with mode: 0644]
queue-6.12/f2fs-fix-potential-deadlock-in-f2fs_balance_fs.patch [new file with mode: 0644]
queue-6.12/f2fs-fix-potential-deadlock-in-gc_merge-path-of-f2fs_balance_fs.patch [new file with mode: 0644]
queue-6.12/f2fs-validate-orphan-inode-entry-count.patch [new file with mode: 0644]
queue-6.12/fbdev-fbcon-fix-out-of-bounds-read-in-err_out-of-fbcon_do_set_font.patch [new file with mode: 0644]
queue-6.12/locking-rtmutex-make-sure-we-wake-anything-on-the-wake_q-when-we-release-the-lock-wait_lock.patch [new file with mode: 0644]
queue-6.12/nfs_common-rename-functions-that-invalidate-localio-nfs_clients.patch [new file with mode: 0644]
queue-6.12/nfsd-add-nfsd_file_-get-put-to-nfs_to-nfsd_localio_operations.patch [new file with mode: 0644]
queue-6.12/nfsv4-flexfiles-add-data-structure-support-for-striped-layouts.patch [new file with mode: 0644]
queue-6.12/nfsv4-flexfiles-reject-zero-filehandle-version-count.patch [new file with mode: 0644]
queue-6.12/nfsv4-flexfiles-remove-cred-local-variable-dependency.patch [new file with mode: 0644]
queue-6.12/series

diff --git a/queue-6.12/apparmor-advertise-the-tcp-fast-open-fix-is-applied.patch b/queue-6.12/apparmor-advertise-the-tcp-fast-open-fix-is-applied.patch
new file mode 100644 (file)
index 0000000..adfe0dc
--- /dev/null
@@ -0,0 +1,36 @@
+From stable+bounces-272222-greg=kroah.com@vger.kernel.org Mon Jul  6 16:57:59 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon,  6 Jul 2026 09:17:08 -0400
+Subject: apparmor: advertise the tcp fast open fix is applied
+To: stable@vger.kernel.org
+Cc: John Johansen <john.johansen@canonical.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260706131708.2289810-1-sashal@kernel.org>
+
+From: John Johansen <john.johansen@canonical.com>
+
+[ Upstream commit 2f6701a5ce6257ae7a64ddc6d89d0a08d2a034f8 ]
+
+The fix for tcp-fast-open ensures that the connect permission is being
+mediated correctly but it didn't add an artifact to the feature set to
+advertise the fix is available. Add an artifact so that the test suite
+can identify if the fix has not been properly applied or a new
+unexpected regression has occurred.
+
+Fixes: 4d587cd8a7215 ("apparmor: mediate the implicit connect of TCP fast open sendmsg")
+Signed-off-by: John Johansen <john.johansen@canonical.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ security/apparmor/net.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/security/apparmor/net.c
++++ b/security/apparmor/net.c
+@@ -21,6 +21,7 @@
+ struct aa_sfs_entry aa_sfs_entry_network[] = {
+       AA_SFS_FILE_STRING("af_mask",   AA_SFS_AF_MASK),
++      AA_SFS_FILE_BOOLEAN("tcp-fast-open",            1),
+       { }
+ };
diff --git a/queue-6.12/device-property-initialize-the-remaining-fields-of-fwnode_handle-in-fwnode_init.patch b/queue-6.12/device-property-initialize-the-remaining-fields-of-fwnode_handle-in-fwnode_init.patch
new file mode 100644 (file)
index 0000000..29ee4d9
--- /dev/null
@@ -0,0 +1,49 @@
+From stable+bounces-271576-greg=kroah.com@vger.kernel.org Fri Jul  3 00:23:04 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu,  2 Jul 2026 18:22:52 -0400
+Subject: device property: initialize the remaining fields of fwnode_handle in fwnode_init()
+To: stable@vger.kernel.org
+Cc: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>, Sakari Ailus <sakari.ailus@linux.intel.com>, "Rafael J. Wysocki (Intel)" <rafael@kernel.org>, Andy Shevchenko <andriy.shevchenko@linux.intel.com>, Danilo Krummrich <dakr@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260702222252.3701173-1-sashal@kernel.org>
+
+From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+
+[ Upstream commit 7eba000621fff223dd7bab484d48918c7c77a307 ]
+
+If a firmware node is allocated on the stack (for instance: temporary
+software node whose life-time we control) or on the heap - but using a
+non-zeroing allocation function - and initialized using fwnode_init(),
+its secondary pointer will contain uninitialized memory which likely
+will be neither NULL nor IS_ERR() and so may end up being dereferenced
+(for example: in dev_to_swnode()). Set fwnode->secondary to NULL on
+initialization. While at it: initialize the remaining fields of struct
+fwnode_handle too just to be sure.
+
+Cc: stable@vger.kernel.org
+Fixes: 01bb86b380a3 ("driver core: Add fwnode_init()")
+Reviewed-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Reviewed-by: Rafael J. Wysocki (Intel) <rafael@kernel.org>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260511074927.9473-1-bartosz.golaszewski@oss.qualcomm.com
+[ Fix typo in commit message. - Danilo ]
+Signed-off-by: Danilo Krummrich <dakr@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/fwnode.h |    2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/include/linux/fwnode.h
++++ b/include/linux/fwnode.h
+@@ -202,8 +202,10 @@ static inline void fwnode_init(struct fw
+ {
+       fwnode->secondary = NULL;
+       fwnode->ops = ops;
++      fwnode->dev = NULL;
+       INIT_LIST_HEAD(&fwnode->consumers);
+       INIT_LIST_HEAD(&fwnode->suppliers);
++      fwnode->flags = 0;
+ }
+ static inline void fwnode_set_flag(struct fwnode_handle *fwnode,
diff --git a/queue-6.12/f2fs-atomic-fix-uaf-issue-on-f2fs_inode_info.atomic_inode.patch b/queue-6.12/f2fs-atomic-fix-uaf-issue-on-f2fs_inode_info.atomic_inode.patch
new file mode 100644 (file)
index 0000000..0569454
--- /dev/null
@@ -0,0 +1,168 @@
+From stable+bounces-271775-greg=kroah.com@vger.kernel.org Fri Jul  3 15:33:23 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri,  3 Jul 2026 09:23:42 -0400
+Subject: f2fs: atomic: fix UAF issue on f2fs_inode_info.atomic_inode
+To: stable@vger.kernel.org
+Cc: Chao Yu <chao@kernel.org>, stable@kernel.org, Daeho Jeong <daehojeong@google.com>, Sunmin Jeong <s_min.jeong@samsung.com>, Jaegeuk Kim <jaegeuk@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260703132342.4101156-1-sashal@kernel.org>
+
+From: Chao Yu <chao@kernel.org>
+
+[ Upstream commit e0288584baa5dc41df4a829a023c4c1b33fe53d7 ]
+
+- ioctl(F2FS_IOC_GARBAGE_COLLECT_RANGE)                - shrink
+ - f2fs_gc
+  - gc_data_segment
+   - ra_data_block(cow_inode)
+    - mapping = F2FS_I(inode)->atomic_inode->i_mapping
+    : f2fs_is_cow_file(cow_inode) is true
+                                                - f2fs_evict_inode(atomic_inode)
+                                                 - clear_inode_flag(fi->cow_inode, FI_COW_FILE)
+                                                 - F2FS_I(fi->cow_inode)->atomic_inode = NULL
+                                                 ...
+                                                 - truncate_inode_pages_final(atomic_inode)
+    - f2fs_grab_cache_folio(mapping)
+    : create folio in atomic_inode->mapping
+                                                 - clear_inode(atomic_inode)
+                                                  - BUG_ON(atomic_inode->i_data.nrpages)
+
+We need to add a reference on fi->atomic_inode before using its mapping
+field during garbage collection, otherwise, it will cause UAF issue.
+
+Cc: stable@kernel.org
+Cc: Daeho Jeong <daehojeong@google.com>
+Cc: Sunmin Jeong <s_min.jeong@samsung.com>
+Fixes: 3db1de0e582c ("f2fs: change the current atomic write way")
+Fixes: f18d00769336 ("f2fs: use meta inode for GC of COW file")
+Signed-off-by: Chao Yu <chao@kernel.org>
+Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/f2fs/gc.c    |   50 ++++++++++++++++++++++++++++++++++++++++++--------
+ fs/f2fs/inode.c |   11 ++++++++---
+ 2 files changed, 50 insertions(+), 11 deletions(-)
+
+--- a/fs/f2fs/gc.c
++++ b/fs/f2fs/gc.c
+@@ -1213,8 +1213,8 @@ static bool is_alive(struct f2fs_sb_info
+ static int ra_data_block(struct inode *inode, pgoff_t index)
+ {
+       struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+-      struct address_space *mapping = f2fs_is_cow_file(inode) ?
+-                              F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping;
++      struct address_space *mapping = inode->i_mapping;
++      struct inode *atomic_inode = NULL;
+       struct dnode_of_data dn;
+       struct page *page;
+       struct f2fs_io_info fio = {
+@@ -1229,9 +1229,22 @@ static int ra_data_block(struct inode *i
+       };
+       int err;
++      f2fs_down_read(&F2FS_I(inode)->i_sem);
++      if (f2fs_is_cow_file(inode)) {
++              atomic_inode = igrab(F2FS_I(inode)->atomic_inode);
++              if (!atomic_inode) {
++                      f2fs_up_read(&F2FS_I(inode)->i_sem);
++                      return -EBUSY;
++              }
++              mapping = atomic_inode->i_mapping;
++      }
++      f2fs_up_read(&F2FS_I(inode)->i_sem);
++
+       page = f2fs_grab_cache_page(mapping, index, true);
+-      if (!page)
+-              return -ENOMEM;
++      if (!page) {
++              err = -ENOMEM;
++              goto out_iput;
++      }
+       if (f2fs_lookup_read_extent_cache_block(inode, index,
+                                               &dn.data_blkaddr)) {
+@@ -1288,11 +1301,16 @@ got_it:
+       f2fs_update_iostat(sbi, inode, FS_DATA_READ_IO, F2FS_BLKSIZE);
+       f2fs_update_iostat(sbi, NULL, FS_GDATA_READ_IO, F2FS_BLKSIZE);
++      if (atomic_inode)
++              iput(atomic_inode);
+       return 0;
+ put_encrypted_page:
+       f2fs_put_page(fio.encrypted_page, 1);
+ put_page:
+       f2fs_put_page(page, 1);
++out_iput:
++      if (atomic_inode)
++              iput(atomic_inode);
+       return err;
+ }
+@@ -1303,8 +1321,8 @@ put_page:
+ static int move_data_block(struct inode *inode, block_t bidx,
+                               int gc_type, unsigned int segno, int off)
+ {
+-      struct address_space *mapping = f2fs_is_cow_file(inode) ?
+-                              F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping;
++      struct address_space *mapping = inode->i_mapping;
++      struct inode *atomic_inode = NULL;
+       struct f2fs_io_info fio = {
+               .sbi = F2FS_I_SB(inode),
+               .ino = inode->i_ino,
+@@ -1326,10 +1344,23 @@ static int move_data_block(struct inode
+                               (fio.sbi->gc_mode != GC_URGENT_HIGH) ?
+                               CURSEG_ALL_DATA_ATGC : CURSEG_COLD_DATA;
++      f2fs_down_read(&F2FS_I(inode)->i_sem);
++      if (f2fs_is_cow_file(inode)) {
++              atomic_inode = igrab(F2FS_I(inode)->atomic_inode);
++              if (!atomic_inode) {
++                      f2fs_up_read(&F2FS_I(inode)->i_sem);
++                      return -EBUSY;
++              }
++              mapping = atomic_inode->i_mapping;
++      }
++      f2fs_up_read(&F2FS_I(inode)->i_sem);
++
+       /* do not read out */
+       page = f2fs_grab_cache_page(mapping, bidx, false);
+-      if (!page)
+-              return -ENOMEM;
++      if (!page) {
++              err = -ENOMEM;
++              goto out_iput;
++      }
+       if (!check_valid_map(F2FS_I_SB(inode), segno, off)) {
+               err = -ENOENT;
+@@ -1456,6 +1487,9 @@ put_out:
+       f2fs_put_dnode(&dn);
+ out:
+       f2fs_put_page(page, 1);
++out_iput:
++      if (atomic_inode)
++              iput(atomic_inode);
+       return err;
+ }
+--- a/fs/f2fs/inode.c
++++ b/fs/f2fs/inode.c
+@@ -839,10 +839,15 @@ void f2fs_evict_inode(struct inode *inod
+       f2fs_abort_atomic_write(inode, true);
+       if (fi->cow_inode && f2fs_is_cow_file(fi->cow_inode)) {
+-              clear_inode_flag(fi->cow_inode, FI_COW_FILE);
+-              F2FS_I(fi->cow_inode)->atomic_inode = NULL;
+-              iput(fi->cow_inode);
++              struct inode *cow_inode = fi->cow_inode;
++
++              f2fs_down_write(&F2FS_I(cow_inode)->i_sem);
++              clear_inode_flag(cow_inode, FI_COW_FILE);
++              F2FS_I(cow_inode)->atomic_inode = NULL;
+               fi->cow_inode = NULL;
++              f2fs_up_write(&F2FS_I(cow_inode)->i_sem);
++
++              iput(cow_inode);
+       }
+       trace_f2fs_evict_inode(inode);
diff --git a/queue-6.12/f2fs-bound-i_inline_xattr_size-for-non-inline-xattr-inodes.patch b/queue-6.12/f2fs-bound-i_inline_xattr_size-for-non-inline-xattr-inodes.patch
new file mode 100644 (file)
index 0000000..b57c7e2
--- /dev/null
@@ -0,0 +1,96 @@
+From stable+bounces-271805-greg=kroah.com@vger.kernel.org Fri Jul  3 16:35:31 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri,  3 Jul 2026 10:26:35 -0400
+Subject: f2fs: bound i_inline_xattr_size for non-inline-xattr inodes
+To: stable@vger.kernel.org
+Cc: Bryam Vargas <hexlabsecurity@proton.me>, Chao Yu <chao@kernel.org>, Jaegeuk Kim <jaegeuk@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260703142635.55675-1-sashal@kernel.org>
+
+From: Bryam Vargas <hexlabsecurity@proton.me>
+
+[ Upstream commit 378acf3cf19b6af6cba55e8dd1154c4e1504bae8 ]
+
+When the flexible_inline_xattr feature is enabled, do_read_inode() loads
+the on-disk i_inline_xattr_size unconditionally:
+
+       if (f2fs_sb_has_flexible_inline_xattr(sbi))
+               fi->i_inline_xattr_size = le16_to_cpu(ri->i_inline_xattr_size);
+
+but sanity_check_inode() only range-checks it when the inode also has the
+FI_INLINE_XATTR flag set.  An inode that carries an inline dentry or inline
+data but not FI_INLINE_XATTR -- the normal layout for an inline
+directory -- therefore keeps a fully attacker-controlled
+i_inline_xattr_size from a crafted image.
+
+get_inline_xattr_addrs() returns that value with no flag gating, so it
+feeds the inode geometry:
+
+       MAX_INLINE_DATA()  = 4 * (CUR_ADDRS_PER_INODE - i_inline_xattr_size - 1)
+       NR_INLINE_DENTRY() = MAX_INLINE_DATA() * BITS_PER_BYTE / (...)
+       addrs_per_page()   = CUR_ADDRS_PER_INODE - i_inline_xattr_size
+
+A large i_inline_xattr_size drives MAX_INLINE_DATA() and NR_INLINE_DENTRY()
+negative, so make_dentry_ptr_inline() sets d->max (int) to a negative
+value.  The inline directory walk then compares an unsigned long bit_pos
+against that negative d->max, which is promoted to a huge unsigned bound,
+and reads far past the inline area:
+
+       while (bit_pos < d->max)                /* fs/f2fs/dir.c */
+               ... test_bit_le(bit_pos, d->bitmap) / d->dentry[bit_pos] ...
+
+Mounting a crafted image and reading such a directory triggers an
+out-of-bounds read in f2fs_fill_dentries(); the same underflow also
+corrupts ADDRS_PER_INODE for regular files.
+
+Validate i_inline_xattr_size against MAX_INLINE_XATTR_SIZE whenever the
+flexible_inline_xattr feature is enabled -- i.e. whenever the value is
+loaded from disk and consumed -- and keep the lower MIN_INLINE_XATTR_SIZE
+bound gated on inodes that actually carry an inline xattr, so legitimate
+inodes with i_inline_xattr_size == 0 are still accepted.
+
+Cc: stable@vger.kernel.org
+Fixes: 6afc662e68b5 ("f2fs: support flexible inline xattr size")
+Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
+Reviewed-by: Chao Yu <chao@kernel.org>
+Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/f2fs/inode.c |   19 ++++++++++---------
+ 1 file changed, 10 insertions(+), 9 deletions(-)
+
+--- a/fs/f2fs/inode.c
++++ b/fs/f2fs/inode.c
+@@ -308,15 +308,6 @@ static bool sanity_check_inode(struct in
+                                 F2FS_TOTAL_EXTRA_ATTR_SIZE);
+                       return false;
+               }
+-              if (f2fs_sb_has_flexible_inline_xattr(sbi) &&
+-                      f2fs_has_inline_xattr(inode) &&
+-                      (!fi->i_inline_xattr_size ||
+-                      fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE)) {
+-                      f2fs_warn(sbi, "%s: inode (ino=%lx) has corrupted i_inline_xattr_size: %d, max: %lu",
+-                                __func__, inode->i_ino, fi->i_inline_xattr_size,
+-                                MAX_INLINE_XATTR_SIZE);
+-                      return false;
+-              }
+               if (f2fs_sb_has_compression(sbi) &&
+                       fi->i_flags & F2FS_COMPR_FL &&
+                       F2FS_FITS_IN_INODE(ri, fi->i_extra_isize,
+@@ -326,6 +317,16 @@ static bool sanity_check_inode(struct in
+               }
+       }
++      if (f2fs_sb_has_flexible_inline_xattr(sbi) &&
++              (fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE ||
++              (f2fs_has_inline_xattr(inode) &&
++              fi->i_inline_xattr_size < MIN_INLINE_XATTR_SIZE))) {
++              f2fs_warn(sbi, "%s: inode (ino=%lx) has corrupted i_inline_xattr_size: %d, min: %zu, max: %lu",
++                        __func__, inode->i_ino, fi->i_inline_xattr_size,
++                        MIN_INLINE_XATTR_SIZE, MAX_INLINE_XATTR_SIZE);
++              return false;
++      }
++
+       if (!f2fs_sb_has_extra_attr(sbi)) {
+               if (f2fs_sb_has_project_quota(sbi)) {
+                       f2fs_warn(sbi, "%s: corrupted inode ino=%lx, wrong feature flag: %u, run fsck to fix.",
diff --git a/queue-6.12/f2fs-fix-listxattr-handling-of-corrupted-xattr-entries.patch b/queue-6.12/f2fs-fix-listxattr-handling-of-corrupted-xattr-entries.patch
new file mode 100644 (file)
index 0000000..71fe3fc
--- /dev/null
@@ -0,0 +1,51 @@
+From stable+bounces-271856-greg=kroah.com@vger.kernel.org Fri Jul  3 20:50:22 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri,  3 Jul 2026 14:50:15 -0400
+Subject: f2fs: fix listxattr handling of corrupted xattr entries
+To: stable@vger.kernel.org
+Cc: Keshav Verma <iganschel@gmail.com>, stable@kernel.org, Chao Yu <chao@kernel.org>, Jaegeuk Kim <jaegeuk@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260703185015.285028-1-sashal@kernel.org>
+
+From: Keshav Verma <iganschel@gmail.com>
+
+[ Upstream commit 5ef5bc304f23c3fe255d4936472378dcb74d0e94 ]
+
+Validate the xattr entry before reading its fields in f2fs_listxattr().
+Return -EFSCORRUPTED when the entry is outside the valid xattr storage
+area instead of returning a successful partial result.
+
+Fixes: 688078e7f36c ("f2fs: fix to avoid memory leakage in f2fs_listxattr")
+Cc: stable@kernel.org
+Reviewed-by: Chao Yu <chao@kernel.org>
+Signed-off-by: Keshav Verma <iganschel@gmail.com>
+Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/f2fs/xattr.c |    6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/fs/f2fs/xattr.c
++++ b/fs/f2fs/xattr.c
+@@ -581,8 +581,6 @@ ssize_t f2fs_listxattr(struct dentry *de
+               size_t prefix_len;
+               size_t size;
+-              prefix = f2fs_xattr_prefix(entry->e_name_index, dentry);
+-
+               if ((void *)(entry) + sizeof(__u32) > last_base_addr ||
+                       (void *)XATTR_NEXT_ENTRY(entry) > last_base_addr) {
+                       f2fs_err(F2FS_I_SB(inode), "list inode (%lu) has corrupted xattr",
+@@ -590,9 +588,11 @@ ssize_t f2fs_listxattr(struct dentry *de
+                       set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_FSCK);
+                       f2fs_handle_error(F2FS_I_SB(inode),
+                                               ERROR_CORRUPTED_XATTR);
+-                      break;
++                      error = -EFSCORRUPTED;
++                      goto cleanup;
+               }
++              prefix = f2fs_xattr_prefix(entry->e_name_index, dentry);
+               if (!prefix)
+                       continue;
diff --git a/queue-6.12/f2fs-fix-potential-deadlock-in-f2fs_balance_fs.patch b/queue-6.12/f2fs-fix-potential-deadlock-in-f2fs_balance_fs.patch
new file mode 100644 (file)
index 0000000..512930e
--- /dev/null
@@ -0,0 +1,253 @@
+From stable+bounces-271846-greg=kroah.com@vger.kernel.org Fri Jul  3 20:32:05 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri,  3 Jul 2026 14:24:12 -0400
+Subject: f2fs: fix potential deadlock in f2fs_balance_fs()
+To: stable@vger.kernel.org
+Cc: Ruipeng Qi <ruipengqi3@gmail.com>, Chao Yu <chao@kernel.org>, Jaegeuk Kim <jaegeuk@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260703182413.254727-1-sashal@kernel.org>
+
+From: Ruipeng Qi <ruipengqi3@gmail.com>
+
+[ Upstream commit dd3114870771562036fdcf5abe813956f36d224d ]
+
+When the f2fs filesystem space is nearly exhausted, we encounter deadlock
+issues as below:
+
+INFO: task A:1890 blocked for more than 120 seconds.
+      Tainted: G           O       6.12.41-g3fe07ddf05ab #1
+"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
+task:A    state:D stack:0     pid:1890  tgid:1626  ppid:1153   flags:0x00000204
+Call trace:
+ __switch_to+0xf4/0x158
+ __schedule+0x27c/0x908
+ schedule+0x3c/0x118
+ io_schedule+0x44/0x68
+ folio_wait_bit_common+0x174/0x370
+ folio_wait_bit+0x20/0x38
+ folio_wait_writeback+0x54/0xc8
+ truncate_inode_partial_folio+0x70/0x1e0
+ truncate_inode_pages_range+0x1b0/0x450
+ truncate_pagecache+0x54/0x88
+ f2fs_file_write_iter+0x3e8/0xb80
+ do_iter_readv_writev+0xf0/0x1e0
+ vfs_writev+0x138/0x2c8
+ do_writev+0x88/0x130
+ __arm64_sys_writev+0x28/0x40
+ invoke_syscall+0x50/0x120
+ el0_svc_common.constprop.0+0xc8/0xf0
+ do_el0_svc+0x24/0x38
+ el0_svc+0x30/0xf8
+ el0t_64_sync_handler+0x120/0x130
+ el0t_64_sync+0x190/0x198
+
+INFO: task kworker/u8:11:2680853 blocked for more than 120 seconds.
+      Tainted: G           O       6.12.41-g3fe07ddf05ab #1
+"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
+task:kworker/u8:11   state:D stack:0     pid:2680853 tgid:2680853 ppid:2      flags:0x00000208
+Workqueue: writeback wb_workfn (flush-254:0)
+Call trace:
+ __switch_to+0xf4/0x158
+ __schedule+0x27c/0x908
+ schedule+0x3c/0x118
+ io_schedule+0x44/0x68
+ folio_wait_bit_common+0x174/0x370
+ __filemap_get_folio+0x214/0x348
+ pagecache_get_page+0x20/0x70
+ f2fs_get_read_data_page+0x150/0x3e8
+ f2fs_get_lock_data_page+0x2c/0x160
+ move_data_page+0x50/0x478
+ do_garbage_collect+0xd38/0x1528
+ f2fs_gc+0x240/0x7e0
+ f2fs_balance_fs+0x1a0/0x208
+ f2fs_write_single_data_page+0x6e4/0x730
+ f2fs_write_cache_pages+0x378/0x9b0
+ f2fs_write_data_pages+0x2e4/0x388
+ do_writepages+0x8c/0x2c8
+ __writeback_single_inode+0x4c/0x498
+ writeback_sb_inodes+0x234/0x4a8
+ __writeback_inodes_wb+0x58/0x118
+ wb_writeback+0x2f8/0x3c0
+ wb_workfn+0x2c4/0x508
+ process_one_work+0x180/0x408
+ worker_thread+0x258/0x368
+ kthread+0x118/0x128
+ ret_from_fork+0x10/0x200
+
+INFO: task kworker/u8:8:2641297 blocked for more than 120 seconds.
+      Tainted: G           O       6.12.41-g3fe07ddf05ab #1
+"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
+task:kworker/u8:8    state:D stack:0     pid:2641297 tgid:2641297 ppid:2      flags:0x00000208
+Workqueue: writeback wb_workfn (flush-254:0)
+Call trace:
+ __switch_to+0xf4/0x158
+ __schedule+0x27c/0x908
+ rt_mutex_schedule+0x30/0x60
+ __rt_mutex_slowlock_locked.constprop.0+0x460/0x8a8
+ rwbase_write_lock+0x24c/0x378
+ down_write+0x1c/0x30
+ f2fs_balance_fs+0x184/0x208
+ f2fs_write_inode+0xf4/0x328
+ __writeback_single_inode+0x370/0x498
+ writeback_sb_inodes+0x234/0x4a8
+ __writeback_inodes_wb+0x58/0x118
+ wb_writeback+0x2f8/0x3c0
+ wb_workfn+0x2c4/0x508
+ process_one_work+0x180/0x408
+ worker_thread+0x258/0x368
+ kthread+0x118/0x128
+ ret_from_fork+0x10/0x20
+
+INFO: task B:1902 blocked for more than 120 seconds.
+      Tainted: G           O       6.12.41-g3fe07ddf05ab #1
+"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
+task:B     state:D stack:0     pid:1902  tgid:1626  ppid:1153   flags:0x0000020c
+Call trace:
+ __switch_to+0xf4/0x158
+ __schedule+0x27c/0x908
+ rt_mutex_schedule+0x30/0x60
+ __rt_mutex_slowlock_locked.constprop.0+0x460/0x8a8
+ rwbase_write_lock+0x24c/0x378
+ down_write+0x1c/0x30
+ f2fs_balance_fs+0x184/0x208
+ f2fs_map_blocks+0x94c/0x1110
+ f2fs_file_write_iter+0x228/0xb80
+ do_iter_readv_writev+0xf0/0x1e0
+ vfs_writev+0x138/0x2c8
+ do_writev+0x88/0x130
+ __arm64_sys_writev+0x28/0x40
+ invoke_syscall+0x50/0x120
+ el0_svc_common.constprop.0+0xc8/0xf0
+ do_el0_svc+0x24/0x38
+ el0_svc+0x30/0xf8
+ el0t_64_sync_handler+0x120/0x130
+ el0t_64_sync+0x190/0x198
+
+INFO: task sync:2769849 blocked for more than 120 seconds.
+      Tainted: G           O       6.12.41-g3fe07ddf05ab #1
+"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
+task:sync            state:D stack:0     pid:2769849 tgid:2769849 ppid:736    flags:0x0000020c
+Call trace:
+ __switch_to+0xf4/0x158
+ __schedule+0x27c/0x908
+ schedule+0x3c/0x118
+ wb_wait_for_completion+0xb0/0xe8
+ sync_inodes_sb+0xc8/0x2b0
+ sync_inodes_one_sb+0x24/0x38
+ iterate_supers+0xa8/0x138
+ ksys_sync+0x54/0xc8
+ __arm64_sys_sync+0x18/0x30
+ invoke_syscall+0x50/0x120
+ el0_svc_common.constprop.0+0xc8/0xf0
+ do_el0_svc+0x24/0x38
+ el0_svc+0x30/0xf8
+ el0t_64_sync_handler+0x120/0x130
+ el0t_64_sync+0x190/0x198
+
+The root cause is a potential deadlock between the following tasks:
+
+kworker/u8:11                          Thread A
+- f2fs_write_single_data_page
+ - f2fs_do_write_data_page
+  - folio_start_writeback(X)
+  - f2fs_outplace_write_data
+   - bio_add_folio(X)
+ - folio_unlock(X)
+                                       - truncate_inode_pages_range
+                                        - __filemap_get_folio(X, FGP_LOCK)
+                                        - truncate_inode_partial_folio(X)
+                                         - folio_wait_writeback(X)
+ - f2fs_balance_fs
+  - f2fs_gc
+   - do_garbage_collect
+    - move_data_page
+     - f2fs_get_lock_data_page
+      - __filemap_get_folio(X, FGP_LOCK)
+
+Both threads try to access folio X. Thread A holds the lock but waits
+for writeback, while kworker waits for the lock. This causes a deadlock.
+
+Other threads also enter D state, waiting for locks such as gc_lock and
+writepages.
+
+OPU/IPU DATA folio are all affected by this issue. To avoid such
+potential deadlocks, always commit these cached folios before
+triggering f2fs_gc() in f2fs_balance_fs().
+
+Suggested-by: Chao Yu <chao@kernel.org>
+Reviewed-by: Chao Yu <chao@kernel.org>
+Signed-off-by: Ruipeng Qi <ruipengqi3@gmail.com>
+Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
+Stable-dep-of: 8b4468ec023d ("f2fs: fix potential deadlock in gc_merge path of f2fs_balance_fs()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/f2fs/data.c    |   29 +++++++++++++++++++++++++++++
+ fs/f2fs/f2fs.h    |    1 +
+ fs/f2fs/segment.c |    8 ++++++++
+ 3 files changed, 38 insertions(+)
+
+--- a/fs/f2fs/data.c
++++ b/fs/f2fs/data.c
+@@ -890,6 +890,35 @@ void f2fs_submit_merged_ipu_write(struct
+       }
+ }
++void f2fs_submit_all_merged_ipu_writes(struct f2fs_sb_info *sbi)
++{
++      struct bio_entry *be, *tmp;
++      struct f2fs_bio_info *io;
++      enum temp_type temp;
++
++      for (temp = HOT; temp < NR_TEMP_TYPE; temp++) {
++              LIST_HEAD(list);
++
++              io = sbi->write_io[DATA] + temp;
++
++              /* A lockless list_empty() check is safe here: any bios from
++               * other kworkers that we miss will be submitted by those
++               * kworkers accordingly.
++               */
++              if (list_empty(&io->bio_list))
++                      continue;
++
++              f2fs_down_write(&io->bio_list_lock);
++              list_splice_init(&io->bio_list, &list);
++              f2fs_up_write(&io->bio_list_lock);
++
++              list_for_each_entry_safe(be, tmp, &list, list) {
++                      f2fs_submit_write_bio(sbi, be->bio, DATA);
++                      del_bio_entry(be);
++              }
++      }
++}
++
+ int f2fs_merge_page_bio(struct f2fs_io_info *fio)
+ {
+       struct bio *bio = *fio->bio;
+--- a/fs/f2fs/f2fs.h
++++ b/fs/f2fs/f2fs.h
+@@ -3929,6 +3929,7 @@ void f2fs_submit_merged_write_cond(struc
+                               nid_t ino, enum page_type type);
+ void f2fs_submit_merged_ipu_write(struct f2fs_sb_info *sbi,
+                                       struct bio **bio, struct page *page);
++void f2fs_submit_all_merged_ipu_writes(struct f2fs_sb_info *sbi);
+ void f2fs_flush_merged_writes(struct f2fs_sb_info *sbi);
+ int f2fs_submit_page_bio(struct f2fs_io_info *fio);
+ int f2fs_merge_page_bio(struct f2fs_io_info *fio);
+--- a/fs/f2fs/segment.c
++++ b/fs/f2fs/segment.c
+@@ -451,6 +451,14 @@ void f2fs_balance_fs(struct f2fs_sb_info
+                       .should_migrate_blocks = false,
+                       .err_gc_skipped = false,
+                       .nr_free_secs = 1 };
++
++              /*
++               * Submit all cached OPU/IPU DATA bios before triggering
++               * foreground GC to avoid potential deadlocks.
++               */
++              f2fs_submit_merged_write(sbi, DATA);
++              f2fs_submit_all_merged_ipu_writes(sbi);
++
+               f2fs_down_write(&sbi->gc_lock);
+               stat_inc_gc_call_count(sbi, FOREGROUND);
+               f2fs_gc(sbi, &gc_control);
diff --git a/queue-6.12/f2fs-fix-potential-deadlock-in-gc_merge-path-of-f2fs_balance_fs.patch b/queue-6.12/f2fs-fix-potential-deadlock-in-gc_merge-path-of-f2fs_balance_fs.patch
new file mode 100644 (file)
index 0000000..98e6fb2
--- /dev/null
@@ -0,0 +1,85 @@
+From stable+bounces-271847-greg=kroah.com@vger.kernel.org Fri Jul  3 20:27:31 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri,  3 Jul 2026 14:24:13 -0400
+Subject: f2fs: fix potential deadlock in gc_merge path of f2fs_balance_fs()
+To: stable@vger.kernel.org
+Cc: Chao Yu <chao@kernel.org>, stable@kernel.org, Ruipeng Qi <ruipengqi3@gmail.com>, Chao Yu <chaseyu@google.com>, Jaegeuk Kim <jaegeuk@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260703182413.254727-2-sashal@kernel.org>
+
+From: Chao Yu <chao@kernel.org>
+
+[ Upstream commit 8b4468ec023d0d1b4669dfb867588997cc03a06b ]
+
+When we mount device w/ gc_merge mount option, we may suffer below
+potential deadlock:
+
+Kworker                                        GC trehad                       Truncator
+- f2fs_write_cache_pages
+ - f2fs_write_single_data_page
+  - f2fs_do_write_data_page
+   - folio_start_writeback  --- set writeback flag on folio
+   - f2fs_outplace_write_data
+   : cached folio in internal bio cache
+  - f2fs_balance_fs
+   - wake_up(gc_thread)
+   : wake up gc thread to run foreground GC
+   - finish_wait(fggc_wq)
+   : wait on the waitqueue --- wait on GC thread to finish the work
+                                                                       - truncate_inode_pages_range
+                                                                        - __filemap_get_folio(, FGP_LOCK)  --- lock folio
+                                                                        - truncate_inode_partial_folio
+                                                                         - folio_wait_writeback            --- wait on writeback being cleared
+                                       - do_garbage_collect
+                                        - move_data_page
+                                         - f2fs_get_lock_data_folio
+                                          - lock on folio  --- blocked on folio's lock
+
+In order to avoid such deadlock, let's call below functions to commit
+cached bios in GC_MERGE path of f2fs_balance_fs() as the same as we did
+in NOGC_MERGE path.
+- f2fs_submit_merged_write(sbi, DATA);
+- f2fs_submit_all_merged_ipu_writes(sbi);
+
+Cc: stable@kernel.org
+Fixes: 351df4b20115 ("f2fs: add segment operations")
+Cc: Ruipeng Qi <ruipengqi3@gmail.com>
+Reported: Sandeep Dhavale <dhavale@google.com>
+Signed-off-by: Chao Yu <chao@kernel.org>
+Signed-off-by: Chao Yu <chaseyu@google.com>
+Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/f2fs/segment.c |   14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+--- a/fs/f2fs/segment.c
++++ b/fs/f2fs/segment.c
+@@ -434,6 +434,13 @@ void f2fs_balance_fs(struct f2fs_sb_info
+       if (has_enough_free_secs(sbi, 0, 0))
+               return;
++      /*
++       * Submit all cached OPU/IPU DATA bios before triggering
++       * foreground GC to avoid potential deadlocks.
++       */
++      f2fs_submit_merged_write(sbi, DATA);
++      f2fs_submit_all_merged_ipu_writes(sbi);
++
+       if (test_opt(sbi, GC_MERGE) && sbi->gc_thread &&
+                               sbi->gc_thread->f2fs_gc_task) {
+               DEFINE_WAIT(wait);
+@@ -452,13 +459,6 @@ void f2fs_balance_fs(struct f2fs_sb_info
+                       .err_gc_skipped = false,
+                       .nr_free_secs = 1 };
+-              /*
+-               * Submit all cached OPU/IPU DATA bios before triggering
+-               * foreground GC to avoid potential deadlocks.
+-               */
+-              f2fs_submit_merged_write(sbi, DATA);
+-              f2fs_submit_all_merged_ipu_writes(sbi);
+-
+               f2fs_down_write(&sbi->gc_lock);
+               stat_inc_gc_call_count(sbi, FOREGROUND);
+               f2fs_gc(sbi, &gc_control);
diff --git a/queue-6.12/f2fs-validate-orphan-inode-entry-count.patch b/queue-6.12/f2fs-validate-orphan-inode-entry-count.patch
new file mode 100644 (file)
index 0000000..54a2e8f
--- /dev/null
@@ -0,0 +1,79 @@
+From stable+bounces-271761-greg=kroah.com@vger.kernel.org Fri Jul  3 14:42:14 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri,  3 Jul 2026 08:34:00 -0400
+Subject: f2fs: validate orphan inode entry count
+To: stable@vger.kernel.org
+Cc: Wenjie Qi <qwjhust@gmail.com>, stable@kernel.org, Wenjie Qi <qiwenjie@xiaomi.com>, Chao Yu <chao@kernel.org>, Jaegeuk Kim <jaegeuk@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260703123400.3946185-1-sashal@kernel.org>
+
+From: Wenjie Qi <qwjhust@gmail.com>
+
+[ Upstream commit 846c499a65816d13f1186e3090e825e8bb8bcb8b ]
+
+f2fs_recover_orphan_inodes() trusts the orphan block entry_count when
+replaying orphan inodes from the checkpoint pack. A corrupted entry_count
+larger than F2FS_ORPHANS_PER_BLOCK makes the recovery loop read past the
+ino[] array and interpret footer or following data as inode numbers.
+
+On a crafted image, mounting an unpatched kernel can drive orphan recovery
+into f2fs_bug_on() and panic the kernel. Validate entry_count before
+consuming entries so corrupted checkpoint data fails the mount with
+-EFSCORRUPTED and requests fsck instead.
+
+Set ERROR_INCONSISTENT_ORPHAN as well, so the corruption reason can be
+recorded in the superblock s_errors[] field. This gives fsck a persistent
+hint even though mount-time orphan recovery failure may leave no chance to
+persist SBI_NEED_FSCK through a checkpoint.
+
+Cc: stable@kernel.org
+Fixes: 127e670abfa7 ("f2fs: add checkpoint operations")
+Signed-off-by: Wenjie Qi <qiwenjie@xiaomi.com>
+Reviewed-by: Chao Yu <chao@kernel.org>
+Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/f2fs/checkpoint.c    |   14 +++++++++++++-
+ include/linux/f2fs_fs.h |    1 +
+ 2 files changed, 14 insertions(+), 1 deletion(-)
+
+--- a/fs/f2fs/checkpoint.c
++++ b/fs/f2fs/checkpoint.c
+@@ -759,6 +759,7 @@ int f2fs_recover_orphan_inodes(struct f2
+       for (i = 0; i < orphan_blocks; i++) {
+               struct page *page;
+               struct f2fs_orphan_block *orphan_blk;
++              unsigned int entry_count;
+               page = f2fs_get_meta_page(sbi, start_blk + i);
+               if (IS_ERR(page)) {
+@@ -767,7 +768,18 @@ int f2fs_recover_orphan_inodes(struct f2
+               }
+               orphan_blk = (struct f2fs_orphan_block *)page_address(page);
+-              for (j = 0; j < le32_to_cpu(orphan_blk->entry_count); j++) {
++              entry_count = le32_to_cpu(orphan_blk->entry_count);
++              if (entry_count > F2FS_ORPHANS_PER_BLOCK) {
++                      f2fs_err(sbi, "invalid orphan inode entry count %u",
++                               entry_count);
++                      set_sbi_flag(sbi, SBI_NEED_FSCK);
++                      f2fs_handle_error(sbi, ERROR_INCONSISTENT_ORPHAN);
++                      err = -EFSCORRUPTED;
++                      f2fs_put_page(page, 1);
++                      goto out;
++              }
++
++              for (j = 0; j < entry_count; j++) {
+                       nid_t ino = le32_to_cpu(orphan_blk->ino[j]);
+                       err = recover_orphan_inode(sbi, ino);
+--- a/include/linux/f2fs_fs.h
++++ b/include/linux/f2fs_fs.h
+@@ -104,6 +104,7 @@ enum f2fs_error {
+       ERROR_CORRUPTED_XATTR,
+       ERROR_INVALID_NODE_REFERENCE,
+       ERROR_INCONSISTENT_NAT,
++      ERROR_INCONSISTENT_ORPHAN,
+       ERROR_MAX,
+ };
diff --git a/queue-6.12/fbdev-fbcon-fix-out-of-bounds-read-in-err_out-of-fbcon_do_set_font.patch b/queue-6.12/fbdev-fbcon-fix-out-of-bounds-read-in-err_out-of-fbcon_do_set_font.patch
new file mode 100644 (file)
index 0000000..0a036e5
--- /dev/null
@@ -0,0 +1,70 @@
+From stable+bounces-271995-greg=kroah.com@vger.kernel.org Sun Jul  5 05:11:47 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat,  4 Jul 2026 23:11:39 -0400
+Subject: fbdev: fbcon: fix out-of-bounds read in err_out of fbcon_do_set_font()
+To: stable@vger.kernel.org
+Cc: Mingyu Wang <25181214217@stu.xidian.edu.cn>, Thomas Zimmermann <tzimmermann@suse.de>, Helge Deller <deller@gmx.de>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260705031139.1600541-1-sashal@kernel.org>
+
+From: Mingyu Wang <25181214217@stu.xidian.edu.cn>
+
+[ Upstream commit 8fdc8c2057eea08d40ce2c8eed41ff9e451c65c2 ]
+
+When fbcon_do_set_font() fails (e.g., due to a memory allocation failure
+inside vc_resize() under heavy memory pressure), it jumps to the `err_out`
+label to roll back the console state. However, the current rollback logic
+forgets to restore the `hi_font` state, leading to a severe state machine
+corruption.
+
+Earlier in the function, `set_vc_hi_font()` might be called to change
+`vc->vc_hi_font_mask` and mutate the screen buffer. If `vc_resize()`
+subsequently fails, the `err_out` path restores `vc_font.charcount`
+but entirely skips rolling back the `vc_hi_font_mask` and the screen
+buffer.
+
+This mismatch leaves the terminal in a desynchronized state. Because
+`vc_hi_font_mask` remains set, the VT subsystem will still accept
+character indices greater than 255 from userspace and write them to the
+screen buffer. Subsequent rendering calls (e.g., `fbcon_putcs()`) will
+then use these inflated indices to access the reverted, 256-character
+font array, leading to a deterministic out-of-bounds read and potential
+kernel memory disclosure.
+
+Fix this by adding the missing rollback logic for the `hi_font` mask
+and screen buffer in the error path.
+
+Fixes: a5a923038d70 ("fbdev: fbcon: Properly revert changes when vc_resize() failed")
+Cc: stable@vger.kernel.org
+Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn>
+Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de>
+Signed-off-by: Helge Deller <deller@gmx.de>
+[ Adjust context ]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/video/fbdev/core/fbcon.c |    7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/drivers/video/fbdev/core/fbcon.c
++++ b/drivers/video/fbdev/core/fbcon.c
+@@ -2427,6 +2427,7 @@ static int fbcon_do_set_font(struct vc_d
+       struct fbcon_display *p = &fb_display[vc->vc_num];
+       int resize, ret, old_userfont, old_width, old_height, old_charcount;
+       u8 *old_data = vc->vc_font.data;
++      unsigned short old_hi_font_mask = vc->vc_hi_font_mask;
+       resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
+       vc->vc_font.data = (void *)(p->fontdata = data);
+@@ -2480,6 +2481,12 @@ err_out:
+       vc->vc_font.height = old_height;
+       vc->vc_font.charcount = old_charcount;
++      /* Restore the hi_font state and screen buffer */
++      if (old_hi_font_mask && !vc->vc_hi_font_mask)
++              set_vc_hi_font(vc, true);
++      else if (!old_hi_font_mask && vc->vc_hi_font_mask)
++              set_vc_hi_font(vc, false);
++
+       return ret;
+ }
diff --git a/queue-6.12/locking-rtmutex-make-sure-we-wake-anything-on-the-wake_q-when-we-release-the-lock-wait_lock.patch b/queue-6.12/locking-rtmutex-make-sure-we-wake-anything-on-the-wake_q-when-we-release-the-lock-wait_lock.patch
new file mode 100644 (file)
index 0000000..02e17e1
--- /dev/null
@@ -0,0 +1,105 @@
+From stable+bounces-272218-greg=kroah.com@vger.kernel.org Mon Jul  6 15:03:44 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon,  6 Jul 2026 08:50:05 -0400
+Subject: locking/rtmutex: Make sure we wake anything on the wake_q when we release the lock->wait_lock
+To: stable@vger.kernel.org
+Cc: John Stultz <jstultz@google.com>, Bert Karwatzki <spasswolf@web.de>, "Peter Zijlstra (Intel)" <peterz@infradead.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260706125005.2225064-1-sashal@kernel.org>
+
+From: John Stultz <jstultz@google.com>
+
+[ Upstream commit 4a077914578183ec397ad09f7156a357e00e5d72 ]
+
+Bert reported seeing occasional boot hangs when running with
+PREEPT_RT and bisected it down to commit 894d1b3db41c
+("locking/mutex: Remove wakeups from under mutex::wait_lock").
+
+It looks like I missed a few spots where we drop the wait_lock and
+potentially call into schedule without waking up the tasks on the
+wake_q structure. Since the tasks being woken are ww_mutex tasks
+they need to be able to run to release the mutex and unblock the
+task that currently is planning to wake them. Thus we can deadlock.
+
+So make sure we wake the wake_q tasks when we unlock the wait_lock.
+
+Closes: https://lore.kernel.org/lkml/20241211182502.2915-1-spasswolf@web.de
+Fixes: 894d1b3db41c ("locking/mutex: Remove wakeups from under mutex::wait_lock")
+Reported-by: Bert Karwatzki <spasswolf@web.de>
+Signed-off-by: John Stultz <jstultz@google.com>
+Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
+Link: https://lkml.kernel.org/r/20241212222138.2400498-1-jstultz@google.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ kernel/locking/rtmutex.c     |   18 ++++++++++++++++--
+ kernel/locking/rtmutex_api.c |    2 +-
+ 2 files changed, 17 insertions(+), 3 deletions(-)
+
+--- a/kernel/locking/rtmutex.c
++++ b/kernel/locking/rtmutex.c
+@@ -1292,7 +1292,13 @@ static int __sched task_blocks_on_rt_mut
+        */
+       get_task_struct(owner);
++      preempt_disable();
+       raw_spin_unlock_irq(&lock->wait_lock);
++      /* wake up any tasks on the wake_q before calling rt_mutex_adjust_prio_chain */
++      wake_up_q(wake_q);
++      wake_q_init(wake_q);
++      preempt_enable();
++
+       res = rt_mutex_adjust_prio_chain(owner, chwalk, lock,
+                                        next_lock, waiter, task);
+@@ -1602,6 +1608,7 @@ static void __sched remove_waiter(struct
+  *                     or TASK_UNINTERRUPTIBLE)
+  * @timeout:           the pre-initialized and started timer, or NULL for none
+  * @waiter:            the pre-initialized rt_mutex_waiter
++ * @wake_q:            wake_q of tasks to wake when we drop the lock->wait_lock
+  *
+  * Must be called with lock->wait_lock held and interrupts disabled
+  */
+@@ -1609,7 +1616,8 @@ static int __sched rt_mutex_slowlock_blo
+                                          struct ww_acquire_ctx *ww_ctx,
+                                          unsigned int state,
+                                          struct hrtimer_sleeper *timeout,
+-                                         struct rt_mutex_waiter *waiter)
++                                         struct rt_mutex_waiter *waiter,
++                                         struct wake_q_head *wake_q)
+ {
+       struct rt_mutex *rtm = container_of(lock, struct rt_mutex, rtmutex);
+       struct task_struct *owner;
+@@ -1639,7 +1647,13 @@ static int __sched rt_mutex_slowlock_blo
+                       owner = rt_mutex_owner(lock);
+               else
+                       owner = NULL;
++              preempt_disable();
+               raw_spin_unlock_irq(&lock->wait_lock);
++              if (wake_q) {
++                      wake_up_q(wake_q);
++                      wake_q_init(wake_q);
++              }
++              preempt_enable();
+               if (!owner || !rtmutex_spin_on_owner(lock, waiter, owner))
+                       rt_mutex_schedule();
+@@ -1713,7 +1727,7 @@ static int __sched __rt_mutex_slowlock(s
+       ret = task_blocks_on_rt_mutex(lock, waiter, current, ww_ctx, chwalk, wake_q);
+       if (likely(!ret))
+-              ret = rt_mutex_slowlock_block(lock, ww_ctx, state, NULL, waiter);
++              ret = rt_mutex_slowlock_block(lock, ww_ctx, state, NULL, waiter, wake_q);
+       if (likely(!ret)) {
+               /* acquired the lock */
+--- a/kernel/locking/rtmutex_api.c
++++ b/kernel/locking/rtmutex_api.c
+@@ -383,7 +383,7 @@ int __sched rt_mutex_wait_proxy_lock(str
+       raw_spin_lock_irq(&lock->wait_lock);
+       /* sleep on the mutex */
+       set_current_state(TASK_INTERRUPTIBLE);
+-      ret = rt_mutex_slowlock_block(lock, NULL, TASK_INTERRUPTIBLE, to, waiter);
++      ret = rt_mutex_slowlock_block(lock, NULL, TASK_INTERRUPTIBLE, to, waiter, NULL);
+       /*
+        * try_to_take_rt_mutex() sets the waiter bit unconditionally. We might
+        * have to fix that up.
diff --git a/queue-6.12/nfs_common-rename-functions-that-invalidate-localio-nfs_clients.patch b/queue-6.12/nfs_common-rename-functions-that-invalidate-localio-nfs_clients.patch
new file mode 100644 (file)
index 0000000..0def407
--- /dev/null
@@ -0,0 +1,105 @@
+From stable+bounces-271997-greg=kroah.com@vger.kernel.org Sun Jul  5 05:11:59 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat,  4 Jul 2026 23:11:47 -0400
+Subject: nfs_common: rename functions that invalidate LOCALIO nfs_clients
+To: stable@vger.kernel.org
+Cc: Mike Snitzer <snitzer@kernel.org>, NeilBrown <neilb@suse.de>, Jeff Layton <jlayton@kernel.org>, Anna Schumaker <anna.schumaker@oracle.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260705031150.1600970-2-sashal@kernel.org>
+
+From: Mike Snitzer <snitzer@kernel.org>
+
+[ Upstream commit b49f049a22227df701bfbca083d6cc859496e615 ]
+
+Rename nfs_uuid_invalidate_one_client to nfs_localio_disable_client.
+Rename nfs_uuid_invalidate_clients to nfs_localio_invalidate_clients.
+
+Signed-off-by: Mike Snitzer <snitzer@kernel.org>
+Reviewed-by: NeilBrown <neilb@suse.de>
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
+Stable-dep-of: 2c6bb3c40bc2 ("NFSv4/flexfiles: reject zero filehandle version count")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfs/localio.c           |    2 +-
+ fs/nfs_common/nfslocalio.c |    8 ++++----
+ fs/nfsd/nfsctl.c           |    4 ++--
+ include/linux/nfslocalio.h |    5 +++--
+ 4 files changed, 10 insertions(+), 9 deletions(-)
+
+--- a/fs/nfs/localio.c
++++ b/fs/nfs/localio.c
+@@ -141,7 +141,7 @@ void nfs_local_disable(struct nfs_client
+       spin_lock(&clp->cl_localio_lock);
+       if (test_and_clear_bit(NFS_CS_LOCAL_IO, &clp->cl_flags)) {
+               trace_nfs_local_disable(clp);
+-              nfs_uuid_invalidate_one_client(&clp->cl_uuid);
++              nfs_localio_disable_client(&clp->cl_uuid);
+       }
+       spin_unlock(&clp->cl_localio_lock);
+ }
+--- a/fs/nfs_common/nfslocalio.c
++++ b/fs/nfs_common/nfslocalio.c
+@@ -107,7 +107,7 @@ static void nfs_uuid_put_locked(nfs_uuid
+       list_del_init(&nfs_uuid->list);
+ }
+-void nfs_uuid_invalidate_clients(struct list_head *list)
++void nfs_localio_invalidate_clients(struct list_head *list)
+ {
+       nfs_uuid_t *nfs_uuid, *tmp;
+@@ -116,9 +116,9 @@ void nfs_uuid_invalidate_clients(struct
+               nfs_uuid_put_locked(nfs_uuid);
+       spin_unlock(&nfs_uuid_lock);
+ }
+-EXPORT_SYMBOL_GPL(nfs_uuid_invalidate_clients);
++EXPORT_SYMBOL_GPL(nfs_localio_invalidate_clients);
+-void nfs_uuid_invalidate_one_client(nfs_uuid_t *nfs_uuid)
++void nfs_localio_disable_client(nfs_uuid_t *nfs_uuid)
+ {
+       if (nfs_uuid->net) {
+               spin_lock(&nfs_uuid_lock);
+@@ -126,7 +126,7 @@ void nfs_uuid_invalidate_one_client(nfs_
+               spin_unlock(&nfs_uuid_lock);
+       }
+ }
+-EXPORT_SYMBOL_GPL(nfs_uuid_invalidate_one_client);
++EXPORT_SYMBOL_GPL(nfs_localio_disable_client);
+ /*
+  * Caller is responsible for calling nfsd_net_put and
+--- a/fs/nfsd/nfsctl.c
++++ b/fs/nfsd/nfsctl.c
+@@ -2296,14 +2296,14 @@ out_export_error:
+  * nfsd_net_pre_exit - Disconnect localio clients from net namespace
+  * @net: a network namespace that is about to be destroyed
+  *
+- * This invalidated ->net pointers held by localio clients
++ * This invalidates ->net pointers held by localio clients
+  * while they can still safely access nn->counter.
+  */
+ static __net_exit void nfsd_net_pre_exit(struct net *net)
+ {
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+-      nfs_uuid_invalidate_clients(&nn->local_clients);
++      nfs_localio_invalidate_clients(&nn->local_clients);
+ }
+ #endif
+--- a/include/linux/nfslocalio.h
++++ b/include/linux/nfslocalio.h
+@@ -37,8 +37,9 @@ bool nfs_uuid_begin(nfs_uuid_t *);
+ void nfs_uuid_end(nfs_uuid_t *);
+ void nfs_uuid_is_local(const uuid_t *, struct list_head *,
+                      struct net *, struct auth_domain *, struct module *);
+-void nfs_uuid_invalidate_clients(struct list_head *list);
+-void nfs_uuid_invalidate_one_client(nfs_uuid_t *nfs_uuid);
++
++void nfs_localio_disable_client(nfs_uuid_t *nfs_uuid);
++void nfs_localio_invalidate_clients(struct list_head *list);
+ /* localio needs to map filehandle -> struct nfsd_file */
+ extern struct nfsd_file *
diff --git a/queue-6.12/nfsd-add-nfsd_file_-get-put-to-nfs_to-nfsd_localio_operations.patch b/queue-6.12/nfsd-add-nfsd_file_-get-put-to-nfs_to-nfsd_localio_operations.patch
new file mode 100644 (file)
index 0000000..9d81bd3
--- /dev/null
@@ -0,0 +1,49 @@
+From stable+bounces-271996-greg=kroah.com@vger.kernel.org Sun Jul  5 05:11:57 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat,  4 Jul 2026 23:11:46 -0400
+Subject: nfsd: add nfsd_file_{get,put} to 'nfs_to' nfsd_localio_operations
+To: stable@vger.kernel.org
+Cc: Mike Snitzer <snitzer@kernel.org>, Jeff Layton <jlayton@kernel.org>, Chuck Lever <chuck.lever@oracle.com>, Anna Schumaker <anna.schumaker@oracle.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260705031150.1600970-1-sashal@kernel.org>
+
+From: Mike Snitzer <snitzer@kernel.org>
+
+[ Upstream commit a61466315d7afca032342183a57e62d5e3a3157c ]
+
+In later a commit LOCALIO must call both nfsd_file_get and
+nfsd_file_put to manage extra nfsd_file references.
+
+Signed-off-by: Mike Snitzer <snitzer@kernel.org>
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Acked-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
+Stable-dep-of: 2c6bb3c40bc2 ("NFSv4/flexfiles: reject zero filehandle version count")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfsd/localio.c          |    2 ++
+ include/linux/nfslocalio.h |    2 ++
+ 2 files changed, 4 insertions(+)
+
+--- a/fs/nfsd/localio.c
++++ b/fs/nfsd/localio.c
+@@ -29,6 +29,8 @@ static const struct nfsd_localio_operati
+       .nfsd_net_put  = nfsd_net_put,
+       .nfsd_open_local_fh = nfsd_open_local_fh,
+       .nfsd_file_put_local = nfsd_file_put_local,
++      .nfsd_file_get = nfsd_file_get,
++      .nfsd_file_put = nfsd_file_put,
+       .nfsd_file_file = nfsd_file_file,
+ };
+--- a/include/linux/nfslocalio.h
++++ b/include/linux/nfslocalio.h
+@@ -56,6 +56,8 @@ struct nfsd_localio_operations {
+                                               const struct nfs_fh *,
+                                               const fmode_t);
+       struct net *(*nfsd_file_put_local)(struct nfsd_file *);
++      struct nfsd_file *(*nfsd_file_get)(struct nfsd_file *);
++      void (*nfsd_file_put)(struct nfsd_file *);
+       struct file *(*nfsd_file_file)(struct nfsd_file *);
+ } ____cacheline_aligned;
diff --git a/queue-6.12/nfsv4-flexfiles-add-data-structure-support-for-striped-layouts.patch b/queue-6.12/nfsv4-flexfiles-add-data-structure-support-for-striped-layouts.patch
new file mode 100644 (file)
index 0000000..c7865a9
--- /dev/null
@@ -0,0 +1,625 @@
+From stable+bounces-271999-greg=kroah.com@vger.kernel.org Sun Jul  5 05:12:08 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat,  4 Jul 2026 23:11:49 -0400
+Subject: NFSv4/flexfiles: Add data structure support for striped layouts
+To: stable@vger.kernel.org
+Cc: Jonathan Curley <jcurley@purestorage.com>, Anna Schumaker <anna.schumaker@oracle.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260705031150.1600970-4-sashal@kernel.org>
+
+From: Jonathan Curley <jcurley@purestorage.com>
+
+[ Upstream commit d442670c0f63c46b7f348f68fb2002af597708f2 ]
+
+Adds a new struct nfs4_ff_layout_ds_stripe that represents a data
+server stripe within a layout. A new dynamically allocated array of
+this type has been added to nfs4_ff_layout_mirror and per stripe
+configuration information has been moved from the mirror type to the
+stripe based on the RFC.
+
+Signed-off-by: Jonathan Curley <jcurley@purestorage.com>
+Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
+Stable-dep-of: 2c6bb3c40bc2 ("NFSv4/flexfiles: reject zero filehandle version count")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfs/flexfilelayout/flexfilelayout.c    |  149 ++++++++++++++++--------------
+ fs/nfs/flexfilelayout/flexfilelayout.h    |   27 +++--
+ fs/nfs/flexfilelayout/flexfilelayoutdev.c |   54 +++++-----
+ 3 files changed, 128 insertions(+), 102 deletions(-)
+
+--- a/fs/nfs/flexfilelayout/flexfilelayout.c
++++ b/fs/nfs/flexfilelayout/flexfilelayout.c
+@@ -183,13 +183,13 @@ static bool ff_mirror_match_fh(const str
+ {
+       int i, j;
+-      if (m1->fh_versions_cnt != m2->fh_versions_cnt)
++      if (m1->dss[0].fh_versions_cnt != m2->dss[0].fh_versions_cnt)
+               return false;
+-      for (i = 0; i < m1->fh_versions_cnt; i++) {
++      for (i = 0; i < m1->dss[0].fh_versions_cnt; i++) {
+               bool found_fh = false;
+-              for (j = 0; j < m2->fh_versions_cnt; j++) {
+-                      if (nfs_compare_fh(&m1->fh_versions[i],
+-                                      &m2->fh_versions[j]) == 0) {
++              for (j = 0; j < m2->dss[0].fh_versions_cnt; j++) {
++                      if (nfs_compare_fh(&m1->dss[0].fh_versions[i],
++                                      &m2->dss[0].fh_versions[j]) == 0) {
+                               found_fh = true;
+                               break;
+                       }
+@@ -210,7 +210,8 @@ ff_layout_add_mirror(struct pnfs_layout_
+       spin_lock(&inode->i_lock);
+       list_for_each_entry(pos, &ff_layout->mirrors, mirrors) {
+-              if (memcmp(&mirror->devid, &pos->devid, sizeof(pos->devid)) != 0)
++              if (memcmp(&mirror->dss[0].devid, &pos->dss[0].devid,
++                         sizeof(pos->dss[0].devid)) != 0)
+                       continue;
+               if (!ff_mirror_match_fh(mirror, pos))
+                       continue;
+@@ -238,30 +239,46 @@ ff_layout_remove_mirror(struct nfs4_ff_l
+       mirror->layout = NULL;
+ }
+-static struct nfs4_ff_layout_mirror *ff_layout_alloc_mirror(gfp_t gfp_flags)
++static struct nfs4_ff_layout_mirror *ff_layout_alloc_mirror(u32 dss_count,
++                                                          gfp_t gfp_flags)
+ {
+       struct nfs4_ff_layout_mirror *mirror;
+       mirror = kzalloc(sizeof(*mirror), gfp_flags);
+-      if (mirror != NULL) {
+-              spin_lock_init(&mirror->lock);
+-              refcount_set(&mirror->ref, 1);
+-              INIT_LIST_HEAD(&mirror->mirrors);
++      if (mirror == NULL)
++              return NULL;
++
++      spin_lock_init(&mirror->lock);
++      refcount_set(&mirror->ref, 1);
++      INIT_LIST_HEAD(&mirror->mirrors);
++
++      mirror->dss_count = dss_count;
++      mirror->dss =
++              kcalloc(dss_count, sizeof(struct nfs4_ff_layout_ds_stripe),
++                      gfp_flags);
++      if (mirror->dss == NULL) {
++              kfree(mirror);
++              return NULL;
+       }
++
+       return mirror;
+ }
+ static void ff_layout_free_mirror(struct nfs4_ff_layout_mirror *mirror)
+ {
+-      const struct cred *cred;
++      const struct cred       *cred;
++      int dss_id = 0;
+       ff_layout_remove_mirror(mirror);
+-      kfree(mirror->fh_versions);
+-      cred = rcu_access_pointer(mirror->ro_cred);
++
++      kfree(mirror->dss[dss_id].fh_versions);
++      cred = rcu_access_pointer(mirror->dss[dss_id].ro_cred);
+       put_cred(cred);
+-      cred = rcu_access_pointer(mirror->rw_cred);
++      cred = rcu_access_pointer(mirror->dss[dss_id].rw_cred);
+       put_cred(cred);
+-      nfs4_ff_layout_put_deviceid(mirror->mirror_ds);
++      nfs4_ff_layout_put_deviceid(mirror->dss[dss_id].mirror_ds);
++
++      kfree(mirror->dss);
+       kfree(mirror);
+ }
+@@ -371,8 +388,8 @@ static void ff_layout_sort_mirrors(struc
+       for (i = 0; i < fls->mirror_array_cnt - 1; i++) {
+               for (j = i + 1; j < fls->mirror_array_cnt; j++)
+-                      if (fls->mirror_array[i]->efficiency <
+-                          fls->mirror_array[j]->efficiency)
++                      if (fls->mirror_array[i]->dss[0].efficiency <
++                          fls->mirror_array[j]->dss[0].efficiency)
+                               swap(fls->mirror_array[i],
+                                    fls->mirror_array[j]);
+       }
+@@ -426,35 +443,35 @@ ff_layout_alloc_lseg(struct pnfs_layout_
+       fls->mirror_array_cnt = mirror_array_cnt;
+       fls->stripe_unit = stripe_unit;
++      u32 dss_count = 0;
+       for (i = 0; i < fls->mirror_array_cnt; i++) {
+               struct nfs4_ff_layout_mirror *mirror;
+               struct cred *kcred;
+               const struct cred __rcu *cred;
+               kuid_t uid;
+               kgid_t gid;
+-              u32 ds_count, fh_count, id;
+-              int j;
++              u32 fh_count, id;
++              int j, dss_id = 0;
+               rc = -EIO;
+               p = xdr_inline_decode(&stream, 4);
+               if (!p)
+                       goto out_err_free;
+-              ds_count = be32_to_cpup(p);
++
++              dss_count = be32_to_cpup(p);
+               /* FIXME: allow for striping? */
+-              if (ds_count != 1)
++              if (dss_count != 1)
+                       goto out_err_free;
+-              fls->mirror_array[i] = ff_layout_alloc_mirror(gfp_flags);
++              fls->mirror_array[i] = ff_layout_alloc_mirror(dss_count, gfp_flags);
+               if (fls->mirror_array[i] == NULL) {
+                       rc = -ENOMEM;
+                       goto out_err_free;
+               }
+-              fls->mirror_array[i]->ds_count = ds_count;
+-
+               /* deviceid */
+-              rc = decode_deviceid(&stream, &fls->mirror_array[i]->devid);
++              rc = decode_deviceid(&stream, &fls->mirror_array[i]->dss[dss_id].devid);
+               if (rc)
+                       goto out_err_free;
+@@ -463,10 +480,10 @@ ff_layout_alloc_lseg(struct pnfs_layout_
+               p = xdr_inline_decode(&stream, 4);
+               if (!p)
+                       goto out_err_free;
+-              fls->mirror_array[i]->efficiency = be32_to_cpup(p);
++              fls->mirror_array[i]->dss[dss_id].efficiency = be32_to_cpup(p);
+               /* stateid */
+-              rc = decode_pnfs_stateid(&stream, &fls->mirror_array[i]->stateid);
++              rc = decode_pnfs_stateid(&stream, &fls->mirror_array[i]->dss[dss_id].stateid);
+               if (rc)
+                       goto out_err_free;
+@@ -477,22 +494,22 @@ ff_layout_alloc_lseg(struct pnfs_layout_
+                       goto out_err_free;
+               fh_count = be32_to_cpup(p);
+-              fls->mirror_array[i]->fh_versions =
+-                      kcalloc(fh_count, sizeof(struct nfs_fh),
+-                              gfp_flags);
+-              if (fls->mirror_array[i]->fh_versions == NULL) {
++              fls->mirror_array[i]->dss[dss_id].fh_versions =
++                  kcalloc(fh_count, sizeof(struct nfs_fh),
++                          gfp_flags);
++              if (fls->mirror_array[i]->dss[dss_id].fh_versions == NULL) {
+                       rc = -ENOMEM;
+                       goto out_err_free;
+               }
+               for (j = 0; j < fh_count; j++) {
+                       rc = decode_nfs_fh(&stream,
+-                                         &fls->mirror_array[i]->fh_versions[j]);
++                                         &fls->mirror_array[i]->dss[dss_id].fh_versions[j]);
+                       if (rc)
+                               goto out_err_free;
+               }
+-              fls->mirror_array[i]->fh_versions_cnt = fh_count;
++              fls->mirror_array[i]->dss[dss_id].fh_versions_cnt = fh_count;
+               /* user */
+               rc = decode_name(&stream, &id);
+@@ -523,19 +540,21 @@ ff_layout_alloc_lseg(struct pnfs_layout_
+               cred = RCU_INITIALIZER(kcred);
+               if (lgr->range.iomode == IOMODE_READ)
+-                      rcu_assign_pointer(fls->mirror_array[i]->ro_cred, cred);
++                      rcu_assign_pointer(fls->mirror_array[i]->dss[dss_id].ro_cred, cred);
+               else
+-                      rcu_assign_pointer(fls->mirror_array[i]->rw_cred, cred);
++                      rcu_assign_pointer(fls->mirror_array[i]->dss[dss_id].rw_cred, cred);
+               mirror = ff_layout_add_mirror(lh, fls->mirror_array[i]);
+               if (mirror != fls->mirror_array[i]) {
+                       /* swap cred ptrs so free_mirror will clean up old */
+                       if (lgr->range.iomode == IOMODE_READ) {
+-                              cred = xchg(&mirror->ro_cred, fls->mirror_array[i]->ro_cred);
+-                              rcu_assign_pointer(fls->mirror_array[i]->ro_cred, cred);
++                              cred = xchg(&mirror->dss[dss_id].ro_cred,
++                                          fls->mirror_array[i]->dss[dss_id].ro_cred);
++                              rcu_assign_pointer(fls->mirror_array[i]->dss[dss_id].ro_cred, cred);
+                       } else {
+-                              cred = xchg(&mirror->rw_cred, fls->mirror_array[i]->rw_cred);
+-                              rcu_assign_pointer(fls->mirror_array[i]->rw_cred, cred);
++                              cred = xchg(&mirror->dss[dss_id].rw_cred,
++                                          fls->mirror_array[i]->dss[dss_id].rw_cred);
++                              rcu_assign_pointer(fls->mirror_array[i]->dss[dss_id].rw_cred, cred);
+                       }
+                       ff_layout_free_mirror(fls->mirror_array[i]);
+                       fls->mirror_array[i] = mirror;
+@@ -623,8 +642,8 @@ nfs4_ff_layoutstat_start_io(struct nfs4_
+       struct nfs4_flexfile_layout *ffl = FF_LAYOUT_FROM_HDR(mirror->layout);
+       nfs4_ff_start_busy_timer(&layoutstat->busy_timer, now);
+-      if (!mirror->start_time)
+-              mirror->start_time = now;
++      if (!mirror->dss[0].start_time)
++              mirror->dss[0].start_time = now;
+       if (mirror->report_interval != 0)
+               report_interval = (s64)mirror->report_interval * 1000LL;
+       else if (layoutstats_timer != 0)
+@@ -679,8 +698,8 @@ nfs4_ff_layout_stat_io_start_read(struct
+       bool report;
+       spin_lock(&mirror->lock);
+-      report = nfs4_ff_layoutstat_start_io(mirror, &mirror->read_stat, now);
+-      nfs4_ff_layout_stat_io_update_requested(&mirror->read_stat, requested);
++      report = nfs4_ff_layoutstat_start_io(mirror, &mirror->dss[0].read_stat, now);
++      nfs4_ff_layout_stat_io_update_requested(&mirror->dss[0].read_stat, requested);
+       set_bit(NFS4_FF_MIRROR_STAT_AVAIL, &mirror->flags);
+       spin_unlock(&mirror->lock);
+@@ -695,7 +714,7 @@ nfs4_ff_layout_stat_io_end_read(struct r
+               __u64 completed)
+ {
+       spin_lock(&mirror->lock);
+-      nfs4_ff_layout_stat_io_update_completed(&mirror->read_stat,
++      nfs4_ff_layout_stat_io_update_completed(&mirror->dss[0].read_stat,
+                       requested, completed,
+                       ktime_get(), task->tk_start);
+       set_bit(NFS4_FF_MIRROR_STAT_AVAIL, &mirror->flags);
+@@ -710,8 +729,8 @@ nfs4_ff_layout_stat_io_start_write(struc
+       bool report;
+       spin_lock(&mirror->lock);
+-      report = nfs4_ff_layoutstat_start_io(mirror , &mirror->write_stat, now);
+-      nfs4_ff_layout_stat_io_update_requested(&mirror->write_stat, requested);
++      report = nfs4_ff_layoutstat_start_io(mirror, &mirror->dss[0].write_stat, now);
++      nfs4_ff_layout_stat_io_update_requested(&mirror->dss[0].write_stat, requested);
+       set_bit(NFS4_FF_MIRROR_STAT_AVAIL, &mirror->flags);
+       spin_unlock(&mirror->lock);
+@@ -730,7 +749,7 @@ nfs4_ff_layout_stat_io_end_write(struct
+               requested = completed = 0;
+       spin_lock(&mirror->lock);
+-      nfs4_ff_layout_stat_io_update_completed(&mirror->write_stat,
++      nfs4_ff_layout_stat_io_update_completed(&mirror->dss[0].write_stat,
+                       requested, completed, ktime_get(), task->tk_start);
+       set_bit(NFS4_FF_MIRROR_STAT_AVAIL, &mirror->flags);
+       spin_unlock(&mirror->lock);
+@@ -772,7 +791,7 @@ ff_layout_choose_ds_for_read(struct pnfs
+                       continue;
+               if (check_device &&
+-                  nfs4_test_deviceid_unavailable(&mirror->mirror_ds->id_node)) {
++                  nfs4_test_deviceid_unavailable(&mirror->dss[0].mirror_ds->id_node)) {
+                       // reinitialize the error state in case if this is the last iteration
+                       ds = ERR_PTR(-EINVAL);
+                       continue;
+@@ -881,7 +900,7 @@ retry:
+       mirror = FF_LAYOUT_COMP(pgio->pg_lseg, ds_idx);
+       pgm = &pgio->pg_mirrors[0];
+-      pgm->pg_bsize = mirror->mirror_ds->ds_versions[0].rsize;
++      pgm->pg_bsize = mirror->dss[0].mirror_ds->ds_versions[0].rsize;
+       pgio->pg_mirror_idx = ds_idx;
+       return;
+@@ -953,7 +972,7 @@ retry:
+                       goto retry;
+               }
+               pgm = &pgio->pg_mirrors[i];
+-              pgm->pg_bsize = mirror->mirror_ds->ds_versions[0].wsize;
++              pgm->pg_bsize = mirror->dss[0].mirror_ds->ds_versions[0].wsize;
+       }
+       if (NFS_SERVER(pgio->pg_inode)->flags &
+@@ -2013,7 +2032,7 @@ select_ds_fh_from_commit(struct pnfs_lay
+       /* FIXME: Assume that there is only one NFS version available
+        * for the DS.
+        */
+-      return &flseg->mirror_array[i]->fh_versions[0];
++      return &flseg->mirror_array[i]->dss[0].fh_versions[0];
+ }
+ static int ff_layout_initiate_commit(struct nfs_commit_data *data, int how)
+@@ -2129,10 +2148,10 @@ static void ff_layout_cancel_io(struct p
+       for (idx = 0; idx < flseg->mirror_array_cnt; idx++) {
+               mirror = flseg->mirror_array[idx];
+-              mirror_ds = mirror->mirror_ds;
++              mirror_ds = mirror->dss[0].mirror_ds;
+               if (IS_ERR_OR_NULL(mirror_ds))
+                       continue;
+-              ds = mirror->mirror_ds->ds;
++              ds = mirror->dss[0].mirror_ds->ds;
+               if (!ds)
+                       continue;
+               ds_clp = ds->ds_clp;
+@@ -2533,8 +2552,8 @@ ff_layout_encode_ff_layoutupdate(struct
+                             struct nfs4_ff_layout_mirror *mirror)
+ {
+       struct nfs4_pnfs_ds_addr *da;
+-      struct nfs4_pnfs_ds *ds = mirror->mirror_ds->ds;
+-      struct nfs_fh *fh = &mirror->fh_versions[0];
++      struct nfs4_pnfs_ds *ds = mirror->dss[0].mirror_ds->ds;
++      struct nfs_fh *fh = &mirror->dss[0].fh_versions[0];
+       __be32 *p;
+       da = list_first_entry(&ds->ds_addrs, struct nfs4_pnfs_ds_addr, da_node);
+@@ -2547,12 +2566,12 @@ ff_layout_encode_ff_layoutupdate(struct
+       xdr_encode_opaque(p, fh->data, fh->size);
+       /* ff_io_latency4 read */
+       spin_lock(&mirror->lock);
+-      ff_layout_encode_io_latency(xdr, &mirror->read_stat.io_stat);
++      ff_layout_encode_io_latency(xdr, &mirror->dss[0].read_stat.io_stat);
+       /* ff_io_latency4 write */
+-      ff_layout_encode_io_latency(xdr, &mirror->write_stat.io_stat);
++      ff_layout_encode_io_latency(xdr, &mirror->dss[0].write_stat.io_stat);
+       spin_unlock(&mirror->lock);
+       /* nfstime4 */
+-      ff_layout_encode_nfstime(xdr, ktime_sub(ktime_get(), mirror->start_time));
++      ff_layout_encode_nfstime(xdr, ktime_sub(ktime_get(), mirror->dss[0].start_time));
+       /* bool */
+       p = xdr_reserve_space(xdr, 4);
+       *p = cpu_to_be32(false);
+@@ -2599,7 +2618,7 @@ ff_layout_mirror_prepare_stats(struct pn
+       list_for_each_entry(mirror, &ff_layout->mirrors, mirrors) {
+               if (i >= dev_limit)
+                       break;
+-              if (IS_ERR_OR_NULL(mirror->mirror_ds))
++              if (IS_ERR_OR_NULL(mirror->dss[0].mirror_ds))
+                       continue;
+               if (!test_and_clear_bit(NFS4_FF_MIRROR_STAT_AVAIL,
+                                       &mirror->flags) &&
+@@ -2608,15 +2627,15 @@ ff_layout_mirror_prepare_stats(struct pn
+               /* mirror refcount put in cleanup_layoutstats */
+               if (!refcount_inc_not_zero(&mirror->ref))
+                       continue;
+-              dev = &mirror->mirror_ds->id_node; 
++              dev = &mirror->dss[0].mirror_ds->id_node;
+               memcpy(&devinfo->dev_id, &dev->deviceid, NFS4_DEVICEID4_SIZE);
+               devinfo->offset = 0;
+               devinfo->length = NFS4_MAX_UINT64;
+               spin_lock(&mirror->lock);
+-              devinfo->read_count = mirror->read_stat.io_stat.ops_completed;
+-              devinfo->read_bytes = mirror->read_stat.io_stat.bytes_completed;
+-              devinfo->write_count = mirror->write_stat.io_stat.ops_completed;
+-              devinfo->write_bytes = mirror->write_stat.io_stat.bytes_completed;
++              devinfo->read_count = mirror->dss[0].read_stat.io_stat.ops_completed;
++              devinfo->read_bytes = mirror->dss[0].read_stat.io_stat.bytes_completed;
++              devinfo->write_count = mirror->dss[0].write_stat.io_stat.ops_completed;
++              devinfo->write_bytes = mirror->dss[0].write_stat.io_stat.bytes_completed;
+               spin_unlock(&mirror->lock);
+               devinfo->layout_type = LAYOUT_FLEX_FILES;
+               devinfo->ld_private.ops = &layoutstat_ops;
+--- a/fs/nfs/flexfilelayout/flexfilelayout.h
++++ b/fs/nfs/flexfilelayout/flexfilelayout.h
+@@ -71,24 +71,31 @@ struct nfs4_ff_layoutstat {
+       struct nfs4_ff_busy_timer busy_timer;
+ };
+-struct nfs4_ff_layout_mirror {
+-      struct pnfs_layout_hdr          *layout;
+-      struct list_head                mirrors;
+-      u32                             ds_count;
+-      u32                             efficiency;
++struct nfs4_ff_layout_mirror;
++
++struct nfs4_ff_layout_ds_stripe {
++      struct nfs4_ff_layout_mirror   *mirror;
+       struct nfs4_deviceid            devid;
++      u32                             efficiency;
+       struct nfs4_ff_layout_ds        *mirror_ds;
+       u32                             fh_versions_cnt;
+       struct nfs_fh                   *fh_versions;
+       nfs4_stateid                    stateid;
+       const struct cred __rcu         *ro_cred;
+       const struct cred __rcu         *rw_cred;
+-      refcount_t                      ref;
+-      spinlock_t                      lock;
+-      unsigned long                   flags;
+       struct nfs4_ff_layoutstat       read_stat;
+       struct nfs4_ff_layoutstat       write_stat;
+       ktime_t                         start_time;
++};
++
++struct nfs4_ff_layout_mirror {
++      struct pnfs_layout_hdr          *layout;
++      struct list_head                mirrors;
++      u32                             dss_count;
++      struct nfs4_ff_layout_ds_stripe *dss;
++      refcount_t                      ref;
++      spinlock_t                      lock;
++      unsigned long                   flags;
+       u32                             report_interval;
+ };
+@@ -154,7 +161,7 @@ FF_LAYOUT_DEVID_NODE(struct pnfs_layout_
+       struct nfs4_ff_layout_mirror *mirror = FF_LAYOUT_COMP(lseg, idx);
+       if (mirror != NULL) {
+-              struct nfs4_ff_layout_ds *mirror_ds = mirror->mirror_ds;
++              struct nfs4_ff_layout_ds *mirror_ds = mirror->dss[0].mirror_ds;
+               if (!IS_ERR_OR_NULL(mirror_ds))
+                       return &mirror_ds->id_node;
+@@ -183,7 +190,7 @@ ff_layout_no_read_on_rw(struct pnfs_layo
+ static inline int
+ nfs4_ff_layout_ds_version(const struct nfs4_ff_layout_mirror *mirror)
+ {
+-      return mirror->mirror_ds->ds_versions[0].version;
++      return mirror->dss[0].mirror_ds->ds_versions[0].version;
+ }
+ struct nfs4_ff_layout_ds *
+--- a/fs/nfs/flexfilelayout/flexfilelayoutdev.c
++++ b/fs/nfs/flexfilelayout/flexfilelayoutdev.c
+@@ -259,7 +259,7 @@ int ff_layout_track_ds_error(struct nfs4
+       if (status == 0)
+               return 0;
+-      if (IS_ERR_OR_NULL(mirror->mirror_ds))
++      if (IS_ERR_OR_NULL(mirror->dss[0].mirror_ds))
+               return -EINVAL;
+       dserr = kmalloc(sizeof(*dserr), gfp_flags);
+@@ -271,8 +271,8 @@ int ff_layout_track_ds_error(struct nfs4
+       dserr->length = length;
+       dserr->status = status;
+       dserr->opnum = opnum;
+-      nfs4_stateid_copy(&dserr->stateid, &mirror->stateid);
+-      memcpy(&dserr->deviceid, &mirror->mirror_ds->id_node.deviceid,
++      nfs4_stateid_copy(&dserr->stateid, &mirror->dss[0].stateid);
++      memcpy(&dserr->deviceid, &mirror->dss[0].mirror_ds->id_node.deviceid,
+              NFS4_DEVICEID4_SIZE);
+       spin_lock(&flo->generic_hdr.plh_inode->i_lock);
+@@ -287,9 +287,9 @@ ff_layout_get_mirror_cred(struct nfs4_ff
+       const struct cred *cred, __rcu **pcred;
+       if (iomode == IOMODE_READ)
+-              pcred = &mirror->ro_cred;
++              pcred = &mirror->dss[0].ro_cred;
+       else
+-              pcred = &mirror->rw_cred;
++              pcred = &mirror->dss[0].rw_cred;
+       rcu_read_lock();
+       do {
+@@ -307,7 +307,7 @@ struct nfs_fh *
+ nfs4_ff_layout_select_ds_fh(struct nfs4_ff_layout_mirror *mirror)
+ {
+       /* FIXME: For now assume there is only 1 version available for the DS */
+-      return &mirror->fh_versions[0];
++      return &mirror->dss[0].fh_versions[0];
+ }
+ void
+@@ -315,7 +315,7 @@ nfs4_ff_layout_select_ds_stateid(const s
+               nfs4_stateid *stateid)
+ {
+       if (nfs4_ff_layout_ds_version(mirror) == 4)
+-              nfs4_stateid_copy(stateid, &mirror->stateid);
++              nfs4_stateid_copy(stateid, &mirror->dss[0].stateid);
+ }
+ static bool
+@@ -324,23 +324,23 @@ ff_layout_init_mirror_ds(struct pnfs_lay
+ {
+       if (mirror == NULL)
+               goto outerr;
+-      if (mirror->mirror_ds == NULL) {
++      if (mirror->dss[0].mirror_ds == NULL) {
+               struct nfs4_deviceid_node *node;
+               struct nfs4_ff_layout_ds *mirror_ds = ERR_PTR(-ENODEV);
+               node = nfs4_find_get_deviceid(NFS_SERVER(lo->plh_inode),
+-                              &mirror->devid, lo->plh_lc_cred,
++                              &mirror->dss[0].devid, lo->plh_lc_cred,
+                               GFP_KERNEL);
+               if (node)
+                       mirror_ds = FF_LAYOUT_MIRROR_DS(node);
+               /* check for race with another call to this function */
+-              if (cmpxchg(&mirror->mirror_ds, NULL, mirror_ds) &&
++              if (cmpxchg(&mirror->dss[0].mirror_ds, NULL, mirror_ds) &&
+                   mirror_ds != ERR_PTR(-ENODEV))
+                       nfs4_put_deviceid_node(node);
+       }
+-      if (IS_ERR(mirror->mirror_ds))
++      if (IS_ERR(mirror->dss[0].mirror_ds))
+               goto outerr;
+       return true;
+@@ -379,7 +379,7 @@ nfs4_ff_layout_prepare_ds(struct pnfs_la
+       if (!ff_layout_init_mirror_ds(lseg->pls_layout, mirror))
+               goto noconnect;
+-      ds = mirror->mirror_ds->ds;
++      ds = mirror->dss[0].mirror_ds->ds;
+       if (READ_ONCE(ds->ds_clp))
+               goto out;
+       /* matching smp_wmb() in _nfs4_pnfs_v3/4_ds_connect */
+@@ -388,10 +388,10 @@ nfs4_ff_layout_prepare_ds(struct pnfs_la
+       /* FIXME: For now we assume the server sent only one version of NFS
+        * to use for the DS.
+        */
+-      status = nfs4_pnfs_ds_connect(s, ds, &mirror->mirror_ds->id_node,
++      status = nfs4_pnfs_ds_connect(s, ds, &mirror->dss[0].mirror_ds->id_node,
+                            dataserver_timeo, dataserver_retrans,
+-                           mirror->mirror_ds->ds_versions[0].version,
+-                           mirror->mirror_ds->ds_versions[0].minor_version);
++                           mirror->dss[0].mirror_ds->ds_versions[0].version,
++                           mirror->dss[0].mirror_ds->ds_versions[0].minor_version);
+       /* connect success, check rsize/wsize limit */
+       if (!status) {
+@@ -404,10 +404,10 @@ nfs4_ff_layout_prepare_ds(struct pnfs_la
+               max_payload =
+                       nfs_block_size(rpc_max_payload(ds->ds_clp->cl_rpcclient),
+                                      NULL);
+-              if (mirror->mirror_ds->ds_versions[0].rsize > max_payload)
+-                      mirror->mirror_ds->ds_versions[0].rsize = max_payload;
+-              if (mirror->mirror_ds->ds_versions[0].wsize > max_payload)
+-                      mirror->mirror_ds->ds_versions[0].wsize = max_payload;
++              if (mirror->dss[0].mirror_ds->ds_versions[0].rsize > max_payload)
++                      mirror->dss[0].mirror_ds->ds_versions[0].rsize = max_payload;
++              if (mirror->dss[0].mirror_ds->ds_versions[0].wsize > max_payload)
++                      mirror->dss[0].mirror_ds->ds_versions[0].wsize = max_payload;
+               goto out;
+       }
+ noconnect:
+@@ -430,7 +430,7 @@ ff_layout_get_ds_cred(struct nfs4_ff_lay
+ {
+       const struct cred *cred;
+-      if (mirror && !mirror->mirror_ds->ds_versions[0].tightly_coupled) {
++      if (mirror && !mirror->dss[0].mirror_ds->ds_versions[0].tightly_coupled) {
+               cred = ff_layout_get_mirror_cred(mirror, range->iomode);
+               if (!cred)
+                       cred = get_cred(mdscred);
+@@ -453,7 +453,7 @@ struct rpc_clnt *
+ nfs4_ff_find_or_create_ds_client(struct nfs4_ff_layout_mirror *mirror,
+                                struct nfs_client *ds_clp, struct inode *inode)
+ {
+-      switch (mirror->mirror_ds->ds_versions[0].version) {
++      switch (mirror->dss[0].mirror_ds->ds_versions[0].version) {
+       case 3:
+               /* For NFSv3 DS, flavor is set when creating DS connections */
+               return ds_clp->cl_rpcclient;
+@@ -564,11 +564,11 @@ static bool ff_read_layout_has_available
+       for (idx = 0; idx < FF_LAYOUT_MIRROR_COUNT(lseg); idx++) {
+               mirror = FF_LAYOUT_COMP(lseg, idx);
+               if (mirror) {
+-                      if (!mirror->mirror_ds)
++                      if (!mirror->dss[0].mirror_ds)
+                               return true;
+-                      if (IS_ERR(mirror->mirror_ds))
++                      if (IS_ERR(mirror->dss[0].mirror_ds))
+                               continue;
+-                      devid = &mirror->mirror_ds->id_node;
++                      devid = &mirror->dss[0].mirror_ds->id_node;
+                       if (!nfs4_test_deviceid_unavailable(devid))
+                               return true;
+               }
+@@ -585,11 +585,11 @@ static bool ff_rw_layout_has_available_d
+       for (idx = 0; idx < FF_LAYOUT_MIRROR_COUNT(lseg); idx++) {
+               mirror = FF_LAYOUT_COMP(lseg, idx);
+-              if (!mirror || IS_ERR(mirror->mirror_ds))
++              if (!mirror || IS_ERR(mirror->dss[0].mirror_ds))
+                       return false;
+-              if (!mirror->mirror_ds)
++              if (!mirror->dss[0].mirror_ds)
+                       continue;
+-              devid = &mirror->mirror_ds->id_node;
++              devid = &mirror->dss[0].mirror_ds->id_node;
+               if (nfs4_test_deviceid_unavailable(devid))
+                       return false;
+       }
diff --git a/queue-6.12/nfsv4-flexfiles-reject-zero-filehandle-version-count.patch b/queue-6.12/nfsv4-flexfiles-reject-zero-filehandle-version-count.patch
new file mode 100644 (file)
index 0000000..5b3531a
--- /dev/null
@@ -0,0 +1,58 @@
+From stable+bounces-272000-greg=kroah.com@vger.kernel.org Sun Jul  5 05:12:09 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat,  4 Jul 2026 23:11:50 -0400
+Subject: NFSv4/flexfiles: reject zero filehandle version count
+To: stable@vger.kernel.org
+Cc: Michael Bommarito <michael.bommarito@gmail.com>, Anna Schumaker <anna.schumaker@hammerspace.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260705031150.1600970-5-sashal@kernel.org>
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 2c6bb3c40bc24f6aa8dfbe6fe98c3ad6389203f2 ]
+
+ff_layout_alloc_lseg() decodes the filehandle-version array count
+from the flexfiles layout body. The value is used as the count for
+kzalloc_objs(), and the current code only rejects NULL.
+
+A zero count yields ZERO_SIZE_PTR, which can be stored in
+dss_info->fh_versions even though later flexfiles paths assume that at
+least one filehandle version exists.
+
+Reject fh_count == 0 before the allocation, matching the existing zero
+version_count validation in the flexfiles GETDEVICEINFO parser.
+
+A QEMU/KASAN run with a malformed flexfiles layout hit:
+
+  KASAN: null-ptr-deref in range [0x0000000000000010-0x0000000000000017]
+  RIP: 0010:ff_layout_encode_ff_layoutupdate.isra.0+0x15f/0x750
+  ff_layout_encode_layoutreturn+0x683/0x970
+  nfs4_xdr_enc_layoutreturn+0x278/0x3a0
+  Kernel panic - not syncing: Fatal exception
+
+The patched kernel rejects the malformed layout without KASAN/oops/panic,
+and a valid fh_count=1 regression still opens, reads, and unmounts cleanly.
+
+Cc: stable@vger.kernel.org
+Fixes: d67ae825a59d ("pnfs/flexfiles: Add the FlexFile Layout Driver")
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Signed-off-by: Anna Schumaker <anna.schumaker@hammerspace.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfs/flexfilelayout/flexfilelayout.c |    4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/fs/nfs/flexfilelayout/flexfilelayout.c
++++ b/fs/nfs/flexfilelayout/flexfilelayout.c
+@@ -493,6 +493,10 @@ ff_layout_alloc_lseg(struct pnfs_layout_
+               if (!p)
+                       goto out_err_free;
+               fh_count = be32_to_cpup(p);
++              if (fh_count == 0) {
++                      rc = -EINVAL;
++                      goto out_err_free;
++              }
+               fls->mirror_array[i]->dss[dss_id].fh_versions =
+                   kcalloc(fh_count, sizeof(struct nfs_fh),
diff --git a/queue-6.12/nfsv4-flexfiles-remove-cred-local-variable-dependency.patch b/queue-6.12/nfsv4-flexfiles-remove-cred-local-variable-dependency.patch
new file mode 100644 (file)
index 0000000..4dc4997
--- /dev/null
@@ -0,0 +1,40 @@
+From stable+bounces-271998-greg=kroah.com@vger.kernel.org Sun Jul  5 05:12:02 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat,  4 Jul 2026 23:11:48 -0400
+Subject: NFSv4/flexfiles: Remove cred local variable dependency
+To: stable@vger.kernel.org
+Cc: Jonathan Curley <jcurley@purestorage.com>, Anna Schumaker <anna.schumaker@oracle.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260705031150.1600970-3-sashal@kernel.org>
+
+From: Jonathan Curley <jcurley@purestorage.com>
+
+[ Upstream commit fec80afc41afa3016421115427df8d00dc491ee5 ]
+
+No-op preparation change to remove dependency on cred local
+variable. Subsequent striping diff has a cred per stripe so this local
+variable can't be trusted to be the same.
+
+Signed-off-by: Jonathan Curley <jcurley@purestorage.com>
+Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
+Stable-dep-of: 2c6bb3c40bc2 ("NFSv4/flexfiles: reject zero filehandle version count")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfs/flexfilelayout/flexfilelayout.c |    4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/fs/nfs/flexfilelayout/flexfilelayout.c
++++ b/fs/nfs/flexfilelayout/flexfilelayout.c
+@@ -531,10 +531,10 @@ ff_layout_alloc_lseg(struct pnfs_layout_
+               if (mirror != fls->mirror_array[i]) {
+                       /* swap cred ptrs so free_mirror will clean up old */
+                       if (lgr->range.iomode == IOMODE_READ) {
+-                              cred = xchg(&mirror->ro_cred, cred);
++                              cred = xchg(&mirror->ro_cred, fls->mirror_array[i]->ro_cred);
+                               rcu_assign_pointer(fls->mirror_array[i]->ro_cred, cred);
+                       } else {
+-                              cred = xchg(&mirror->rw_cred, cred);
++                              cred = xchg(&mirror->rw_cred, fls->mirror_array[i]->rw_cred);
+                               rcu_assign_pointer(fls->mirror_array[i]->rw_cred, cred);
+                       }
+                       ff_layout_free_mirror(fls->mirror_array[i]);
index a914a683569246ccad829c37104041c6876876c9..2ce5a72f9bd102f1fd3c04ac74f51b9b77644f39 100644 (file)
@@ -9,3 +9,18 @@ gpio-rockchip-change-the-gpio-version-judgment-logic.patch
 gpio-rockchip-teardown-bugs-and-resource-leaks.patch
 gpio-rockchip-fix-generic-irq-chip-leak-on-remove.patch
 mm-vmalloc-take-vmap_purge_lock-in-shrinker.patch
+device-property-initialize-the-remaining-fields-of-fwnode_handle-in-fwnode_init.patch
+f2fs-validate-orphan-inode-entry-count.patch
+f2fs-atomic-fix-uaf-issue-on-f2fs_inode_info.atomic_inode.patch
+f2fs-bound-i_inline_xattr_size-for-non-inline-xattr-inodes.patch
+f2fs-fix-potential-deadlock-in-f2fs_balance_fs.patch
+f2fs-fix-potential-deadlock-in-gc_merge-path-of-f2fs_balance_fs.patch
+f2fs-fix-listxattr-handling-of-corrupted-xattr-entries.patch
+fbdev-fbcon-fix-out-of-bounds-read-in-err_out-of-fbcon_do_set_font.patch
+nfsd-add-nfsd_file_-get-put-to-nfs_to-nfsd_localio_operations.patch
+nfs_common-rename-functions-that-invalidate-localio-nfs_clients.patch
+nfsv4-flexfiles-remove-cred-local-variable-dependency.patch
+nfsv4-flexfiles-add-data-structure-support-for-striped-layouts.patch
+nfsv4-flexfiles-reject-zero-filehandle-version-count.patch
+locking-rtmutex-make-sure-we-wake-anything-on-the-wake_q-when-we-release-the-lock-wait_lock.patch
+apparmor-advertise-the-tcp-fast-open-fix-is-applied.patch