]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.19-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 17 Feb 2026 12:57:41 +0000 (13:57 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 17 Feb 2026 12:57:41 +0000 (13:57 +0100)
added patches:
f2fs-fix-is_checkpointed-flag-inconsistency-issue-caused-by-concurrent-atomic-commit-and-checkpoint-writes.patch
f2fs-fix-out-of-bounds-access-in-sysfs-attribute-read-write.patch
f2fs-fix-to-add-gc-count-stat-in-f2fs_gc_range.patch
f2fs-fix-to-avoid-mapping-wrong-physical-block-for-swapfile.patch
f2fs-fix-to-avoid-uaf-in-f2fs_write_end_io.patch
f2fs-fix-to-check-sysfs-filename-w-gc_pin_file_thresh-correctly.patch
f2fs-optimize-f2fs_overwrite_io-for-f2fs_iomap_begin.patch
f2fs-support-non-4kb-block-size-without-packed_ssa-feature.patch
fbdev-rivafb-fix-divide-error-in-nv3_arb.patch
fbdev-smscufx-properly-copy-ioctl-memory-to-kernelspace.patch

queue-6.19/f2fs-fix-is_checkpointed-flag-inconsistency-issue-caused-by-concurrent-atomic-commit-and-checkpoint-writes.patch [new file with mode: 0644]
queue-6.19/f2fs-fix-out-of-bounds-access-in-sysfs-attribute-read-write.patch [new file with mode: 0644]
queue-6.19/f2fs-fix-to-add-gc-count-stat-in-f2fs_gc_range.patch [new file with mode: 0644]
queue-6.19/f2fs-fix-to-avoid-mapping-wrong-physical-block-for-swapfile.patch [new file with mode: 0644]
queue-6.19/f2fs-fix-to-avoid-uaf-in-f2fs_write_end_io.patch [new file with mode: 0644]
queue-6.19/f2fs-fix-to-check-sysfs-filename-w-gc_pin_file_thresh-correctly.patch [new file with mode: 0644]
queue-6.19/f2fs-optimize-f2fs_overwrite_io-for-f2fs_iomap_begin.patch [new file with mode: 0644]
queue-6.19/f2fs-support-non-4kb-block-size-without-packed_ssa-feature.patch [new file with mode: 0644]
queue-6.19/fbdev-rivafb-fix-divide-error-in-nv3_arb.patch [new file with mode: 0644]
queue-6.19/fbdev-smscufx-properly-copy-ioctl-memory-to-kernelspace.patch [new file with mode: 0644]
queue-6.19/series

diff --git a/queue-6.19/f2fs-fix-is_checkpointed-flag-inconsistency-issue-caused-by-concurrent-atomic-commit-and-checkpoint-writes.patch b/queue-6.19/f2fs-fix-is_checkpointed-flag-inconsistency-issue-caused-by-concurrent-atomic-commit-and-checkpoint-writes.patch
new file mode 100644 (file)
index 0000000..0615612
--- /dev/null
@@ -0,0 +1,98 @@
+From 7633a7387eb4d0259d6bea945e1d3469cd135bbc Mon Sep 17 00:00:00 2001
+From: Yongpeng Yang <yangyongpeng@xiaomi.com>
+Date: Tue, 6 Jan 2026 20:12:11 +0800
+Subject: f2fs: fix IS_CHECKPOINTED flag inconsistency issue caused by concurrent atomic commit and checkpoint writes
+
+From: Yongpeng Yang <yangyongpeng@xiaomi.com>
+
+commit 7633a7387eb4d0259d6bea945e1d3469cd135bbc upstream.
+
+During SPO tests, when mounting F2FS, an -EINVAL error was returned from
+f2fs_recover_inode_page. The issue occurred under the following scenario
+
+Thread A                                     Thread B
+f2fs_ioc_commit_atomic_write
+ - f2fs_do_sync_file // atomic = true
+  - f2fs_fsync_node_pages
+    : last_folio = inode folio
+    : schedule before folio_lock(last_folio) f2fs_write_checkpoint
+                                              - block_operations// writeback last_folio
+                                              - schedule before f2fs_flush_nat_entries
+    : set_fsync_mark(last_folio, 1)
+    : set_dentry_mark(last_folio, 1)
+    : folio_mark_dirty(last_folio)
+    - __write_node_folio(last_folio)
+      : f2fs_down_read(&sbi->node_write)//block
+                                              - f2fs_flush_nat_entries
+                                                : {struct nat_entry}->flag |= BIT(IS_CHECKPOINTED)
+                                              - unblock_operations
+                                                : f2fs_up_write(&sbi->node_write)
+                                             f2fs_write_checkpoint//return
+      : f2fs_do_write_node_page()
+f2fs_ioc_commit_atomic_write//return
+                                             SPO
+
+Thread A calls f2fs_need_dentry_mark(sbi, ino), and the last_folio has
+already been written once. However, the {struct nat_entry}->flag did not
+have the IS_CHECKPOINTED set, causing set_dentry_mark(last_folio, 1) and
+write last_folio again after Thread B finishes f2fs_write_checkpoint.
+
+After SPO and reboot, it was detected that {struct node_info}->blk_addr
+was not NULL_ADDR because Thread B successfully write the checkpoint.
+
+This issue only occurs in atomic write scenarios. For regular file
+fsync operations, the folio must be dirty. If
+block_operations->f2fs_sync_node_pages successfully submit the folio
+write, this path will not be executed. Otherwise, the
+f2fs_write_checkpoint will need to wait for the folio write submission
+to complete, as sbi->nr_pages[F2FS_DIRTY_NODES] > 0. Therefore, the
+situation where f2fs_need_dentry_mark checks that the {struct
+nat_entry}->flag /wo the IS_CHECKPOINTED flag, but the folio write has
+already been submitted, will not occur.
+
+Therefore, for atomic file fsync, sbi->node_write should be acquired
+through __write_node_folio to ensure that the IS_CHECKPOINTED flag
+correctly indicates that the checkpoint write has been completed.
+
+Fixes: 608514deba38 ("f2fs: set fsync mark only for the last dnode")
+Cc: stable@kernel.org
+Signed-off-by: Sheng Yong <shengyong1@xiaomi.com>
+Signed-off-by: Jinbao Liu <liujinbao1@xiaomi.com>
+Signed-off-by: Yongpeng Yang <yangyongpeng@xiaomi.com>
+Reviewed-by: Chao Yu <chao@kernel.org>
+Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/f2fs/node.c |   14 ++++++++++----
+ 1 file changed, 10 insertions(+), 4 deletions(-)
+
+--- a/fs/f2fs/node.c
++++ b/fs/f2fs/node.c
+@@ -1774,8 +1774,13 @@ static bool __write_node_folio(struct fo
+               goto redirty_out;
+       }
+-      if (atomic && !test_opt(sbi, NOBARRIER))
+-              fio.op_flags |= REQ_PREFLUSH | REQ_FUA;
++      if (atomic) {
++              if (!test_opt(sbi, NOBARRIER))
++                      fio.op_flags |= REQ_PREFLUSH | REQ_FUA;
++              if (IS_INODE(folio))
++                      set_dentry_mark(folio,
++                              f2fs_need_dentry_mark(sbi, ino_of_node(folio)));
++      }
+       /* should add to global list before clearing PAGECACHE status */
+       if (f2fs_in_warm_node_list(sbi, folio)) {
+@@ -1916,8 +1921,9 @@ continue_unlock:
+                                       if (is_inode_flag_set(inode,
+                                                               FI_DIRTY_INODE))
+                                               f2fs_update_inode(inode, folio);
+-                                      set_dentry_mark(folio,
+-                                              f2fs_need_dentry_mark(sbi, ino));
++                                      if (!atomic)
++                                              set_dentry_mark(folio,
++                                                      f2fs_need_dentry_mark(sbi, ino));
+                               }
+                               /* may be written by other thread */
+                               if (!folio_test_dirty(folio))
diff --git a/queue-6.19/f2fs-fix-out-of-bounds-access-in-sysfs-attribute-read-write.patch b/queue-6.19/f2fs-fix-out-of-bounds-access-in-sysfs-attribute-read-write.patch
new file mode 100644 (file)
index 0000000..e499fc2
--- /dev/null
@@ -0,0 +1,170 @@
+From 98ea0039dbfdd00e5cc1b9a8afa40434476c0955 Mon Sep 17 00:00:00 2001
+From: Yongpeng Yang <yangyongpeng@xiaomi.com>
+Date: Wed, 7 Jan 2026 10:33:46 +0800
+Subject: f2fs: fix out-of-bounds access in sysfs attribute read/write
+
+From: Yongpeng Yang <yangyongpeng@xiaomi.com>
+
+commit 98ea0039dbfdd00e5cc1b9a8afa40434476c0955 upstream.
+
+Some f2fs sysfs attributes suffer from out-of-bounds memory access and
+incorrect handling of integer values whose size is not 4 bytes.
+
+For example:
+vm:~# echo 65537 > /sys/fs/f2fs/vde/carve_out
+vm:~# cat /sys/fs/f2fs/vde/carve_out
+65537
+vm:~# echo 4294967297 > /sys/fs/f2fs/vde/atgc_age_threshold
+vm:~# cat /sys/fs/f2fs/vde/atgc_age_threshold
+1
+
+carve_out maps to {struct f2fs_sb_info}->carve_out, which is a 8-bit
+integer. However, the sysfs interface allows setting it to a value
+larger than 255, resulting in an out-of-range update.
+
+atgc_age_threshold maps to {struct atgc_management}->age_threshold,
+which is a 64-bit integer, but its sysfs interface cannot correctly set
+values larger than UINT_MAX.
+
+The root causes are:
+1. __sbi_store() treats all default values as unsigned int, which
+prevents updating integers larger than 4 bytes and causes out-of-bounds
+writes for integers smaller than 4 bytes.
+
+2. f2fs_sbi_show() also assumes all default values are unsigned int,
+leading to out-of-bounds reads and incorrect access to integers larger
+than 4 bytes.
+
+This patch introduces {struct f2fs_attr}->size to record the actual size
+of the integer associated with each sysfs attribute. With this
+information, sysfs read and write operations can correctly access and
+update values according to their real data size, avoiding memory
+corruption and truncation.
+
+Fixes: b59d0bae6ca3 ("f2fs: add sysfs support for controlling the gc_thread")
+Cc: stable@kernel.org
+Signed-off-by: Jinbao Liu <liujinbao1@xiaomi.com>
+Signed-off-by: Yongpeng Yang <yangyongpeng@xiaomi.com>
+Reviewed-by: Chao Yu <chao@kernel.org>
+Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/f2fs/sysfs.c |   60 ++++++++++++++++++++++++++++++++++++++++++++++++--------
+ 1 file changed, 52 insertions(+), 8 deletions(-)
+
+--- a/fs/f2fs/sysfs.c
++++ b/fs/f2fs/sysfs.c
+@@ -58,6 +58,7 @@ struct f2fs_attr {
+                        const char *buf, size_t len);
+       int struct_type;
+       int offset;
++      int size;
+       int id;
+ };
+@@ -344,11 +345,30 @@ static ssize_t main_blkaddr_show(struct
+                       (unsigned long long)MAIN_BLKADDR(sbi));
+ }
++static ssize_t __sbi_show_value(struct f2fs_attr *a,
++              struct f2fs_sb_info *sbi, char *buf,
++              unsigned char *value)
++{
++      switch (a->size) {
++      case 1:
++              return sysfs_emit(buf, "%u\n", *(u8 *)value);
++      case 2:
++              return sysfs_emit(buf, "%u\n", *(u16 *)value);
++      case 4:
++              return sysfs_emit(buf, "%u\n", *(u32 *)value);
++      case 8:
++              return sysfs_emit(buf, "%llu\n", *(u64 *)value);
++      default:
++              f2fs_bug_on(sbi, 1);
++              return sysfs_emit(buf,
++                              "show sysfs node value with wrong type\n");
++      }
++}
++
+ static ssize_t f2fs_sbi_show(struct f2fs_attr *a,
+                       struct f2fs_sb_info *sbi, char *buf)
+ {
+       unsigned char *ptr = NULL;
+-      unsigned int *ui;
+       ptr = __struct_ptr(sbi, a->struct_type);
+       if (!ptr)
+@@ -428,9 +448,30 @@ static ssize_t f2fs_sbi_show(struct f2fs
+                               atomic_read(&sbi->cp_call_count[BACKGROUND]));
+ #endif
+-      ui = (unsigned int *)(ptr + a->offset);
++      return __sbi_show_value(a, sbi, buf, ptr + a->offset);
++}
+-      return sysfs_emit(buf, "%u\n", *ui);
++static void __sbi_store_value(struct f2fs_attr *a,
++                      struct f2fs_sb_info *sbi,
++                      unsigned char *ui, unsigned long value)
++{
++      switch (a->size) {
++      case 1:
++              *(u8 *)ui = value;
++              break;
++      case 2:
++              *(u16 *)ui = value;
++              break;
++      case 4:
++              *(u32 *)ui = value;
++              break;
++      case 8:
++              *(u64 *)ui = value;
++              break;
++      default:
++              f2fs_bug_on(sbi, 1);
++              f2fs_err(sbi, "store sysfs node value with wrong type");
++      }
+ }
+ static ssize_t __sbi_store(struct f2fs_attr *a,
+@@ -906,7 +947,7 @@ out:
+               return count;
+       }
+-      *ui = (unsigned int)t;
++      __sbi_store_value(a, sbi, ptr + a->offset, t);
+       return count;
+ }
+@@ -1053,24 +1094,27 @@ static struct f2fs_attr f2fs_attr_sb_##_
+       .id     = F2FS_FEATURE_##_feat,                         \
+ }
+-#define F2FS_ATTR_OFFSET(_struct_type, _name, _mode, _show, _store, _offset) \
++#define F2FS_ATTR_OFFSET(_struct_type, _name, _mode, _show, _store, _offset, _size) \
+ static struct f2fs_attr f2fs_attr_##_name = {                 \
+       .attr = {.name = __stringify(_name), .mode = _mode },   \
+       .show   = _show,                                        \
+       .store  = _store,                                       \
+       .struct_type = _struct_type,                            \
+-      .offset = _offset                                       \
++      .offset = _offset,                                      \
++      .size = _size                                           \
+ }
+ #define F2FS_RO_ATTR(struct_type, struct_name, name, elname)  \
+       F2FS_ATTR_OFFSET(struct_type, name, 0444,               \
+               f2fs_sbi_show, NULL,                            \
+-              offsetof(struct struct_name, elname))
++              offsetof(struct struct_name, elname),           \
++              sizeof_field(struct struct_name, elname))
+ #define F2FS_RW_ATTR(struct_type, struct_name, name, elname)  \
+       F2FS_ATTR_OFFSET(struct_type, name, 0644,               \
+               f2fs_sbi_show, f2fs_sbi_store,                  \
+-              offsetof(struct struct_name, elname))
++              offsetof(struct struct_name, elname),           \
++              sizeof_field(struct struct_name, elname))
+ #define F2FS_GENERAL_RO_ATTR(name) \
+ static struct f2fs_attr f2fs_attr_##name = __ATTR(name, 0444, name##_show, NULL)
diff --git a/queue-6.19/f2fs-fix-to-add-gc-count-stat-in-f2fs_gc_range.patch b/queue-6.19/f2fs-fix-to-add-gc-count-stat-in-f2fs_gc_range.patch
new file mode 100644 (file)
index 0000000..ba1120b
--- /dev/null
@@ -0,0 +1,31 @@
+From 761dac9073cd67d4705a94cd1af674945a117f4c Mon Sep 17 00:00:00 2001
+From: Zhiguo Niu <zhiguo.niu@unisoc.com>
+Date: Fri, 26 Dec 2025 10:56:04 +0800
+Subject: f2fs: fix to add gc count stat in f2fs_gc_range
+
+From: Zhiguo Niu <zhiguo.niu@unisoc.com>
+
+commit 761dac9073cd67d4705a94cd1af674945a117f4c upstream.
+
+It missed the stat count in f2fs_gc_range.
+
+Cc: stable@kernel.org
+Fixes: 9bf1dcbdfdc8 ("f2fs: fix to account gc stats correctly")
+Signed-off-by: Zhiguo Niu <zhiguo.niu@unisoc.com>
+Reviewed-by: Chao Yu <chao@kernel.org>
+Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/f2fs/gc.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/fs/f2fs/gc.c
++++ b/fs/f2fs/gc.c
+@@ -2096,6 +2096,7 @@ int f2fs_gc_range(struct f2fs_sb_info *s
+       if (unlikely(f2fs_cp_error(sbi)))
+               return -EIO;
++      stat_inc_gc_call_count(sbi, FOREGROUND);
+       for (segno = start_seg; segno <= end_seg; segno += SEGS_PER_SEC(sbi)) {
+               struct gc_inode_list gc_list = {
+                       .ilist = LIST_HEAD_INIT(gc_list.ilist),
diff --git a/queue-6.19/f2fs-fix-to-avoid-mapping-wrong-physical-block-for-swapfile.patch b/queue-6.19/f2fs-fix-to-avoid-mapping-wrong-physical-block-for-swapfile.patch
new file mode 100644 (file)
index 0000000..3cb9bcf
--- /dev/null
@@ -0,0 +1,140 @@
+From 5c145c03188bc9ba1c29e0bc4d527a5978fc47f9 Mon Sep 17 00:00:00 2001
+From: Chao Yu <chao@kernel.org>
+Date: Tue, 13 Jan 2026 14:22:29 +0800
+Subject: f2fs: fix to avoid mapping wrong physical block for swapfile
+
+From: Chao Yu <chao@kernel.org>
+
+commit 5c145c03188bc9ba1c29e0bc4d527a5978fc47f9 upstream.
+
+Xiaolong Guo reported a f2fs bug in bugzilla [1]
+
+[1] https://bugzilla.kernel.org/show_bug.cgi?id=220951
+
+Quoted:
+
+"When using stress-ng's swap stress test on F2FS filesystem with kernel 6.6+,
+the system experiences data corruption leading to either:
+1 dm-verity corruption errors and device reboot
+2 F2FS node corruption errors and boot hangs
+
+The issue occurs specifically when:
+1 Using F2FS filesystem (ext4 is unaffected)
+2 Swapfile size is less than F2FS section size (2MB)
+3 Swapfile has fragmented physical layout (multiple non-contiguous extents)
+4 Kernel version is 6.6+ (6.1 is unaffected)
+
+The root cause is in check_swap_activate() function in fs/f2fs/data.c. When the
+first extent of a small swapfile (< 2MB) is not aligned to section boundaries,
+the function incorrectly treats it as the last extent, failing to map
+subsequent extents. This results in incorrect swap_extent creation where only
+the first extent is mapped, causing subsequent swap writes to overwrite wrong
+physical locations (other files' data).
+
+Steps to Reproduce
+1 Setup a device with F2FS-formatted userdata partition
+2 Compile stress-ng from https://github.com/ColinIanKing/stress-ng
+3 Run swap stress test: (Android devices)
+adb shell "cd /data/stressng; ./stress-ng-64 --metrics-brief --timeout 60
+--swap 0"
+
+Log:
+1 Ftrace shows in kernel 6.6, only first extent is mapped during second
+f2fs_map_blocks call in check_swap_activate():
+stress-ng-swap-8990: f2fs_map_blocks: ino=11002, file offset=0, start
+blkaddr=0x43143, len=0x1
+(Only 4KB mapped, not the full swapfile)
+2 in kernel 6.1, both extents are correctly mapped:
+stress-ng-swap-5966: f2fs_map_blocks: ino=28011, file offset=0, start
+blkaddr=0x13cd4, len=0x1
+stress-ng-swap-5966: f2fs_map_blocks: ino=28011, file offset=1, start
+blkaddr=0x60c84b, len=0xff
+
+The problematic code is in check_swap_activate():
+if ((pblock - SM_I(sbi)->main_blkaddr) % blks_per_sec ||
+    nr_pblocks % blks_per_sec ||
+    !f2fs_valid_pinned_area(sbi, pblock)) {
+    bool last_extent = false;
+
+    not_aligned++;
+
+    nr_pblocks = roundup(nr_pblocks, blks_per_sec);
+    if (cur_lblock + nr_pblocks > sis->max)
+        nr_pblocks -= blks_per_sec;
+
+    /* this extent is last one */
+    if (!nr_pblocks) {
+        nr_pblocks = last_lblock - cur_lblock;
+        last_extent = true;
+    }
+
+    ret = f2fs_migrate_blocks(inode, cur_lblock, nr_pblocks);
+    if (ret) {
+        if (ret == -ENOENT)
+            ret = -EINVAL;
+        goto out;
+    }
+
+    if (!last_extent)
+        goto retry;
+}
+
+When the first extent is unaligned and roundup(nr_pblocks, blks_per_sec)
+exceeds sis->max, we subtract blks_per_sec resulting in nr_pblocks = 0. The
+code then incorrectly assumes this is the last extent, sets nr_pblocks =
+last_lblock - cur_lblock (entire swapfile), and performs migration. After
+migration, it doesn't retry mapping, so subsequent extents are never processed.
+"
+
+In order to fix this issue, we need to lookup block mapping info after
+we migrate all blocks in the tail of swapfile.
+
+Cc: stable@kernel.org
+Fixes: 9703d69d9d15 ("f2fs: support file pinning for zoned devices")
+Cc: Daeho Jeong <daehojeong@google.com>
+Reported-and-tested-by: Xiaolong Guo <guoxiaolong2008@gmail.com>
+Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220951
+Signed-off-by: Chao Yu <chao@kernel.org>
+Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/f2fs/data.c |   14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+--- a/fs/f2fs/data.c
++++ b/fs/f2fs/data.c
+@@ -3935,6 +3935,7 @@ static int check_swap_activate(struct sw
+       while (cur_lblock < last_lblock && cur_lblock < sis->max) {
+               struct f2fs_map_blocks map;
++              bool last_extent = false;
+ retry:
+               cond_resched();
+@@ -3960,11 +3961,10 @@ retry:
+               pblock = map.m_pblk;
+               nr_pblocks = map.m_len;
+-              if ((pblock - SM_I(sbi)->main_blkaddr) % blks_per_sec ||
+-                              nr_pblocks % blks_per_sec ||
+-                              f2fs_is_sequential_zone_area(sbi, pblock)) {
+-                      bool last_extent = false;
+-
++              if (!last_extent &&
++                      ((pblock - SM_I(sbi)->main_blkaddr) % blks_per_sec ||
++                      nr_pblocks % blks_per_sec ||
++                      f2fs_is_sequential_zone_area(sbi, pblock))) {
+                       not_aligned++;
+                       nr_pblocks = roundup(nr_pblocks, blks_per_sec);
+@@ -3985,8 +3985,8 @@ retry:
+                               goto out;
+                       }
+-                      if (!last_extent)
+-                              goto retry;
++                      /* lookup block mapping info after block migration */
++                      goto retry;
+               }
+               if (cur_lblock + nr_pblocks >= sis->max)
diff --git a/queue-6.19/f2fs-fix-to-avoid-uaf-in-f2fs_write_end_io.patch b/queue-6.19/f2fs-fix-to-avoid-uaf-in-f2fs_write_end_io.patch
new file mode 100644 (file)
index 0000000..34dd219
--- /dev/null
@@ -0,0 +1,75 @@
+From ce2739e482bce8d2c014d76c4531c877f382aa54 Mon Sep 17 00:00:00 2001
+From: Chao Yu <chao@kernel.org>
+Date: Wed, 7 Jan 2026 19:22:18 +0800
+Subject: f2fs: fix to avoid UAF in f2fs_write_end_io()
+
+From: Chao Yu <chao@kernel.org>
+
+commit ce2739e482bce8d2c014d76c4531c877f382aa54 upstream.
+
+As syzbot reported an use-after-free issue in f2fs_write_end_io().
+
+It is caused by below race condition:
+
+loop device                            umount
+- worker_thread
+ - loop_process_work
+  - do_req_filebacked
+   - lo_rw_aio
+    - lo_rw_aio_complete
+     - blk_mq_end_request
+      - blk_update_request
+       - f2fs_write_end_io
+        - dec_page_count
+        - folio_end_writeback
+                                       - kill_f2fs_super
+                                        - kill_block_super
+                                         - f2fs_put_super
+                                        : free(sbi)
+       : get_pages(, F2FS_WB_CP_DATA)
+         accessed sbi which is freed
+
+In kill_f2fs_super(), we will drop all page caches of f2fs inodes before
+call free(sbi), it guarantee that all folios should end its writeback, so
+it should be safe to access sbi before last folio_end_writeback().
+
+Let's relocate ckpt thread wakeup flow before folio_end_writeback() to
+resolve this issue.
+
+Cc: stable@kernel.org
+Fixes: e234088758fc ("f2fs: avoid wait if IO end up when do_checkpoint for better performance")
+Reported-by: syzbot+b4444e3c972a7a124187@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=b4444e3c972a7a124187
+Signed-off-by: Chao Yu <chao@kernel.org>
+Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/f2fs/data.c |   12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+--- a/fs/f2fs/data.c
++++ b/fs/f2fs/data.c
+@@ -356,14 +356,20 @@ static void f2fs_write_end_io(struct bio
+                               folio->index != nid_of_node(folio));
+               dec_page_count(sbi, type);
++
++              /*
++               * we should access sbi before folio_end_writeback() to
++               * avoid racing w/ kill_f2fs_super()
++               */
++              if (type == F2FS_WB_CP_DATA && !get_pages(sbi, type) &&
++                              wq_has_sleeper(&sbi->cp_wait))
++                      wake_up(&sbi->cp_wait);
++
+               if (f2fs_in_warm_node_list(sbi, folio))
+                       f2fs_del_fsync_node_entry(sbi, folio);
+               folio_clear_f2fs_gcing(folio);
+               folio_end_writeback(folio);
+       }
+-      if (!get_pages(sbi, F2FS_WB_CP_DATA) &&
+-                              wq_has_sleeper(&sbi->cp_wait))
+-              wake_up(&sbi->cp_wait);
+       bio_put(bio);
+ }
diff --git a/queue-6.19/f2fs-fix-to-check-sysfs-filename-w-gc_pin_file_thresh-correctly.patch b/queue-6.19/f2fs-fix-to-check-sysfs-filename-w-gc_pin_file_thresh-correctly.patch
new file mode 100644 (file)
index 0000000..87343fb
--- /dev/null
@@ -0,0 +1,32 @@
+From 0eda086de85e140f53c6123a4c00662f4e614ee4 Mon Sep 17 00:00:00 2001
+From: Chao Yu <chao@kernel.org>
+Date: Tue, 6 Jan 2026 14:31:17 +0800
+Subject: f2fs: fix to check sysfs filename w/ gc_pin_file_thresh correctly
+
+From: Chao Yu <chao@kernel.org>
+
+commit 0eda086de85e140f53c6123a4c00662f4e614ee4 upstream.
+
+Sysfs entry name is gc_pin_file_thresh instead of gc_pin_file_threshold,
+fix it.
+
+Cc: stable@kernel.org
+Fixes: c521a6ab4ad7 ("f2fs: fix to limit gc_pin_file_threshold")
+Signed-off-by: Chao Yu <chao@kernel.org>
+Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/f2fs/sysfs.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/f2fs/sysfs.c
++++ b/fs/f2fs/sysfs.c
+@@ -749,7 +749,7 @@ out:
+               return count;
+       }
+-      if (!strcmp(a->attr.name, "gc_pin_file_threshold")) {
++      if (!strcmp(a->attr.name, "gc_pin_file_thresh")) {
+               if (t > MAX_GC_FAILED_PINNED_FILES)
+                       return -EINVAL;
+               sbi->gc_pin_file_threshold = t;
diff --git a/queue-6.19/f2fs-optimize-f2fs_overwrite_io-for-f2fs_iomap_begin.patch b/queue-6.19/f2fs-optimize-f2fs_overwrite_io-for-f2fs_iomap_begin.patch
new file mode 100644 (file)
index 0000000..437826b
--- /dev/null
@@ -0,0 +1,75 @@
+From d860974a7e38d35e9e2c4dc8a9f4223b38b6ad99 Mon Sep 17 00:00:00 2001
+From: Yeongjin Gil <youngjin.gil@samsung.com>
+Date: Thu, 22 Jan 2026 19:45:27 +0900
+Subject: f2fs: optimize f2fs_overwrite_io() for f2fs_iomap_begin
+
+From: Yeongjin Gil <youngjin.gil@samsung.com>
+
+commit d860974a7e38d35e9e2c4dc8a9f4223b38b6ad99 upstream.
+
+When overwriting already allocated blocks, f2fs_iomap_begin() calls
+f2fs_overwrite_io() to check block mappings. However,
+f2fs_overwrite_io() iterates through all mapped blocks in the range,
+which can be inefficient for fragmented files with large I/O requests.
+
+This patch optimizes f2fs_overwrite_io() by adding a 'check_first'
+parameter and introducing __f2fs_overwrite_io() helper. When called from
+f2fs_iomap_begin(), we only check the first mapping to determine if the
+range is already allocated, which is sufficient for setting
+map.m_may_create.
+
+This optimization significantly reduces the number of f2fs_map_blocks()
+calls in f2fs_overwrite_io() when called from f2fs_iomap_begin(),
+especially for fragmented files with large I/O requests.
+
+Cc: stable@kernel.org
+Fixes: 351bc761338d ("f2fs: optimize f2fs DIO overwrites")
+Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com>
+Reviewed-by: Sunmin Jeong <s_min.jeong@samsung.com>
+Signed-off-by: Yeongjin Gil <youngjin.gil@samsung.com>
+Reviewed-by: Chao Yu <chao@kernel.org>
+Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/f2fs/data.c |   12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+--- a/fs/f2fs/data.c
++++ b/fs/f2fs/data.c
+@@ -1799,7 +1799,8 @@ out:
+       return err;
+ }
+-bool f2fs_overwrite_io(struct inode *inode, loff_t pos, size_t len)
++static bool __f2fs_overwrite_io(struct inode *inode, loff_t pos, size_t len,
++                              bool check_first)
+ {
+       struct f2fs_map_blocks map;
+       block_t last_lblk;
+@@ -1821,10 +1822,17 @@ bool f2fs_overwrite_io(struct inode *ino
+               if (err || map.m_len == 0)
+                       return false;
+               map.m_lblk += map.m_len;
++              if (check_first)
++                      break;
+       }
+       return true;
+ }
++bool f2fs_overwrite_io(struct inode *inode, loff_t pos, size_t len)
++{
++      return __f2fs_overwrite_io(inode, pos, len, false);
++}
++
+ static int f2fs_xattr_fiemap(struct inode *inode,
+                               struct fiemap_extent_info *fieinfo)
+ {
+@@ -4187,7 +4195,7 @@ static int f2fs_iomap_begin(struct inode
+        * f2fs_map_lock and f2fs_balance_fs are not necessary.
+        */
+       if ((flags & IOMAP_WRITE) &&
+-              !f2fs_overwrite_io(inode, offset, length))
++              !__f2fs_overwrite_io(inode, offset, length, true))
+               map.m_may_create = true;
+       err = f2fs_map_blocks(inode, &map, F2FS_GET_BLOCK_DIO);
diff --git a/queue-6.19/f2fs-support-non-4kb-block-size-without-packed_ssa-feature.patch b/queue-6.19/f2fs-support-non-4kb-block-size-without-packed_ssa-feature.patch
new file mode 100644 (file)
index 0000000..4e95add
--- /dev/null
@@ -0,0 +1,721 @@
+From e48e16f3e37fac76e2f0c14c58df2b0398a323b0 Mon Sep 17 00:00:00 2001
+From: Daeho Jeong <daehojeong@google.com>
+Date: Sat, 10 Jan 2026 15:54:05 -0800
+Subject: f2fs: support non-4KB block size without packed_ssa feature
+
+From: Daeho Jeong <daehojeong@google.com>
+
+commit e48e16f3e37fac76e2f0c14c58df2b0398a323b0 upstream.
+
+Currently, F2FS requires the packed_ssa feature to be enabled when
+utilizing non-4KB block sizes (e.g., 16KB). This restriction limits
+the flexibility of filesystem formatting options.
+
+This patch allows F2FS to support non-4KB block sizes even when the
+packed_ssa feature is disabled. It adjusts the SSA calculation logic to
+correctly handle summary entries in larger blocks without the packed
+layout.
+
+Cc: stable@kernel.org
+Fixes: 7ee8bc3942f2 ("f2fs: revert summary entry count from 2048 to 512 in 16kb block support")
+Signed-off-by: Daeho Jeong <daehojeong@google.com>
+Reviewed-by: Chao Yu <chao@kernel.org>
+Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/f2fs/f2fs.h          |   52 ++++++++++++++++++++---------
+ fs/f2fs/gc.c            |   23 ++++++------
+ fs/f2fs/node.c          |   12 +++---
+ fs/f2fs/recovery.c      |    6 +--
+ fs/f2fs/segment.c       |   86 +++++++++++++++++++++++++-----------------------
+ fs/f2fs/segment.h       |    9 ++---
+ fs/f2fs/super.c         |   26 ++++++--------
+ include/linux/f2fs_fs.h |   73 +++++++++++++++++++++++++---------------
+ 8 files changed, 165 insertions(+), 122 deletions(-)
+
+--- a/fs/f2fs/f2fs.h
++++ b/fs/f2fs/f2fs.h
+@@ -521,13 +521,25 @@ struct fsync_inode_entry {
+ #define nats_in_cursum(jnl)           (le16_to_cpu((jnl)->n_nats))
+ #define sits_in_cursum(jnl)           (le16_to_cpu((jnl)->n_sits))
+-#define nat_in_journal(jnl, i)                ((jnl)->nat_j.entries[i].ne)
+-#define nid_in_journal(jnl, i)                ((jnl)->nat_j.entries[i].nid)
+-#define sit_in_journal(jnl, i)                ((jnl)->sit_j.entries[i].se)
+-#define segno_in_journal(jnl, i)      ((jnl)->sit_j.entries[i].segno)
++#define nat_in_journal(jnl, i) \
++      (((struct nat_journal_entry *)(jnl)->nat_j.entries)[i].ne)
++#define nid_in_journal(jnl, i) \
++      (((struct nat_journal_entry *)(jnl)->nat_j.entries)[i].nid)
++#define sit_in_journal(jnl, i) \
++      (((struct sit_journal_entry *)(jnl)->sit_j.entries)[i].se)
++#define segno_in_journal(jnl, i) \
++      (((struct sit_journal_entry *)(jnl)->sit_j.entries)[i].segno)
++
++#define sum_entries(sum)      ((struct f2fs_summary *)(sum))
++#define sum_journal(sbi, sum) \
++      ((struct f2fs_journal *)((char *)(sum) + \
++      ((sbi)->entries_in_sum * sizeof(struct f2fs_summary))))
++#define sum_footer(sbi, sum) \
++      ((struct summary_footer *)((char *)(sum) + (sbi)->sum_blocksize - \
++      sizeof(struct summary_footer)))
+-#define MAX_NAT_JENTRIES(jnl) (NAT_JOURNAL_ENTRIES - nats_in_cursum(jnl))
+-#define MAX_SIT_JENTRIES(jnl) (SIT_JOURNAL_ENTRIES - sits_in_cursum(jnl))
++#define MAX_NAT_JENTRIES(sbi, jnl)    ((sbi)->nat_journal_entries - nats_in_cursum(jnl))
++#define MAX_SIT_JENTRIES(sbi, jnl)    ((sbi)->sit_journal_entries - sits_in_cursum(jnl))
+ static inline int update_nats_in_cursum(struct f2fs_journal *journal, int i)
+ {
+@@ -545,14 +557,6 @@ static inline int update_sits_in_cursum(
+       return before;
+ }
+-static inline bool __has_cursum_space(struct f2fs_journal *journal,
+-                                                      int size, int type)
+-{
+-      if (type == NAT_JOURNAL)
+-              return size <= MAX_NAT_JENTRIES(journal);
+-      return size <= MAX_SIT_JENTRIES(journal);
+-}
+-
+ /* for inline stuff */
+ #define DEF_INLINE_RESERVED_SIZE      1
+ static inline int get_extra_isize(struct inode *inode);
+@@ -1764,6 +1768,15 @@ struct f2fs_sb_info {
+       bool readdir_ra;                        /* readahead inode in readdir */
+       u64 max_io_bytes;                       /* max io bytes to merge IOs */
++      /* variable summary block units */
++      unsigned int sum_blocksize;             /* sum block size */
++      unsigned int sums_per_block;            /* sum block count per block */
++      unsigned int entries_in_sum;            /* entry count in sum block */
++      unsigned int sum_entry_size;            /* total entry size in sum block */
++      unsigned int sum_journal_size;          /* journal size in sum block */
++      unsigned int nat_journal_entries;       /* nat journal entry count in the journal */
++      unsigned int sit_journal_entries;       /* sit journal entry count in the journal */
++
+       block_t user_block_count;               /* # of user blocks */
+       block_t total_valid_block_count;        /* # of valid blocks */
+       block_t discard_blks;                   /* discard command candidats */
+@@ -2813,6 +2826,14 @@ static inline block_t __start_sum_addr(s
+       return le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_start_sum);
+ }
++static inline bool __has_cursum_space(struct f2fs_sb_info *sbi,
++                      struct f2fs_journal *journal, int size, int type)
++{
++      if (type == NAT_JOURNAL)
++              return size <= MAX_NAT_JENTRIES(sbi, journal);
++      return size <= MAX_SIT_JENTRIES(sbi, journal);
++}
++
+ extern void f2fs_mark_inode_dirty_sync(struct inode *inode, bool sync);
+ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
+                                       struct inode *inode, bool is_inode)
+@@ -3956,7 +3977,8 @@ void f2fs_wait_on_block_writeback_range(
+                                                               block_t len);
+ void f2fs_write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk);
+ void f2fs_write_node_summaries(struct f2fs_sb_info *sbi, block_t start_blk);
+-int f2fs_lookup_journal_in_cursum(struct f2fs_journal *journal, int type,
++int f2fs_lookup_journal_in_cursum(struct f2fs_sb_info *sbi,
++                      struct f2fs_journal *journal, int type,
+                       unsigned int val, int alloc);
+ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc);
+ int f2fs_check_and_fix_write_pointer(struct f2fs_sb_info *sbi);
+--- a/fs/f2fs/gc.c
++++ b/fs/f2fs/gc.c
+@@ -1769,8 +1769,8 @@ static int do_garbage_collect(struct f2f
+       sanity_check_seg_type(sbi, get_seg_entry(sbi, segno)->type);
+-      segno = rounddown(segno, SUMS_PER_BLOCK);
+-      sum_blk_cnt = DIV_ROUND_UP(end_segno - segno, SUMS_PER_BLOCK);
++      segno = rounddown(segno, sbi->sums_per_block);
++      sum_blk_cnt = DIV_ROUND_UP(end_segno - segno, sbi->sums_per_block);
+       /* readahead multi ssa blocks those have contiguous address */
+       if (__is_large_section(sbi))
+               f2fs_ra_meta_pages(sbi, GET_SUM_BLOCK(sbi, segno),
+@@ -1780,17 +1780,17 @@ static int do_garbage_collect(struct f2f
+       while (segno < end_segno) {
+               struct folio *sum_folio = f2fs_get_sum_folio(sbi, segno);
+-              segno += SUMS_PER_BLOCK;
++              segno += sbi->sums_per_block;
+               if (IS_ERR(sum_folio)) {
+                       int err = PTR_ERR(sum_folio);
+-                      end_segno = segno - SUMS_PER_BLOCK;
+-                      segno = rounddown(start_segno, SUMS_PER_BLOCK);
++                      end_segno = segno - sbi->sums_per_block;
++                      segno = rounddown(start_segno, sbi->sums_per_block);
+                       while (segno < end_segno) {
+                               sum_folio = filemap_get_folio(META_MAPPING(sbi),
+                                               GET_SUM_BLOCK(sbi, segno));
+                               folio_put_refs(sum_folio, 2);
+-                              segno += SUMS_PER_BLOCK;
++                              segno += sbi->sums_per_block;
+                       }
+                       return err;
+               }
+@@ -1806,8 +1806,8 @@ static int do_garbage_collect(struct f2f
+               /* find segment summary of victim */
+               struct folio *sum_folio = filemap_get_folio(META_MAPPING(sbi),
+                                       GET_SUM_BLOCK(sbi, segno));
+-              unsigned int block_end_segno = rounddown(segno, SUMS_PER_BLOCK)
+-                                      + SUMS_PER_BLOCK;
++              unsigned int block_end_segno = rounddown(segno, sbi->sums_per_block)
++                                      + sbi->sums_per_block;
+               if (block_end_segno > end_segno)
+                       block_end_segno = end_segno;
+@@ -1833,12 +1833,13 @@ static int do_garbage_collect(struct f2f
+                                       migrated >= sbi->migration_granularity)
+                               continue;
+-                      sum = SUM_BLK_PAGE_ADDR(sum_folio, cur_segno);
+-                      if (type != GET_SUM_TYPE((&sum->footer))) {
++                      sum = SUM_BLK_PAGE_ADDR(sbi, sum_folio, cur_segno);
++                      if (type != GET_SUM_TYPE(sum_footer(sbi, sum))) {
+                               f2fs_err(sbi, "Inconsistent segment (%u) type "
+                                               "[%d, %d] in SSA and SIT",
+                                               cur_segno, type,
+-                                              GET_SUM_TYPE((&sum->footer)));
++                                              GET_SUM_TYPE(
++                                              sum_footer(sbi, sum)));
+                               f2fs_stop_checkpoint(sbi, false,
+                                               STOP_CP_REASON_CORRUPTED_SUMMARY);
+                               continue;
+--- a/fs/f2fs/node.c
++++ b/fs/f2fs/node.c
+@@ -606,7 +606,7 @@ retry:
+               goto retry;
+       }
+-      i = f2fs_lookup_journal_in_cursum(journal, NAT_JOURNAL, nid, 0);
++      i = f2fs_lookup_journal_in_cursum(sbi, journal, NAT_JOURNAL, nid, 0);
+       if (i >= 0) {
+               ne = nat_in_journal(journal, i);
+               node_info_from_raw_nat(ni, &ne);
+@@ -2943,7 +2943,7 @@ int f2fs_restore_node_summary(struct f2f
+       /* scan the node segment */
+       last_offset = BLKS_PER_SEG(sbi);
+       addr = START_BLOCK(sbi, segno);
+-      sum_entry = &sum->entries[0];
++      sum_entry = sum_entries(sum);
+       for (i = 0; i < last_offset; i += nrpages, addr += nrpages) {
+               nrpages = bio_max_segs(last_offset - i);
+@@ -3084,7 +3084,7 @@ static int __flush_nat_entry_set(struct
+        * #2, flush nat entries to nat page.
+        */
+       if (enabled_nat_bits(sbi, cpc) ||
+-              !__has_cursum_space(journal, set->entry_cnt, NAT_JOURNAL))
++              !__has_cursum_space(sbi, journal, set->entry_cnt, NAT_JOURNAL))
+               to_journal = false;
+       if (to_journal) {
+@@ -3107,7 +3107,7 @@ static int __flush_nat_entry_set(struct
+               f2fs_bug_on(sbi, nat_get_blkaddr(ne) == NEW_ADDR);
+               if (to_journal) {
+-                      offset = f2fs_lookup_journal_in_cursum(journal,
++                      offset = f2fs_lookup_journal_in_cursum(sbi, journal,
+                                                       NAT_JOURNAL, nid, 1);
+                       f2fs_bug_on(sbi, offset < 0);
+                       raw_ne = &nat_in_journal(journal, offset);
+@@ -3178,7 +3178,7 @@ int f2fs_flush_nat_entries(struct f2fs_s
+        * into nat entry set.
+        */
+       if (enabled_nat_bits(sbi, cpc) ||
+-              !__has_cursum_space(journal,
++              !__has_cursum_space(sbi, journal,
+                       nm_i->nat_cnt[DIRTY_NAT], NAT_JOURNAL))
+               remove_nats_in_journal(sbi);
+@@ -3189,7 +3189,7 @@ int f2fs_flush_nat_entries(struct f2fs_s
+               set_idx = setvec[found - 1]->set + 1;
+               for (idx = 0; idx < found; idx++)
+                       __adjust_nat_entry_set(setvec[idx], &sets,
+-                                              MAX_NAT_JENTRIES(journal));
++                                      MAX_NAT_JENTRIES(sbi, journal));
+       }
+       /* flush dirty nats in nat entry set */
+--- a/fs/f2fs/recovery.c
++++ b/fs/f2fs/recovery.c
+@@ -514,7 +514,7 @@ static int check_index_in_prev_nodes(str
+               struct curseg_info *curseg = CURSEG_I(sbi, i);
+               if (curseg->segno == segno) {
+-                      sum = curseg->sum_blk->entries[blkoff];
++                      sum = sum_entries(curseg->sum_blk)[blkoff];
+                       goto got_it;
+               }
+       }
+@@ -522,8 +522,8 @@ static int check_index_in_prev_nodes(str
+       sum_folio = f2fs_get_sum_folio(sbi, segno);
+       if (IS_ERR(sum_folio))
+               return PTR_ERR(sum_folio);
+-      sum_node = SUM_BLK_PAGE_ADDR(sum_folio, segno);
+-      sum = sum_node->entries[blkoff];
++      sum_node = SUM_BLK_PAGE_ADDR(sbi, sum_folio, segno);
++      sum = sum_entries(sum_node)[blkoff];
+       f2fs_folio_put(sum_folio, true);
+ got_it:
+       /* Use the locked dnode page and inode */
+--- a/fs/f2fs/segment.c
++++ b/fs/f2fs/segment.c
+@@ -2685,12 +2685,12 @@ int f2fs_npages_for_summary_flush(struct
+                       valid_sum_count += f2fs_curseg_valid_blocks(sbi, i);
+       }
+-      sum_in_page = (PAGE_SIZE - 2 * SUM_JOURNAL_SIZE -
++      sum_in_page = (sbi->sum_blocksize - 2 * sbi->sum_journal_size -
+                       SUM_FOOTER_SIZE) / SUMMARY_SIZE;
+       if (valid_sum_count <= sum_in_page)
+               return 1;
+       else if ((valid_sum_count - sum_in_page) <=
+-              (PAGE_SIZE - SUM_FOOTER_SIZE) / SUMMARY_SIZE)
++              (sbi->sum_blocksize - SUM_FOOTER_SIZE) / SUMMARY_SIZE)
+               return 2;
+       return 3;
+ }
+@@ -2710,7 +2710,7 @@ void f2fs_update_meta_page(struct f2fs_s
+ {
+       struct folio *folio;
+-      if (SUMS_PER_BLOCK == 1)
++      if (!f2fs_sb_has_packed_ssa(sbi))
+               folio = f2fs_grab_meta_folio(sbi, blk_addr);
+       else
+               folio = f2fs_get_meta_folio_retry(sbi, blk_addr);
+@@ -2728,7 +2728,7 @@ static void write_sum_page(struct f2fs_s
+ {
+       struct folio *folio;
+-      if (SUMS_PER_BLOCK == 1)
++      if (!f2fs_sb_has_packed_ssa(sbi))
+               return f2fs_update_meta_page(sbi, (void *)sum_blk,
+                               GET_SUM_BLOCK(sbi, segno));
+@@ -2736,7 +2736,8 @@ static void write_sum_page(struct f2fs_s
+       if (IS_ERR(folio))
+               return;
+-      memcpy(SUM_BLK_PAGE_ADDR(folio, segno), sum_blk, sizeof(*sum_blk));
++      memcpy(SUM_BLK_PAGE_ADDR(sbi, folio, segno), sum_blk,
++                      sbi->sum_blocksize);
+       folio_mark_dirty(folio);
+       f2fs_folio_put(folio, true);
+ }
+@@ -2755,11 +2756,11 @@ static void write_current_sum_page(struc
+       mutex_lock(&curseg->curseg_mutex);
+       down_read(&curseg->journal_rwsem);
+-      memcpy(&dst->journal, curseg->journal, SUM_JOURNAL_SIZE);
++      memcpy(sum_journal(sbi, dst), curseg->journal, sbi->sum_journal_size);
+       up_read(&curseg->journal_rwsem);
+-      memcpy(dst->entries, src->entries, SUM_ENTRY_SIZE);
+-      memcpy(&dst->footer, &src->footer, SUM_FOOTER_SIZE);
++      memcpy(sum_entries(dst), sum_entries(src), sbi->sum_entry_size);
++      memcpy(sum_footer(sbi, dst), sum_footer(sbi, src), SUM_FOOTER_SIZE);
+       mutex_unlock(&curseg->curseg_mutex);
+@@ -2932,7 +2933,7 @@ static void reset_curseg(struct f2fs_sb_
+       curseg->next_blkoff = 0;
+       curseg->next_segno = NULL_SEGNO;
+-      sum_footer = &(curseg->sum_blk->footer);
++      sum_footer = sum_footer(sbi, curseg->sum_blk);
+       memset(sum_footer, 0, sizeof(struct summary_footer));
+       sanity_check_seg_type(sbi, seg_type);
+@@ -3078,11 +3079,11 @@ static int change_curseg(struct f2fs_sb_
+       sum_folio = f2fs_get_sum_folio(sbi, new_segno);
+       if (IS_ERR(sum_folio)) {
+               /* GC won't be able to use stale summary pages by cp_error */
+-              memset(curseg->sum_blk, 0, SUM_ENTRY_SIZE);
++              memset(curseg->sum_blk, 0, sbi->sum_entry_size);
+               return PTR_ERR(sum_folio);
+       }
+-      sum_node = SUM_BLK_PAGE_ADDR(sum_folio, new_segno);
+-      memcpy(curseg->sum_blk, sum_node, SUM_ENTRY_SIZE);
++      sum_node = SUM_BLK_PAGE_ADDR(sbi, sum_folio, new_segno);
++      memcpy(curseg->sum_blk, sum_node, sbi->sum_entry_size);
+       f2fs_folio_put(sum_folio, true);
+       return 0;
+ }
+@@ -3814,7 +3815,7 @@ int f2fs_allocate_data_block(struct f2fs
+       f2fs_wait_discard_bio(sbi, *new_blkaddr);
+-      curseg->sum_blk->entries[curseg->next_blkoff] = *sum;
++      sum_entries(curseg->sum_blk)[curseg->next_blkoff] = *sum;
+       if (curseg->alloc_type == SSR) {
+               curseg->next_blkoff = f2fs_find_next_ssr_block(sbi, curseg);
+       } else {
+@@ -4183,7 +4184,7 @@ void f2fs_do_replace_block(struct f2fs_s
+       }
+       curseg->next_blkoff = GET_BLKOFF_FROM_SEG0(sbi, new_blkaddr);
+-      curseg->sum_blk->entries[curseg->next_blkoff] = *sum;
++      sum_entries(curseg->sum_blk)[curseg->next_blkoff] = *sum;
+       if (!recover_curseg || recover_newaddr) {
+               if (!from_gc)
+@@ -4303,12 +4304,12 @@ static int read_compacted_summaries(stru
+       /* Step 1: restore nat cache */
+       seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA);
+-      memcpy(seg_i->journal, kaddr, SUM_JOURNAL_SIZE);
++      memcpy(seg_i->journal, kaddr, sbi->sum_journal_size);
+       /* Step 2: restore sit cache */
+       seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA);
+-      memcpy(seg_i->journal, kaddr + SUM_JOURNAL_SIZE, SUM_JOURNAL_SIZE);
+-      offset = 2 * SUM_JOURNAL_SIZE;
++      memcpy(seg_i->journal, kaddr + sbi->sum_journal_size, sbi->sum_journal_size);
++      offset = 2 * sbi->sum_journal_size;
+       /* Step 3: restore summary entries */
+       for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) {
+@@ -4330,9 +4331,9 @@ static int read_compacted_summaries(stru
+                       struct f2fs_summary *s;
+                       s = (struct f2fs_summary *)(kaddr + offset);
+-                      seg_i->sum_blk->entries[j] = *s;
++                      sum_entries(seg_i->sum_blk)[j] = *s;
+                       offset += SUMMARY_SIZE;
+-                      if (offset + SUMMARY_SIZE <= PAGE_SIZE -
++                      if (offset + SUMMARY_SIZE <= sbi->sum_blocksize -
+                                               SUM_FOOTER_SIZE)
+                               continue;
+@@ -4388,7 +4389,7 @@ static int read_normal_summaries(struct
+       if (IS_NODESEG(type)) {
+               if (__exist_node_summaries(sbi)) {
+-                      struct f2fs_summary *ns = &sum->entries[0];
++                      struct f2fs_summary *ns = sum_entries(sum);
+                       int i;
+                       for (i = 0; i < BLKS_PER_SEG(sbi); i++, ns++) {
+@@ -4408,11 +4409,13 @@ static int read_normal_summaries(struct
+       /* update journal info */
+       down_write(&curseg->journal_rwsem);
+-      memcpy(curseg->journal, &sum->journal, SUM_JOURNAL_SIZE);
++      memcpy(curseg->journal, sum_journal(sbi, sum), sbi->sum_journal_size);
+       up_write(&curseg->journal_rwsem);
+-      memcpy(curseg->sum_blk->entries, sum->entries, SUM_ENTRY_SIZE);
+-      memcpy(&curseg->sum_blk->footer, &sum->footer, SUM_FOOTER_SIZE);
++      memcpy(sum_entries(curseg->sum_blk), sum_entries(sum),
++                      sbi->sum_entry_size);
++      memcpy(sum_footer(sbi, curseg->sum_blk), sum_footer(sbi, sum),
++                      SUM_FOOTER_SIZE);
+       curseg->next_segno = segno;
+       reset_curseg(sbi, type, 0);
+       curseg->alloc_type = ckpt->alloc_type[type];
+@@ -4456,8 +4459,8 @@ static int restore_curseg_summaries(stru
+       }
+       /* sanity check for summary blocks */
+-      if (nats_in_cursum(nat_j) > NAT_JOURNAL_ENTRIES ||
+-                      sits_in_cursum(sit_j) > SIT_JOURNAL_ENTRIES) {
++      if (nats_in_cursum(nat_j) > sbi->nat_journal_entries ||
++                      sits_in_cursum(sit_j) > sbi->sit_journal_entries) {
+               f2fs_err(sbi, "invalid journal entries nats %u sits %u",
+                        nats_in_cursum(nat_j), sits_in_cursum(sit_j));
+               return -EINVAL;
+@@ -4481,13 +4484,13 @@ static void write_compacted_summaries(st
+       /* Step 1: write nat cache */
+       seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA);
+-      memcpy(kaddr, seg_i->journal, SUM_JOURNAL_SIZE);
+-      written_size += SUM_JOURNAL_SIZE;
++      memcpy(kaddr, seg_i->journal, sbi->sum_journal_size);
++      written_size += sbi->sum_journal_size;
+       /* Step 2: write sit cache */
+       seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA);
+-      memcpy(kaddr + written_size, seg_i->journal, SUM_JOURNAL_SIZE);
+-      written_size += SUM_JOURNAL_SIZE;
++      memcpy(kaddr + written_size, seg_i->journal, sbi->sum_journal_size);
++      written_size += sbi->sum_journal_size;
+       /* Step 3: write summary entries */
+       for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) {
+@@ -4500,7 +4503,7 @@ static void write_compacted_summaries(st
+                               written_size = 0;
+                       }
+                       summary = (struct f2fs_summary *)(kaddr + written_size);
+-                      *summary = seg_i->sum_blk->entries[j];
++                      *summary = sum_entries(seg_i->sum_blk)[j];
+                       written_size += SUMMARY_SIZE;
+                       if (written_size + SUMMARY_SIZE <= PAGE_SIZE -
+@@ -4545,8 +4548,9 @@ void f2fs_write_node_summaries(struct f2
+       write_normal_summaries(sbi, start_blk, CURSEG_HOT_NODE);
+ }
+-int f2fs_lookup_journal_in_cursum(struct f2fs_journal *journal, int type,
+-                                      unsigned int val, int alloc)
++int f2fs_lookup_journal_in_cursum(struct f2fs_sb_info *sbi,
++                      struct f2fs_journal *journal, int type,
++                      unsigned int val, int alloc)
+ {
+       int i;
+@@ -4555,13 +4559,13 @@ int f2fs_lookup_journal_in_cursum(struct
+                       if (le32_to_cpu(nid_in_journal(journal, i)) == val)
+                               return i;
+               }
+-              if (alloc && __has_cursum_space(journal, 1, NAT_JOURNAL))
++              if (alloc && __has_cursum_space(sbi, journal, 1, NAT_JOURNAL))
+                       return update_nats_in_cursum(journal, 1);
+       } else if (type == SIT_JOURNAL) {
+               for (i = 0; i < sits_in_cursum(journal); i++)
+                       if (le32_to_cpu(segno_in_journal(journal, i)) == val)
+                               return i;
+-              if (alloc && __has_cursum_space(journal, 1, SIT_JOURNAL))
++              if (alloc && __has_cursum_space(sbi, journal, 1, SIT_JOURNAL))
+                       return update_sits_in_cursum(journal, 1);
+       }
+       return -1;
+@@ -4709,8 +4713,8 @@ void f2fs_flush_sit_entries(struct f2fs_
+        * entries, remove all entries from journal and add and account
+        * them in sit entry set.
+        */
+-      if (!__has_cursum_space(journal, sit_i->dirty_sentries, SIT_JOURNAL) ||
+-                                                              !to_journal)
++      if (!__has_cursum_space(sbi, journal,
++                      sit_i->dirty_sentries, SIT_JOURNAL) || !to_journal)
+               remove_sits_in_journal(sbi);
+       /*
+@@ -4727,7 +4731,8 @@ void f2fs_flush_sit_entries(struct f2fs_
+               unsigned int segno = start_segno;
+               if (to_journal &&
+-                      !__has_cursum_space(journal, ses->entry_cnt, SIT_JOURNAL))
++                      !__has_cursum_space(sbi, journal, ses->entry_cnt,
++                              SIT_JOURNAL))
+                       to_journal = false;
+               if (to_journal) {
+@@ -4755,7 +4760,7 @@ void f2fs_flush_sit_entries(struct f2fs_
+                       }
+                       if (to_journal) {
+-                              offset = f2fs_lookup_journal_in_cursum(journal,
++                              offset = f2fs_lookup_journal_in_cursum(sbi, journal,
+                                                       SIT_JOURNAL, segno, 1);
+                               f2fs_bug_on(sbi, offset < 0);
+                               segno_in_journal(journal, offset) =
+@@ -4962,12 +4967,13 @@ static int build_curseg(struct f2fs_sb_i
+       for (i = 0; i < NO_CHECK_TYPE; i++) {
+               mutex_init(&array[i].curseg_mutex);
+-              array[i].sum_blk = f2fs_kzalloc(sbi, PAGE_SIZE, GFP_KERNEL);
++              array[i].sum_blk = f2fs_kzalloc(sbi, sbi->sum_blocksize,
++                              GFP_KERNEL);
+               if (!array[i].sum_blk)
+                       return -ENOMEM;
+               init_rwsem(&array[i].journal_rwsem);
+               array[i].journal = f2fs_kzalloc(sbi,
+-                              sizeof(struct f2fs_journal), GFP_KERNEL);
++                              sbi->sum_journal_size, GFP_KERNEL);
+               if (!array[i].journal)
+                       return -ENOMEM;
+               array[i].seg_type = log_type_to_seg_type(i);
+--- a/fs/f2fs/segment.h
++++ b/fs/f2fs/segment.h
+@@ -90,12 +90,11 @@ static inline void sanity_check_seg_type
+ #define GET_ZONE_FROM_SEG(sbi, segno)                         \
+       GET_ZONE_FROM_SEC(sbi, GET_SEC_FROM_SEG(sbi, segno))
+-#define SUMS_PER_BLOCK (F2FS_BLKSIZE / F2FS_SUM_BLKSIZE)
+ #define GET_SUM_BLOCK(sbi, segno)     \
+-      (SM_I(sbi)->ssa_blkaddr + (segno / SUMS_PER_BLOCK))
+-#define GET_SUM_BLKOFF(segno) (segno % SUMS_PER_BLOCK)
+-#define SUM_BLK_PAGE_ADDR(folio, segno)       \
+-      (folio_address(folio) + GET_SUM_BLKOFF(segno) * F2FS_SUM_BLKSIZE)
++      (SM_I(sbi)->ssa_blkaddr + (segno / (sbi)->sums_per_block))
++#define GET_SUM_BLKOFF(sbi, segno) (segno % (sbi)->sums_per_block)
++#define SUM_BLK_PAGE_ADDR(sbi, folio, segno)  \
++      (folio_address(folio) + GET_SUM_BLKOFF(sbi, segno) * (sbi)->sum_blocksize)
+ #define GET_SUM_TYPE(footer) ((footer)->entry_type)
+ #define SET_SUM_TYPE(footer, type) ((footer)->entry_type = (type))
+--- a/fs/f2fs/super.c
++++ b/fs/f2fs/super.c
+@@ -4080,20 +4080,6 @@ static int sanity_check_raw_super(struct
+       if (sanity_check_area_boundary(sbi, folio, index))
+               return -EFSCORRUPTED;
+-      /*
+-       * Check for legacy summary layout on 16KB+ block devices.
+-       * Modern f2fs-tools packs multiple 4KB summary areas into one block,
+-       * whereas legacy versions used one block per summary, leading
+-       * to a much larger SSA.
+-       */
+-      if (SUMS_PER_BLOCK > 1 &&
+-                  !(__F2FS_HAS_FEATURE(raw_super, F2FS_FEATURE_PACKED_SSA))) {
+-              f2fs_info(sbi, "Error: Device formatted with a legacy version. "
+-                      "Please reformat with a tool supporting the packed ssa "
+-                      "feature for block sizes larger than 4kb.");
+-              return -EOPNOTSUPP;
+-      }
+-
+       return 0;
+ }
+@@ -4304,6 +4290,18 @@ static void init_sb_info(struct f2fs_sb_
+       spin_lock_init(&sbi->gc_remaining_trials_lock);
+       atomic64_set(&sbi->current_atomic_write, 0);
++      sbi->sum_blocksize = f2fs_sb_has_packed_ssa(sbi) ?
++              4096 : sbi->blocksize;
++      sbi->sums_per_block = sbi->blocksize / sbi->sum_blocksize;
++      sbi->entries_in_sum = sbi->sum_blocksize / 8;
++      sbi->sum_entry_size = SUMMARY_SIZE * sbi->entries_in_sum;
++      sbi->sum_journal_size = sbi->sum_blocksize - SUM_FOOTER_SIZE -
++              sbi->sum_entry_size;
++      sbi->nat_journal_entries = (sbi->sum_journal_size - 2) /
++              sizeof(struct nat_journal_entry);
++      sbi->sit_journal_entries = (sbi->sum_journal_size - 2) /
++              sizeof(struct sit_journal_entry);
++
+       sbi->dir_level = DEF_DIR_LEVEL;
+       sbi->interval_time[CP_TIME] = DEF_CP_INTERVAL;
+       sbi->interval_time[REQ_TIME] = DEF_IDLE_INTERVAL;
+--- a/include/linux/f2fs_fs.h
++++ b/include/linux/f2fs_fs.h
+@@ -17,7 +17,6 @@
+ #define F2FS_LOG_SECTORS_PER_BLOCK    (PAGE_SHIFT - 9) /* log number for sector/blk */
+ #define F2FS_BLKSIZE                  PAGE_SIZE /* support only block == page */
+ #define F2FS_BLKSIZE_BITS             PAGE_SHIFT /* bits for F2FS_BLKSIZE */
+-#define F2FS_SUM_BLKSIZE              4096    /* only support 4096 byte sum block */
+ #define F2FS_MAX_EXTENSION            64      /* # of extension entries */
+ #define F2FS_EXTENSION_LEN            8       /* max size of extension */
+@@ -442,10 +441,8 @@ struct f2fs_sit_block {
+  * from node's page's beginning to get a data block address.
+  * ex) data_blkaddr = (block_t)(nodepage_start_address + ofs_in_node)
+  */
+-#define ENTRIES_IN_SUM                (F2FS_SUM_BLKSIZE / 8)
+ #define       SUMMARY_SIZE            (7)     /* sizeof(struct f2fs_summary) */
+ #define       SUM_FOOTER_SIZE         (5)     /* sizeof(struct summary_footer) */
+-#define SUM_ENTRY_SIZE                (SUMMARY_SIZE * ENTRIES_IN_SUM)
+ /* a summary entry for a block in a segment */
+ struct f2fs_summary {
+@@ -468,22 +465,6 @@ struct summary_footer {
+       __le32 check_sum;               /* summary checksum */
+ } __packed;
+-#define SUM_JOURNAL_SIZE      (F2FS_SUM_BLKSIZE - SUM_FOOTER_SIZE -\
+-                              SUM_ENTRY_SIZE)
+-#define NAT_JOURNAL_ENTRIES   ((SUM_JOURNAL_SIZE - 2) /\
+-                              sizeof(struct nat_journal_entry))
+-#define NAT_JOURNAL_RESERVED  ((SUM_JOURNAL_SIZE - 2) %\
+-                              sizeof(struct nat_journal_entry))
+-#define SIT_JOURNAL_ENTRIES   ((SUM_JOURNAL_SIZE - 2) /\
+-                              sizeof(struct sit_journal_entry))
+-#define SIT_JOURNAL_RESERVED  ((SUM_JOURNAL_SIZE - 2) %\
+-                              sizeof(struct sit_journal_entry))
+-
+-/* Reserved area should make size of f2fs_extra_info equals to
+- * that of nat_journal and sit_journal.
+- */
+-#define EXTRA_INFO_RESERVED   (SUM_JOURNAL_SIZE - 2 - 8)
+-
+ /*
+  * frequently updated NAT/SIT entries can be stored in the spare area in
+  * summary blocks
+@@ -498,9 +479,16 @@ struct nat_journal_entry {
+       struct f2fs_nat_entry ne;
+ } __packed;
++/*
++ * The nat_journal structure is a placeholder whose actual size varies depending
++ * on the use of packed_ssa. Therefore, it must always be accessed only through
++ * specific sets of macros and fields, and size calculations should use
++ * size-related macros instead of sizeof().
++ * Relevant macros: sbi->nat_journal_entries, nat_in_journal(),
++ * nid_in_journal(), MAX_NAT_JENTRIES().
++ */
+ struct nat_journal {
+-      struct nat_journal_entry entries[NAT_JOURNAL_ENTRIES];
+-      __u8 reserved[NAT_JOURNAL_RESERVED];
++      struct nat_journal_entry entries[0];
+ } __packed;
+ struct sit_journal_entry {
+@@ -508,14 +496,21 @@ struct sit_journal_entry {
+       struct f2fs_sit_entry se;
+ } __packed;
++/*
++ * The sit_journal structure is a placeholder whose actual size varies depending
++ * on the use of packed_ssa. Therefore, it must always be accessed only through
++ * specific sets of macros and fields, and size calculations should use
++ * size-related macros instead of sizeof().
++ * Relevant macros: sbi->sit_journal_entries, sit_in_journal(),
++ * segno_in_journal(), MAX_SIT_JENTRIES().
++ */
+ struct sit_journal {
+-      struct sit_journal_entry entries[SIT_JOURNAL_ENTRIES];
+-      __u8 reserved[SIT_JOURNAL_RESERVED];
++      struct sit_journal_entry entries[0];
+ } __packed;
+ struct f2fs_extra_info {
+       __le64 kbytes_written;
+-      __u8 reserved[EXTRA_INFO_RESERVED];
++      __u8 reserved[];
+ } __packed;
+ struct f2fs_journal {
+@@ -531,11 +526,33 @@ struct f2fs_journal {
+       };
+ } __packed;
+-/* Block-sized summary block structure */
++/*
++ * Block-sized summary block structure
++ *
++ * The f2fs_summary_block structure is a placeholder whose actual size varies
++ * depending on the use of packed_ssa. Therefore, it must always be accessed
++ * only through specific sets of macros and fields, and size calculations should
++ * use size-related macros instead of sizeof().
++ * Relevant macros: sbi->sum_blocksize, sbi->entries_in_sum,
++ * sbi->sum_entry_size, sum_entries(), sum_journal(), sum_footer().
++ *
++ * Summary Block Layout
++ *
++ * +-----------------------+ <--- Block Start
++ * | struct f2fs_summary   |
++ * | entries[0]            |
++ * | ...                   |
++ * | entries[N-1]          |
++ * +-----------------------+
++ * | struct f2fs_journal   |
++ * +-----------------------+
++ * | struct summary_footer |
++ * +-----------------------+ <--- Block End
++ */
+ struct f2fs_summary_block {
+-      struct f2fs_summary entries[ENTRIES_IN_SUM];
+-      struct f2fs_journal journal;
+-      struct summary_footer footer;
++      struct f2fs_summary entries[0];
++      // struct f2fs_journal journal;
++      // struct summary_footer footer;
+ } __packed;
+ /*
diff --git a/queue-6.19/fbdev-rivafb-fix-divide-error-in-nv3_arb.patch b/queue-6.19/fbdev-rivafb-fix-divide-error-in-nv3_arb.patch
new file mode 100644 (file)
index 0000000..508d7ba
--- /dev/null
@@ -0,0 +1,65 @@
+From 0209e21e3c372fa2da04c39214bec0b64e4eb5f4 Mon Sep 17 00:00:00 2001
+From: Guangshuo Li <lgs201920130244@gmail.com>
+Date: Sun, 7 Dec 2025 15:25:32 +0800
+Subject: fbdev: rivafb: fix divide error in nv3_arb()
+
+From: Guangshuo Li <lgs201920130244@gmail.com>
+
+commit 0209e21e3c372fa2da04c39214bec0b64e4eb5f4 upstream.
+
+A userspace program can trigger the RIVA NV3 arbitration code by calling
+the FBIOPUT_VSCREENINFO ioctl on /dev/fb*. When doing so, the driver
+recomputes FIFO arbitration parameters in nv3_arb(), using state->mclk_khz
+(derived from the PRAMDAC MCLK PLL) as a divisor without validating it
+first.
+
+In a normal setup, state->mclk_khz is provided by the real hardware and is
+non-zero. However, an attacker can construct a malicious or misconfigured
+device (e.g. a crafted/emulated PCI device) that exposes a bogus PLL
+configuration, causing state->mclk_khz to become zero.  Once
+nv3_get_param() calls nv3_arb(), the division by state->mclk_khz in the gns
+calculation causes a divide error and crashes the kernel.
+
+Fix this by checking whether state->mclk_khz is zero and bailing out before
+doing the division.
+
+The following log reveals it:
+
+rivafb: setting virtual Y resolution to 2184
+divide error: 0000 [#1] PREEMPT SMP KASAN PTI
+CPU: 0 PID: 2187 Comm: syz-executor.0 Not tainted 5.18.0-rc1+ #1
+Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.0-59-gc9ba5276e321-prebuilt.qemu.org 04/01/2014
+RIP: 0010:nv3_arb drivers/video/fbdev/riva/riva_hw.c:439 [inline]
+RIP: 0010:nv3_get_param+0x3ab/0x13b0 drivers/video/fbdev/riva/riva_hw.c:546
+Call Trace:
+  nv3CalcArbitration.constprop.0+0x255/0x460 drivers/video/fbdev/riva/riva_hw.c:603
+  nv3UpdateArbitrationSettings drivers/video/fbdev/riva/riva_hw.c:637 [inline]
+  CalcStateExt+0x447/0x1b90 drivers/video/fbdev/riva/riva_hw.c:1246
+  riva_load_video_mode+0x8a9/0xea0 drivers/video/fbdev/riva/fbdev.c:779
+  rivafb_set_par+0xc0/0x5f0 drivers/video/fbdev/riva/fbdev.c:1196
+  fb_set_var+0x604/0xeb0 drivers/video/fbdev/core/fbmem.c:1033
+  do_fb_ioctl+0x234/0x670 drivers/video/fbdev/core/fbmem.c:1109
+  fb_ioctl+0xdd/0x130 drivers/video/fbdev/core/fbmem.c:1188
+  __x64_sys_ioctl+0x122/0x190 fs/ioctl.c:856
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Cc: stable@vger.kernel.org
+Signed-off-by: Guangshuo Li <lgs201920130244@gmail.com>
+Signed-off-by: Helge Deller <deller@gmx.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/video/fbdev/riva/riva_hw.c |    3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/video/fbdev/riva/riva_hw.c
++++ b/drivers/video/fbdev/riva/riva_hw.c
+@@ -436,6 +436,9 @@ static char nv3_arb(nv3_fifo_info * res_
+     vmisses = 2;
+     eburst_size = state->memory_width * 1;
+     mburst_size = 32;
++    if (!state->mclk_khz)
++      return (0);
++
+     gns = 1000000 * (gmisses*state->mem_page_miss + state->mem_latency)/state->mclk_khz;
+     ainfo->by_gfacc = gns*ainfo->gdrain_rate/1000000;
+     ainfo->wcmocc = 0;
diff --git a/queue-6.19/fbdev-smscufx-properly-copy-ioctl-memory-to-kernelspace.patch b/queue-6.19/fbdev-smscufx-properly-copy-ioctl-memory-to-kernelspace.patch
new file mode 100644 (file)
index 0000000..558b7db
--- /dev/null
@@ -0,0 +1,57 @@
+From 120adae7b42faa641179270c067864544a50ab69 Mon Sep 17 00:00:00 2001
+From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Date: Sun, 28 Dec 2025 14:17:03 +0100
+Subject: fbdev: smscufx: properly copy ioctl memory to kernelspace
+
+From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 120adae7b42faa641179270c067864544a50ab69 upstream.
+
+The UFX_IOCTL_REPORT_DAMAGE ioctl does not properly copy data from
+userspace to kernelspace, and instead directly references the memory,
+which can cause problems if invalid data is passed from userspace.  Fix
+this all up by correctly copying the memory before accessing it within
+the kernel.
+
+Reported-by: Tianchu Chen <flynnnchen@tencent.com>
+Cc: stable <stable@kernel.org>
+Cc: Steve Glendinning <steve.glendinning@shawell.net>
+Cc: Helge Deller <deller@gmx.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Helge Deller <deller@gmx.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/video/fbdev/smscufx.c |    8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+--- a/drivers/video/fbdev/smscufx.c
++++ b/drivers/video/fbdev/smscufx.c
+@@ -932,7 +932,6 @@ static int ufx_ops_ioctl(struct fb_info
+                        unsigned long arg)
+ {
+       struct ufx_data *dev = info->par;
+-      struct dloarea *area = NULL;
+       if (!atomic_read(&dev->usb_active))
+               return 0;
+@@ -947,6 +946,10 @@ static int ufx_ops_ioctl(struct fb_info
+       /* TODO: Help propose a standard fb.h ioctl to report mmap damage */
+       if (cmd == UFX_IOCTL_REPORT_DAMAGE) {
++              struct dloarea *area __free(kfree) = kmalloc(sizeof(*area), GFP_KERNEL);
++              if (!area)
++                      return -ENOMEM;
++
+               /* If we have a damage-aware client, turn fb_defio "off"
+                * To avoid perf imact of unnecessary page fault handling.
+                * Done by resetting the delay for this fb_info to a very
+@@ -956,7 +959,8 @@ static int ufx_ops_ioctl(struct fb_info
+               if (info->fbdefio)
+                       info->fbdefio->delay = UFX_DEFIO_WRITE_DISABLE;
+-              area = (struct dloarea *)arg;
++              if (copy_from_user(area, (u8 __user *)arg, sizeof(*area)))
++                      return -EFAULT;
+               if (area->x < 0)
+                       area->x = 0;
index 8499b237603bec6a6008d8c7a1cbc9c617351245..3540ae11f4550868f27ca95d5a5a0c3d78f91bfa 100644 (file)
@@ -1,3 +1,13 @@
 scsi-qla2xxx-fix-bsg_done-causing-double-free.patch
 arm64-dts-mediatek-mt8183-add-missing-endpoint-ids-to-display-graph.patch
 loongarch-rework-kasan-initialization-for-ptw-enabled-systems.patch
+fbdev-rivafb-fix-divide-error-in-nv3_arb.patch
+fbdev-smscufx-properly-copy-ioctl-memory-to-kernelspace.patch
+f2fs-fix-to-add-gc-count-stat-in-f2fs_gc_range.patch
+f2fs-fix-to-check-sysfs-filename-w-gc_pin_file_thresh-correctly.patch
+f2fs-fix-is_checkpointed-flag-inconsistency-issue-caused-by-concurrent-atomic-commit-and-checkpoint-writes.patch
+f2fs-fix-out-of-bounds-access-in-sysfs-attribute-read-write.patch
+f2fs-fix-to-avoid-uaf-in-f2fs_write_end_io.patch
+f2fs-support-non-4kb-block-size-without-packed_ssa-feature.patch
+f2fs-fix-to-avoid-mapping-wrong-physical-block-for-swapfile.patch
+f2fs-optimize-f2fs_overwrite_io-for-f2fs_iomap_begin.patch