Skip to content

Commit 186098f

Browse files
chaseyugregkh
authored andcommitted
f2fs: fix to do sanity check on node footer for non inode dnode
commit c18ecd9 upstream. 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: [email protected] Reported-by: [email protected] Closes: https://lore.kernel.org/linux-f2fs-devel/[email protected] Signed-off-by: Chao Yu <[email protected]> Signed-off-by: Jaegeuk Kim <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 7366830 commit 186098f

File tree

5 files changed

+46
-23
lines changed

5 files changed

+46
-23
lines changed

fs/f2fs/f2fs.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3764,6 +3764,7 @@ void f2fs_hash_filename(const struct inode *dir, struct f2fs_filename *fname);
37643764
* node.c
37653765
*/
37663766
struct node_info;
3767+
enum node_type;
37673768

37683769
int f2fs_check_nid_range(struct f2fs_sb_info *sbi, nid_t nid);
37693770
bool f2fs_available_free_memory(struct f2fs_sb_info *sbi, int type);
@@ -3786,7 +3787,8 @@ int f2fs_remove_inode_page(struct inode *inode);
37863787
struct folio *f2fs_new_inode_folio(struct inode *inode);
37873788
struct folio *f2fs_new_node_folio(struct dnode_of_data *dn, unsigned int ofs);
37883789
void f2fs_ra_node_page(struct f2fs_sb_info *sbi, nid_t nid);
3789-
struct folio *f2fs_get_node_folio(struct f2fs_sb_info *sbi, pgoff_t nid);
3790+
struct folio *f2fs_get_node_folio(struct f2fs_sb_info *sbi, pgoff_t nid,
3791+
enum node_type node_type);
37903792
struct folio *f2fs_get_inode_folio(struct f2fs_sb_info *sbi, pgoff_t ino);
37913793
struct folio *f2fs_get_xnode_folio(struct f2fs_sb_info *sbi, pgoff_t xnid);
37923794
int f2fs_move_node_folio(struct folio *node_folio, int gc_type);

fs/f2fs/gc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,7 +1071,7 @@ static int gc_node_segment(struct f2fs_sb_info *sbi,
10711071
}
10721072

10731073
/* phase == 2 */
1074-
node_folio = f2fs_get_node_folio(sbi, nid);
1074+
node_folio = f2fs_get_node_folio(sbi, nid, NODE_TYPE_REGULAR);
10751075
if (IS_ERR(node_folio))
10761076
continue;
10771077

@@ -1145,7 +1145,7 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
11451145
nid = le32_to_cpu(sum->nid);
11461146
ofs_in_node = le16_to_cpu(sum->ofs_in_node);
11471147

1148-
node_folio = f2fs_get_node_folio(sbi, nid);
1148+
node_folio = f2fs_get_node_folio(sbi, nid, NODE_TYPE_REGULAR);
11491149
if (IS_ERR(node_folio))
11501150
return false;
11511151

fs/f2fs/node.c

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -871,7 +871,8 @@ int f2fs_get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode)
871871
}
872872

873873
if (!done) {
874-
nfolio[i] = f2fs_get_node_folio(sbi, nids[i]);
874+
nfolio[i] = f2fs_get_node_folio(sbi, nids[i],
875+
NODE_TYPE_NON_INODE);
875876
if (IS_ERR(nfolio[i])) {
876877
err = PTR_ERR(nfolio[i]);
877878
f2fs_folio_put(nfolio[0], false);
@@ -989,7 +990,7 @@ static int truncate_dnode(struct dnode_of_data *dn)
989990
return 1;
990991

991992
/* get direct node */
992-
folio = f2fs_get_node_folio(sbi, dn->nid);
993+
folio = f2fs_get_node_folio(sbi, dn->nid, NODE_TYPE_NON_INODE);
993994
if (PTR_ERR(folio) == -ENOENT)
994995
return 1;
995996
else if (IS_ERR(folio))
@@ -1033,7 +1034,8 @@ static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs,
10331034

10341035
trace_f2fs_truncate_nodes_enter(dn->inode, dn->nid, dn->data_blkaddr);
10351036

1036-
folio = f2fs_get_node_folio(F2FS_I_SB(dn->inode), dn->nid);
1037+
folio = f2fs_get_node_folio(F2FS_I_SB(dn->inode), dn->nid,
1038+
NODE_TYPE_NON_INODE);
10371039
if (IS_ERR(folio)) {
10381040
trace_f2fs_truncate_nodes_exit(dn->inode, PTR_ERR(folio));
10391041
return PTR_ERR(folio);
@@ -1111,7 +1113,8 @@ static int truncate_partial_nodes(struct dnode_of_data *dn,
11111113
/* get indirect nodes in the path */
11121114
for (i = 0; i < idx + 1; i++) {
11131115
/* reference count'll be increased */
1114-
folios[i] = f2fs_get_node_folio(F2FS_I_SB(dn->inode), nid[i]);
1116+
folios[i] = f2fs_get_node_folio(F2FS_I_SB(dn->inode), nid[i],
1117+
NODE_TYPE_NON_INODE);
11151118
if (IS_ERR(folios[i])) {
11161119
err = PTR_ERR(folios[i]);
11171120
idx = i - 1;
@@ -1496,21 +1499,37 @@ static int sanity_check_node_footer(struct f2fs_sb_info *sbi,
14961499
struct folio *folio, pgoff_t nid,
14971500
enum node_type ntype)
14981501
{
1499-
if (unlikely(nid != nid_of_node(folio) ||
1500-
(ntype == NODE_TYPE_INODE && !IS_INODE(folio)) ||
1501-
(ntype == NODE_TYPE_XATTR &&
1502-
!f2fs_has_xattr_block(ofs_of_node(folio))) ||
1503-
time_to_inject(sbi, FAULT_INCONSISTENT_FOOTER))) {
1504-
f2fs_warn(sbi, "inconsistent node block, node_type:%d, nid:%lu, "
1505-
"node_footer[nid:%u,ino:%u,ofs:%u,cpver:%llu,blkaddr:%u]",
1506-
ntype, nid, nid_of_node(folio), ino_of_node(folio),
1507-
ofs_of_node(folio), cpver_of_node(folio),
1508-
next_blkaddr_of_node(folio));
1509-
set_sbi_flag(sbi, SBI_NEED_FSCK);
1510-
f2fs_handle_error(sbi, ERROR_INCONSISTENT_FOOTER);
1511-
return -EFSCORRUPTED;
1502+
if (unlikely(nid != nid_of_node(folio)))
1503+
goto out_err;
1504+
1505+
switch (ntype) {
1506+
case NODE_TYPE_INODE:
1507+
if (!IS_INODE(folio))
1508+
goto out_err;
1509+
break;
1510+
case NODE_TYPE_XATTR:
1511+
if (!f2fs_has_xattr_block(ofs_of_node(folio)))
1512+
goto out_err;
1513+
break;
1514+
case NODE_TYPE_NON_INODE:
1515+
if (IS_INODE(folio))
1516+
goto out_err;
1517+
break;
1518+
default:
1519+
break;
15121520
}
1521+
if (time_to_inject(sbi, FAULT_INCONSISTENT_FOOTER))
1522+
goto out_err;
15131523
return 0;
1524+
out_err:
1525+
f2fs_warn(sbi, "inconsistent node block, node_type:%d, nid:%lu, "
1526+
"node_footer[nid:%u,ino:%u,ofs:%u,cpver:%llu,blkaddr:%u]",
1527+
ntype, nid, nid_of_node(folio), ino_of_node(folio),
1528+
ofs_of_node(folio), cpver_of_node(folio),
1529+
next_blkaddr_of_node(folio));
1530+
set_sbi_flag(sbi, SBI_NEED_FSCK);
1531+
f2fs_handle_error(sbi, ERROR_INCONSISTENT_FOOTER);
1532+
return -EFSCORRUPTED;
15141533
}
15151534

15161535
static struct folio *__get_node_folio(struct f2fs_sb_info *sbi, pgoff_t nid,
@@ -1567,9 +1586,10 @@ static struct folio *__get_node_folio(struct f2fs_sb_info *sbi, pgoff_t nid,
15671586
return ERR_PTR(err);
15681587
}
15691588

1570-
struct folio *f2fs_get_node_folio(struct f2fs_sb_info *sbi, pgoff_t nid)
1589+
struct folio *f2fs_get_node_folio(struct f2fs_sb_info *sbi, pgoff_t nid,
1590+
enum node_type node_type)
15711591
{
1572-
return __get_node_folio(sbi, nid, NULL, 0, NODE_TYPE_REGULAR);
1592+
return __get_node_folio(sbi, nid, NULL, 0, node_type);
15731593
}
15741594

15751595
struct folio *f2fs_get_inode_folio(struct f2fs_sb_info *sbi, pgoff_t ino)

fs/f2fs/node.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ enum node_type {
5757
NODE_TYPE_REGULAR,
5858
NODE_TYPE_INODE,
5959
NODE_TYPE_XATTR,
60+
NODE_TYPE_NON_INODE,
6061
};
6162

6263
/*

fs/f2fs/recovery.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
548548
}
549549

550550
/* Get the node page */
551-
node_folio = f2fs_get_node_folio(sbi, nid);
551+
node_folio = f2fs_get_node_folio(sbi, nid, NODE_TYPE_REGULAR);
552552
if (IS_ERR(node_folio))
553553
return PTR_ERR(node_folio);
554554

0 commit comments

Comments
 (0)