From: Chao Yu chao@kernel.org
stable inclusion from stable-v6.6.23 commit c92f2927df860a60ba815d3ee610a944b92a8694 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I9HKE5 CVE: CVE-2024-26869
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=...
--------------------------------
[ Upstream commit 9f0c4a46be1fe9b97dbe66d49204c1371e3ece65 ]
Below race case can cause data corruption:
Thread A GC thread - gc_data_segment - ra_data_block - locked meta_inode page - f2fs_inplace_write_data - invalidate_mapping_pages : fail to invalidate meta_inode page due to lock failure or dirty|writeback status - f2fs_submit_page_bio : write last dirty data to old blkaddr - move_data_block - load old data from meta_inode page - f2fs_submit_page_write : write old data to new blkaddr
Because invalidate_mapping_pages() will skip invalidating page which has unclear status including locked, dirty, writeback and so on, so we need to use truncate_inode_pages_range() instead of invalidate_mapping_pages() to make sure meta_inode page will be dropped.
Fixes: 6aa58d8ad20a ("f2fs: readahead encrypted block during GC") Fixes: e3b49ea36802 ("f2fs: invalidate META_MAPPING before IPU/DIO write") Signed-off-by: Chao Yu chao@kernel.org Signed-off-by: Jaegeuk Kim jaegeuk@kernel.org Signed-off-by: Sasha Levin sashal@kernel.org
Conflicts: fs/f2fs/data.c fs/f2fs/gc.c fs/f2fs/segment.c fs/f2fs/f2fs.h Signed-off-by: Zizhi Wo wozizhi@huawei.com --- fs/f2fs/data.c | 3 +-- fs/f2fs/f2fs.h | 26 ++++++++++++++++++++++++++ fs/f2fs/gc.c | 3 +-- fs/f2fs/segment.c | 8 +++----- include/linux/f2fs_fs.h | 1 + 5 files changed, 32 insertions(+), 9 deletions(-)
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index fa683a854341..06224270d5ce 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -914,8 +914,7 @@ static int __allocate_data_block(struct dnode_of_data *dn, int seg_type) f2fs_allocate_data_block(sbi, NULL, old_blkaddr, &dn->data_blkaddr, &sum, seg_type, NULL, false); if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO) - invalidate_mapping_pages(META_MAPPING(sbi), - old_blkaddr, old_blkaddr); + f2fs_truncate_meta_inode_pages(sbi, old_blkaddr, 1); f2fs_set_data_blkaddr(dn);
/* diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 6be5cb26d064..8e8b2d46211d 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -3496,6 +3496,32 @@ extern void f2fs_build_fault_attr(struct f2fs_sb_info *sbi, unsigned int rate,
#endif
+static inline void f2fs_truncate_meta_inode_pages(struct f2fs_sb_info *sbi, + block_t blkaddr, unsigned int cnt) +{ + bool need_submit = false; + int i = 0; + + do { + struct page *page; + + page = find_get_page(META_MAPPING(sbi), blkaddr + i); + if (page) { + if (PageWriteback(page)) + need_submit = true; + f2fs_put_page(page, 0); + } + } while (++i < cnt && !need_submit); + + if (need_submit) + f2fs_submit_merged_write_cond(sbi, sbi->meta_inode, + NULL, 0, DATA); + + truncate_inode_pages_range(META_MAPPING(sbi), + F2FS_BLK_TO_BYTES((loff_t)blkaddr), + F2FS_BLK_END_BYTES((loff_t)(blkaddr + cnt - 1))); +} + #define EFSBADCRC EBADMSG /* Bad CRC detected */ #define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index f4f7b8feebef..44781a160a29 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -759,8 +759,7 @@ static void move_data_block(struct inode *inode, block_t bidx, updated = true; } f2fs_put_page(mpage, 1); - invalidate_mapping_pages(META_MAPPING(fio.sbi), - fio.old_blkaddr, fio.old_blkaddr); + f2fs_truncate_meta_inode_pages(fio.sbi, fio.old_blkaddr, 1); if (updated) goto write_page; } diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 0fdfbbaeb386..2d02ac46657e 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2101,7 +2101,7 @@ void f2fs_invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr) if (addr == NEW_ADDR) return;
- invalidate_mapping_pages(META_MAPPING(sbi), addr, addr); + f2fs_truncate_meta_inode_pages(sbi, addr, 1);
/* add it into sit main buffer */ down_write(&sit_i->sentry_lock); @@ -3003,8 +3003,7 @@ static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio) f2fs_allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr, &fio->new_blkaddr, sum, type, fio, true); if (GET_SEGNO(fio->sbi, fio->old_blkaddr) != NULL_SEGNO) - invalidate_mapping_pages(META_MAPPING(fio->sbi), - fio->old_blkaddr, fio->old_blkaddr); + f2fs_truncate_meta_inode_pages(fio->sbi, fio->old_blkaddr, 1);
/* writeout dirty page into bdev */ f2fs_submit_page_write(fio); @@ -3165,8 +3164,7 @@ void f2fs_do_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, if (!recover_curseg || recover_newaddr) update_sit_entry(sbi, new_blkaddr, 1); if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO) { - invalidate_mapping_pages(META_MAPPING(sbi), - old_blkaddr, old_blkaddr); + f2fs_truncate_meta_inode_pages(sbi, old_blkaddr, 1); update_sit_entry(sbi, old_blkaddr, -1); }
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 40fec5f94949..214d4cb9df2a 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -29,6 +29,7 @@
#define F2FS_BYTES_TO_BLK(bytes) ((bytes) >> F2FS_BLKSIZE_BITS) #define F2FS_BLK_TO_BYTES(blk) ((blk) << F2FS_BLKSIZE_BITS) +#define F2FS_BLK_END_BYTES(blk) (F2FS_BLK_TO_BYTES(blk + 1) - 1)
/* 0, 1(node nid), 2(meta nid) are reserved node id */ #define F2FS_RESERVED_NODE_NUM 3