Currently, we access journal ino through sbi->s_es->s_journal_inum, which directly reads from the ext4 sb buffer head. If someone modifies this underneath us then the s_journal_inum field might get corrupted. Although direct block device modifications can be expected to cause issues in the FS, let's cache s_journal_inum in sbi->s_journal_ino so our checks can be more resillient. Suggested-by: Baokun Li <libaokun1@xxxxxxxxxx> Signed-off-by: Ojaswin Mujoo <ojaswin@xxxxxxxxxxxxx> --- fs/ext4/block_validity.c | 23 ++++++++++++++++------- fs/ext4/ext4.h | 1 + fs/ext4/inode.c | 19 +++++++++++++++---- fs/ext4/super.c | 5 ++++- 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/fs/ext4/block_validity.c b/fs/ext4/block_validity.c index 87ee3a17bd29..54e6f3499263 100644 --- a/fs/ext4/block_validity.c +++ b/fs/ext4/block_validity.c @@ -247,9 +247,9 @@ int ext4_setup_system_zone(struct super_block *sb) if (ret) goto err; } - if (ext4_has_feature_journal(sb) && sbi->s_es->s_journal_inum) { + if (ext4_has_feature_journal(sb) && sbi->s_journal_ino) { ret = ext4_protect_reserved_inode(sb, system_blks, - le32_to_cpu(sbi->s_es->s_journal_inum)); + sbi->s_journal_ino); if (ret) goto err; } @@ -351,11 +351,20 @@ int ext4_check_blockref(const char *function, unsigned int line, { __le32 *bref = p; unsigned int blk; - - if (ext4_has_feature_journal(inode->i_sb) && - (inode->i_ino == - le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum))) - return 0; + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); + + if (ext4_has_feature_journal(inode->i_sb)) { + /* If we are called from journal init path then + * sbi->s_journal_ino would be 0 + */ + u32 journal_ino = sbi->s_journal_ino ? + sbi->s_journal_ino : + sbi->s_es->s_journal_inum; + WARN_ON_ONCE(journal_ino == 0); + + if (inode->i_ino == journal_ino) + return 0; + } while (bref < p+max) { blk = le32_to_cpu(*bref++); diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 2b7d781bfcad..648b0459e9fd 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1556,6 +1556,7 @@ struct ext4_sb_info { u32 s_max_batch_time; u32 s_min_batch_time; struct file *s_journal_bdev_file; + u32 s_journal_ino; #ifdef CONFIG_QUOTA /* Names of quota files with journalled quota */ char __rcu *s_qf_names[EXT4_MAXQUOTAS]; diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 365d31004bd0..50961bc0c94d 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -384,10 +384,21 @@ static int __check_block_validity(struct inode *inode, const char *func, unsigned int line, struct ext4_map_blocks *map) { - if (ext4_has_feature_journal(inode->i_sb) && - (inode->i_ino == - le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum))) - return 0; + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); + + if (ext4_has_feature_journal(inode->i_sb)) { + /* + * If we are called from journal init path then + * sbi->s_journal_ino would be 0 + */ + u32 journal_ino = sbi->s_journal_ino ? + sbi->s_journal_ino : + sbi->s_es->s_journal_inum; + WARN_ON_ONCE(journal_ino == 0); + + if (inode->i_ino == journal_ino) + return 0; + } if (!ext4_inode_block_valid(inode, map->m_pblk, map->m_len)) { ext4_error_inode(inode, func, line, map->m_pblk, "lblock %lu mapped to illegal pblock %llu " diff --git a/fs/ext4/super.c b/fs/ext4/super.c index a963ffda692a..44e79db7f12a 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -4162,7 +4162,8 @@ int ext4_calculate_overhead(struct super_block *sb) struct ext4_sb_info *sbi = EXT4_SB(sb); struct ext4_super_block *es = sbi->s_es; struct inode *j_inode; - unsigned int j_blocks, j_inum = le32_to_cpu(es->s_journal_inum); + unsigned int j_blocks; + u32 j_inum = sbi->s_journal_ino; ext4_group_t i, ngroups = ext4_get_groups_count(sb); ext4_fsblk_t overhead = 0; char *buf = (char *) get_zeroed_page(GFP_NOFS); @@ -6091,6 +6092,8 @@ static int ext4_load_journal(struct super_block *sb, ext4_commit_super(sb); } + EXT4_SB(sb)->s_journal_ino = le32_to_cpu(es->s_journal_inum); + return 0; err_out: -- 2.48.1