]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
f2fs: fix to do sanity check on node footer in {read,write}_end_io
authorChao Yu <chao@kernel.org>
Mon, 12 Jan 2026 07:49:16 +0000 (15:49 +0800)
committerJaegeuk Kim <jaegeuk@kernel.org>
Sat, 17 Jan 2026 00:00:34 +0000 (00:00 +0000)
-----------[ cut here ]------------
kernel BUG at fs/f2fs/data.c:358!
Call Trace:
 <IRQ>
 blk_update_request+0x5eb/0xe70 block/blk-mq.c:987
 blk_mq_end_request+0x3e/0x70 block/blk-mq.c:1149
 blk_complete_reqs block/blk-mq.c:1224 [inline]
 blk_done_softirq+0x107/0x160 block/blk-mq.c:1229
 handle_softirqs+0x283/0x870 kernel/softirq.c:579
 __do_softirq kernel/softirq.c:613 [inline]
 invoke_softirq kernel/softirq.c:453 [inline]
 __irq_exit_rcu+0xca/0x1f0 kernel/softirq.c:680
 irq_exit_rcu+0x9/0x30 kernel/softirq.c:696
 instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1050 [inline]
 sysvec_apic_timer_interrupt+0xa6/0xc0 arch/x86/kernel/apic/apic.c:1050
 </IRQ>

In f2fs_write_end_io(), it detects there is inconsistency in between
node page index (nid) and footer.nid of node page.

If footer of node page is corrupted in fuzzed image, then we load corrupted
node page w/ async method, e.g. f2fs_ra_node_pages() or f2fs_ra_node_page(),
in where we won't do sanity check on node footer, once node page becomes
dirty, we will encounter this bug after node page writeback.

Cc: stable@kernel.org
Reported-by: syzbot+803dd716c4310d16ff3a@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=803dd716c4310d16ff3a
Signed-off-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
fs/f2fs/data.c
fs/f2fs/f2fs.h
fs/f2fs/node.c
fs/f2fs/node.h

index f461f1318b4cc05e0f324e30c368d8c3e672dc51..9b70b6d3370314534301aeeb1c5658c4402405a4 100644 (file)
@@ -172,6 +172,11 @@ static void f2fs_finish_read_bio(struct bio *bio, bool in_task)
                while (nr_pages--)
                        dec_page_count(F2FS_F_SB(folio), __read_io_type(folio));
 
+               if (F2FS_F_SB(folio)->node_inode && is_node_folio(folio) &&
+                       f2fs_sanity_check_node_footer(F2FS_F_SB(folio),
+                               folio, folio->index, NODE_TYPE_REGULAR, true))
+                       bio->bi_status = BLK_STS_IOERR;
+
                if (finished)
                        folio_end_read(folio, bio->bi_status == BLK_STS_OK);
        }
@@ -374,8 +379,11 @@ static void f2fs_write_end_io(struct bio *bio)
                                                STOP_CP_REASON_WRITE_FAIL);
                }
 
-               f2fs_bug_on(sbi, is_node_folio(folio) &&
-                               folio->index != nid_of_node(folio));
+               if (is_node_folio(folio)) {
+                       f2fs_sanity_check_node_footer(sbi, folio,
+                               folio->index, NODE_TYPE_REGULAR, true);
+                       f2fs_bug_on(sbi, folio->index != nid_of_node(folio));
+               }
 
                dec_page_count(sbi, type);
 
index ae78b8e1ca0cef3538cfa4c5cda774722c87a5ec..d41210a381cdb063e54eada1b9abdec3ad66f95e 100644 (file)
@@ -1572,6 +1572,14 @@ enum f2fs_lookup_mode {
        LOOKUP_AUTO,
 };
 
+/* For node type in __get_node_folio() */
+enum node_type {
+       NODE_TYPE_REGULAR,
+       NODE_TYPE_INODE,
+       NODE_TYPE_XATTR,
+       NODE_TYPE_NON_INODE,
+};
+
 /* a threshold of maximum elapsed time in critical region to print tracepoint */
 #define MAX_LOCK_ELAPSED_TIME          500
 
@@ -3915,6 +3923,9 @@ struct folio *f2fs_new_node_folio(struct dnode_of_data *dn, unsigned int ofs);
 void f2fs_ra_node_page(struct f2fs_sb_info *sbi, nid_t nid);
 struct folio *f2fs_get_node_folio(struct f2fs_sb_info *sbi, pgoff_t nid,
                                                enum node_type node_type);
+int f2fs_sanity_check_node_footer(struct f2fs_sb_info *sbi,
+                                       struct folio *folio, pgoff_t nid,
+                                       enum node_type ntype, bool in_irq);
 struct folio *f2fs_get_inode_folio(struct f2fs_sb_info *sbi, pgoff_t ino);
 struct folio *f2fs_get_xnode_folio(struct f2fs_sb_info *sbi, pgoff_t xnid);
 int f2fs_move_node_folio(struct folio *node_folio, int gc_type);
index 30e26b878af0b8fa7da7b46c7226c3cdc97ec8a1..efd4f176a1f447e2ba4dccf5c327fcc9e8a08a44 100644 (file)
@@ -1511,9 +1511,9 @@ void f2fs_ra_node_page(struct f2fs_sb_info *sbi, nid_t nid)
        f2fs_folio_put(afolio, err ? true : false);
 }
 
-static int sanity_check_node_footer(struct f2fs_sb_info *sbi,
+int f2fs_sanity_check_node_footer(struct f2fs_sb_info *sbi,
                                        struct folio *folio, pgoff_t nid,
-                                       enum node_type ntype)
+                                       enum node_type ntype, bool in_irq)
 {
        if (unlikely(nid != nid_of_node(folio)))
                goto out_err;
@@ -1538,12 +1538,13 @@ static int sanity_check_node_footer(struct f2fs_sb_info *sbi,
                goto out_err;
        return 0;
 out_err:
-       f2fs_warn(sbi, "inconsistent node block, node_type:%d, nid:%lu, "
-                 "node_footer[nid:%u,ino:%u,ofs:%u,cpver:%llu,blkaddr:%u]",
-                 ntype, nid, nid_of_node(folio), ino_of_node(folio),
-                 ofs_of_node(folio), cpver_of_node(folio),
-                 next_blkaddr_of_node(folio));
        set_sbi_flag(sbi, SBI_NEED_FSCK);
+       f2fs_warn_ratelimited(sbi, "inconsistent node block, node_type:%d, nid:%lu, "
+               "node_footer[nid:%u,ino:%u,ofs:%u,cpver:%llu,blkaddr:%u]",
+               ntype, nid, nid_of_node(folio), ino_of_node(folio),
+               ofs_of_node(folio), cpver_of_node(folio),
+               next_blkaddr_of_node(folio));
+
        f2fs_handle_error(sbi, ERROR_INCONSISTENT_FOOTER);
        return -EFSCORRUPTED;
 }
@@ -1589,7 +1590,7 @@ repeat:
                goto out_err;
        }
 page_hit:
-       err = sanity_check_node_footer(sbi, folio, nid, ntype);
+       err = f2fs_sanity_check_node_footer(sbi, folio, nid, ntype, false);
        if (!err)
                return folio;
 out_err:
@@ -1764,7 +1765,8 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted
        /* get old block addr of this node page */
        nid = nid_of_node(folio);
 
-       if (sanity_check_node_footer(sbi, folio, nid, NODE_TYPE_REGULAR)) {
+       if (f2fs_sanity_check_node_footer(sbi, folio, nid,
+                                       NODE_TYPE_REGULAR, false)) {
                f2fs_handle_critical_error(sbi, STOP_CP_REASON_CORRUPTED_NID);
                goto redirty_out;
        }
index 9cb8dcf8d41760ababd978c879a3fc16a7df8917..824ac9f0e6e42a6dc7db49cf316ae8f187b1d3df 100644 (file)
@@ -52,14 +52,6 @@ enum {
        IS_PREALLOC,            /* nat entry is preallocated */
 };
 
-/* For node type in __get_node_folio() */
-enum node_type {
-       NODE_TYPE_REGULAR,
-       NODE_TYPE_INODE,
-       NODE_TYPE_XATTR,
-       NODE_TYPE_NON_INODE,
-};
-
 /*
  * For node information
  */