* [PATCH] f2fs: fix to do sanity check on node footer for non inode dnode
@ 2025-08-23 5:45 Chao Yu
2025-09-02 20:20 ` [f2fs-dev] " patchwork-bot+f2fs
0 siblings, 1 reply; 2+ messages in thread
From: Chao Yu @ 2025-08-23 5:45 UTC (permalink / raw)
To: jaegeuk
Cc: linux-f2fs-devel, linux-kernel, Chao Yu, stable,
syzbot+b9c7ffd609c3f09416ab
As syzbot reported below:
------------[ cut here ]------------
kernel BUG at fs/f2fs/file.c:1243!
Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI
CPU: 0 UID: 0 PID: 5354 Comm: syz.0.0 Not tainted 6.17.0-rc1-syzkaller-00211-g90d970cade8e #0 PREEMPT(full)
RIP: 0010:f2fs_truncate_hole+0x69e/0x6c0 fs/f2fs/file.c:1243
Call Trace:
<TASK>
f2fs_punch_hole+0x2db/0x330 fs/f2fs/file.c:1306
f2fs_fallocate+0x546/0x990 fs/f2fs/file.c:2018
vfs_fallocate+0x666/0x7e0 fs/open.c:342
ksys_fallocate fs/open.c:366 [inline]
__do_sys_fallocate fs/open.c:371 [inline]
__se_sys_fallocate fs/open.c:369 [inline]
__x64_sys_fallocate+0xc0/0x110 fs/open.c:369
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xfa/0x3b0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f1e65f8ebe9
w/ a fuzzed image, f2fs may encounter panic due to it detects inconsistent
truncation range in direct node in f2fs_truncate_hole().
The root cause is: a non-inode dnode may has the same footer.ino and
footer.nid, so the dnode will be parsed as an inode, then ADDRS_PER_PAGE()
may return wrong blkaddr count which may be 923 typically, by chance,
dn.ofs_in_node is equal to 923, then count can be calculated to 0 in below
statement, later it will trigger panic w/ f2fs_bug_on(, count == 0 || ...).
count = min(end_offset - dn.ofs_in_node, pg_end - pg_start);
This patch introduces a new node_type NODE_TYPE_NON_INODE, then allowing
passing the new_type to sanity_check_node_footer in f2fs_get_node_folio()
to detect corruption that a non-inode dnode has the same footer.ino and
footer.nid.
Scripts to reproduce:
mkfs.f2fs -f /dev/vdb
mount /dev/vdb /mnt/f2fs
touch /mnt/f2fs/foo
touch /mnt/f2fs/bar
dd if=/dev/zero of=/mnt/f2fs/foo bs=1M count=8
umount /mnt/f2fs
inject.f2fs --node --mb i_nid --nid 4 --idx 0 --val 5 /dev/vdb
mount /dev/vdb /mnt/f2fs
xfs_io /mnt/f2fs/foo -c "fpunch 6984k 4k"
Cc: stable@kernel.org
Reported-by: syzbot+b9c7ffd609c3f09416ab@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/linux-f2fs-devel/68a68e27.050a0220.1a3988.0002.GAE@google.com
Signed-off-by: Chao Yu <chao@kernel.org>
---
fs/f2fs/f2fs.h | 4 +++-
fs/f2fs/gc.c | 4 ++--
fs/f2fs/node.c | 58 +++++++++++++++++++++++++++++++---------------
fs/f2fs/node.h | 1 +
fs/f2fs/recovery.c | 2 +-
5 files changed, 46 insertions(+), 23 deletions(-)
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 1e3d0f6d1d4c..bba9ff617ea6 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -3817,6 +3817,7 @@ void f2fs_hash_filename(const struct inode *dir, struct f2fs_filename *fname);
* node.c
*/
struct node_info;
+enum node_type;
int f2fs_check_nid_range(struct f2fs_sb_info *sbi, nid_t nid);
bool f2fs_available_free_memory(struct f2fs_sb_info *sbi, int type);
@@ -3839,7 +3840,8 @@ int f2fs_remove_inode_page(struct inode *inode);
struct folio *f2fs_new_inode_folio(struct inode *inode);
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);
+struct folio *f2fs_get_node_folio(struct f2fs_sb_info *sbi, pgoff_t nid,
+ enum node_type node_type);
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);
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index b57b8fd64747..ed3acbfc83ca 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -1071,7 +1071,7 @@ static int gc_node_segment(struct f2fs_sb_info *sbi,
}
/* phase == 2 */
- node_folio = f2fs_get_node_folio(sbi, nid);
+ node_folio = f2fs_get_node_folio(sbi, nid, NODE_TYPE_REGULAR);
if (IS_ERR(node_folio))
continue;
@@ -1145,7 +1145,7 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
nid = le32_to_cpu(sum->nid);
ofs_in_node = le16_to_cpu(sum->ofs_in_node);
- node_folio = f2fs_get_node_folio(sbi, nid);
+ node_folio = f2fs_get_node_folio(sbi, nid, NODE_TYPE_REGULAR);
if (IS_ERR(node_folio))
return false;
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
index 109bc8898bc6..68d210f7798a 100644
--- a/fs/f2fs/node.c
+++ b/fs/f2fs/node.c
@@ -876,7 +876,8 @@ int f2fs_get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode)
}
if (!done) {
- nfolio[i] = f2fs_get_node_folio(sbi, nids[i]);
+ nfolio[i] = f2fs_get_node_folio(sbi, nids[i],
+ NODE_TYPE_NON_INODE);
if (IS_ERR(nfolio[i])) {
err = PTR_ERR(nfolio[i]);
f2fs_folio_put(nfolio[0], false);
@@ -994,7 +995,7 @@ static int truncate_dnode(struct dnode_of_data *dn)
return 1;
/* get direct node */
- folio = f2fs_get_node_folio(sbi, dn->nid);
+ folio = f2fs_get_node_folio(sbi, dn->nid, NODE_TYPE_NON_INODE);
if (PTR_ERR(folio) == -ENOENT)
return 1;
else if (IS_ERR(folio))
@@ -1038,7 +1039,8 @@ static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs,
trace_f2fs_truncate_nodes_enter(dn->inode, dn->nid, dn->data_blkaddr);
- folio = f2fs_get_node_folio(F2FS_I_SB(dn->inode), dn->nid);
+ folio = f2fs_get_node_folio(F2FS_I_SB(dn->inode), dn->nid,
+ NODE_TYPE_NON_INODE);
if (IS_ERR(folio)) {
trace_f2fs_truncate_nodes_exit(dn->inode, PTR_ERR(folio));
return PTR_ERR(folio);
@@ -1116,7 +1118,8 @@ static int truncate_partial_nodes(struct dnode_of_data *dn,
/* get indirect nodes in the path */
for (i = 0; i < idx + 1; i++) {
/* reference count'll be increased */
- folios[i] = f2fs_get_node_folio(F2FS_I_SB(dn->inode), nid[i]);
+ folios[i] = f2fs_get_node_folio(F2FS_I_SB(dn->inode), nid[i],
+ NODE_TYPE_NON_INODE);
if (IS_ERR(folios[i])) {
err = PTR_ERR(folios[i]);
idx = i - 1;
@@ -1501,21 +1504,37 @@ static int sanity_check_node_footer(struct f2fs_sb_info *sbi,
struct folio *folio, pgoff_t nid,
enum node_type ntype)
{
- if (unlikely(nid != nid_of_node(folio) ||
- (ntype == NODE_TYPE_INODE && !IS_INODE(folio)) ||
- (ntype == NODE_TYPE_XATTR &&
- !f2fs_has_xattr_block(ofs_of_node(folio))) ||
- time_to_inject(sbi, FAULT_INCONSISTENT_FOOTER))) {
- 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_handle_error(sbi, ERROR_INCONSISTENT_FOOTER);
- return -EFSCORRUPTED;
+ if (unlikely(nid != nid_of_node(folio)))
+ goto out_err;
+
+ switch (ntype) {
+ case NODE_TYPE_INODE:
+ if (!IS_INODE(folio))
+ goto out_err;
+ break;
+ case NODE_TYPE_XATTR:
+ if (!f2fs_has_xattr_block(ofs_of_node(folio)))
+ goto out_err;
+ break;
+ case NODE_TYPE_NON_INODE:
+ if (IS_INODE(folio))
+ goto out_err;
+ break;
+ default:
+ break;
}
+ if (time_to_inject(sbi, FAULT_INCONSISTENT_FOOTER))
+ 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_handle_error(sbi, ERROR_INCONSISTENT_FOOTER);
+ return -EFSCORRUPTED;
}
static struct folio *__get_node_folio(struct f2fs_sb_info *sbi, pgoff_t nid,
@@ -1572,9 +1591,10 @@ static struct folio *__get_node_folio(struct f2fs_sb_info *sbi, pgoff_t nid,
return ERR_PTR(err);
}
-struct folio *f2fs_get_node_folio(struct f2fs_sb_info *sbi, pgoff_t nid)
+struct folio *f2fs_get_node_folio(struct f2fs_sb_info *sbi, pgoff_t nid,
+ enum node_type node_type)
{
- return __get_node_folio(sbi, nid, NULL, 0, NODE_TYPE_REGULAR);
+ return __get_node_folio(sbi, nid, NULL, 0, node_type);
}
struct folio *f2fs_get_inode_folio(struct f2fs_sb_info *sbi, pgoff_t ino)
diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h
index 030390543b54..9cb8dcf8d417 100644
--- a/fs/f2fs/node.h
+++ b/fs/f2fs/node.h
@@ -57,6 +57,7 @@ enum node_type {
NODE_TYPE_REGULAR,
NODE_TYPE_INODE,
NODE_TYPE_XATTR,
+ NODE_TYPE_NON_INODE,
};
/*
diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c
index 842b2daffa85..870b68c2c032 100644
--- a/fs/f2fs/recovery.c
+++ b/fs/f2fs/recovery.c
@@ -548,7 +548,7 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
}
/* Get the node page */
- node_folio = f2fs_get_node_folio(sbi, nid);
+ node_folio = f2fs_get_node_folio(sbi, nid, NODE_TYPE_REGULAR);
if (IS_ERR(node_folio))
return PTR_ERR(node_folio);
--
2.40.1
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [f2fs-dev] [PATCH] f2fs: fix to do sanity check on node footer for non inode dnode
2025-08-23 5:45 [PATCH] f2fs: fix to do sanity check on node footer for non inode dnode Chao Yu
@ 2025-09-02 20:20 ` patchwork-bot+f2fs
0 siblings, 0 replies; 2+ messages in thread
From: patchwork-bot+f2fs @ 2025-09-02 20:20 UTC (permalink / raw)
To: Chao Yu
Cc: jaegeuk, syzbot+b9c7ffd609c3f09416ab, stable, linux-kernel,
linux-f2fs-devel
Hello:
This patch was applied to jaegeuk/f2fs.git (dev)
by Jaegeuk Kim <jaegeuk@kernel.org>:
On Sat, 23 Aug 2025 13:45:34 +0800 you wrote:
> As syzbot reported below:
>
> ------------[ cut here ]------------
> kernel BUG at fs/f2fs/file.c:1243!
> Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI
> CPU: 0 UID: 0 PID: 5354 Comm: syz.0.0 Not tainted 6.17.0-rc1-syzkaller-00211-g90d970cade8e #0 PREEMPT(full)
> RIP: 0010:f2fs_truncate_hole+0x69e/0x6c0 fs/f2fs/file.c:1243
> Call Trace:
> <TASK>
> f2fs_punch_hole+0x2db/0x330 fs/f2fs/file.c:1306
> f2fs_fallocate+0x546/0x990 fs/f2fs/file.c:2018
> vfs_fallocate+0x666/0x7e0 fs/open.c:342
> ksys_fallocate fs/open.c:366 [inline]
> __do_sys_fallocate fs/open.c:371 [inline]
> __se_sys_fallocate fs/open.c:369 [inline]
> __x64_sys_fallocate+0xc0/0x110 fs/open.c:369
> do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
> do_syscall_64+0xfa/0x3b0 arch/x86/entry/syscall_64.c:94
> entry_SYSCALL_64_after_hwframe+0x77/0x7f
> RIP: 0033:0x7f1e65f8ebe9
>
> [...]
Here is the summary with links:
- [f2fs-dev] f2fs: fix to do sanity check on node footer for non inode dnode
https://git.kernel.org/jaegeuk/f2fs/c/c18ecd99e0c7
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2025-09-02 20:20 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-23 5:45 [PATCH] f2fs: fix to do sanity check on node footer for non inode dnode Chao Yu
2025-09-02 20:20 ` [f2fs-dev] " patchwork-bot+f2fs
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).