[SRU][F][PATCH 2/4] ext4: simulate various I/O and checksum errors when reading metadata

Tim Gardner tim.gardner at canonical.com
Fri Oct 8 12:36:03 UTC 2021



On 10/7/21 2:05 PM, Luke Nowakowski-Krijger wrote:
> From: Theodore Ts'o <tytso at mit.edu>
> 
> This allows us to test various error handling code paths
> 
> Link: https://lore.kernel.org/r/20191209012317.59398-1-tytso@mit.edu
> Signed-off-by: Theodore Ts'o <tytso at mit.edu>
> (backported from commit 46f870d690fecc792a66730dcbbf0aa109f5f9ab)
> [lukenow: updated minor context change to include sbi_array_rcu_deref
> define]

Similar issue here in your description (see patch 1). The change was a 
simple context adjustment to add simulated fail codes.

> CVE-2021-3428
> Signed-off-by: Luke Nowakowski-Krijger <luke.nowakowskikrijger at canonical.com>
> ---
>   fs/ext4/balloc.c |  4 +++-
>   fs/ext4/ext4.h   | 37 +++++++++++++++++++++++++++++++++++++
>   fs/ext4/ialloc.c |  4 +++-
>   fs/ext4/inode.c  |  6 +++++-
>   fs/ext4/namei.c  | 11 ++++++++---
>   fs/ext4/sysfs.c  | 23 +++++++++++++++++++++++
>   6 files changed, 79 insertions(+), 6 deletions(-)
> 
> diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c
> index f6fee620e57d..7d1a561896ed 100644
> --- a/fs/ext4/balloc.c
> +++ b/fs/ext4/balloc.c
> @@ -379,7 +379,8 @@ static int ext4_validate_block_bitmap(struct super_block *sb,
>   	if (buffer_verified(bh))
>   		goto verified;
>   	if (unlikely(!ext4_block_bitmap_csum_verify(sb, block_group,
> -			desc, bh))) {
> +						    desc, bh) ||
> +		     ext4_simulate_fail(sb, EXT4_SIM_BBITMAP_CRC))) {
>   		ext4_unlock_group(sb, block_group);
>   		ext4_error(sb, "bg %u: bad block bitmap checksum", block_group);
>   		ext4_mark_group_bitmap_corrupted(sb, block_group,
> @@ -513,6 +514,7 @@ int ext4_wait_block_bitmap(struct super_block *sb, ext4_group_t block_group,
>   	if (!desc)
>   		return -EFSCORRUPTED;
>   	wait_on_buffer(bh);
> +	ext4_simulate_fail_bh(sb, bh, EXT4_SIM_BBITMAP_EIO);
>   	if (!buffer_uptodate(bh)) {
>   		ext4_set_errno(sb, EIO);
>   		ext4_error(sb, "Cannot read block bitmap - "
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 68778d362467..ce7fd3f8ca26 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -1556,6 +1556,9 @@ struct ext4_sb_info {
>   	 */
>   	struct percpu_rw_semaphore s_writepages_rwsem;
>   	struct dax_device *s_daxdev;
> +#ifdef CONFIG_EXT4_DEBUG
> +	unsigned long s_simulate_fail;
> +#endif
>   };
>   
>   static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb)
> @@ -1591,6 +1594,40 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
>   	_v;								   \
>   })
>   
> +/*
> + * Simulate_fail codes
> + */
> +#define EXT4_SIM_BBITMAP_EIO	1
> +#define EXT4_SIM_BBITMAP_CRC	2
> +#define EXT4_SIM_IBITMAP_EIO	3
> +#define EXT4_SIM_IBITMAP_CRC	4
> +#define EXT4_SIM_INODE_EIO	5
> +#define EXT4_SIM_INODE_CRC	6
> +#define EXT4_SIM_DIRBLOCK_EIO	7
> +#define EXT4_SIM_DIRBLOCK_CRC	8
> +
> +static inline bool ext4_simulate_fail(struct super_block *sb,
> +				     unsigned long code)
> +{
> +#ifdef CONFIG_EXT4_DEBUG
> +	struct ext4_sb_info *sbi = EXT4_SB(sb);
> +
> +	if (unlikely(sbi->s_simulate_fail == code)) {
> +		sbi->s_simulate_fail = 0;
> +		return true;
> +	}
> +#endif
> +	return false;
> +}
> +
> +static inline void ext4_simulate_fail_bh(struct super_block *sb,
> +					 struct buffer_head *bh,
> +					 unsigned long code)
> +{
> +	if (!IS_ERR(bh) && ext4_simulate_fail(sb, code))
> +		clear_buffer_uptodate(bh);
> +}
> +
>   /*
>    * Error number codes for s_{first,last}_error_errno
>    *
> diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
> index 139fa1e9048a..5c21f78881a8 100644
> --- a/fs/ext4/ialloc.c
> +++ b/fs/ext4/ialloc.c
> @@ -94,7 +94,8 @@ static int ext4_validate_inode_bitmap(struct super_block *sb,
>   		goto verified;
>   	blk = ext4_inode_bitmap(sb, desc);
>   	if (!ext4_inode_bitmap_csum_verify(sb, block_group, desc, bh,
> -					   EXT4_INODES_PER_GROUP(sb) / 8)) {
> +					   EXT4_INODES_PER_GROUP(sb) / 8) ||
> +	    ext4_simulate_fail(sb, EXT4_SIM_IBITMAP_CRC)) {
>   		ext4_unlock_group(sb, block_group);
>   		ext4_error(sb, "Corrupt inode bitmap - block_group = %u, "
>   			   "inode_bitmap = %llu", block_group, blk);
> @@ -192,6 +193,7 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
>   	get_bh(bh);
>   	submit_bh(REQ_OP_READ, REQ_META | REQ_PRIO, bh);
>   	wait_on_buffer(bh);
> +	ext4_simulate_fail_bh(sb, bh, EXT4_SIM_IBITMAP_EIO);
>   	if (!buffer_uptodate(bh)) {
>   		put_bh(bh);
>   		ext4_set_errno(sb, EIO);
> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
> index 1d48cdd77fbb..7f91d95f9f32 100644
> --- a/fs/ext4/inode.c
> +++ b/fs/ext4/inode.c
> @@ -4617,6 +4617,8 @@ static int __ext4_get_inode_loc(struct inode *inode,
>   	bh = sb_getblk(sb, block);
>   	if (unlikely(!bh))
>   		return -ENOMEM;
> +	if (ext4_simulate_fail(sb, EXT4_SIM_INODE_EIO))
> +		goto simulate_eio;
>   	if (!buffer_uptodate(bh)) {
>   		lock_buffer(bh);
>   
> @@ -4715,6 +4717,7 @@ static int __ext4_get_inode_loc(struct inode *inode,
>   		blk_finish_plug(&plug);
>   		wait_on_buffer(bh);
>   		if (!buffer_uptodate(bh)) {
> +		simulate_eio:
>   			ext4_set_errno(inode->i_sb, EIO);
>   			EXT4_ERROR_INODE_BLOCK(inode, block,
>   					       "unable to read itable block");
> @@ -4929,7 +4932,8 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
>   					      sizeof(gen));
>   	}
>   
> -	if (!ext4_inode_csum_verify(inode, raw_inode, ei)) {
> +	if (!ext4_inode_csum_verify(inode, raw_inode, ei) ||
> +	    ext4_simulate_fail(sb, EXT4_SIM_INODE_CRC)) {
>   		ext4_set_errno(inode->i_sb, EFSBADCRC);
>   		ext4_error_inode(inode, function, line, 0,
>   				 "iget: checksum invalid");
> diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
> index 5ec855a3b3e7..71ac73c31a16 100644
> --- a/fs/ext4/namei.c
> +++ b/fs/ext4/namei.c
> @@ -109,7 +109,10 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
>   	struct ext4_dir_entry *dirent;
>   	int is_dx_block = 0;
>   
> -	bh = ext4_bread(NULL, inode, block, 0);
> +	if (ext4_simulate_fail(inode->i_sb, EXT4_SIM_DIRBLOCK_EIO))
> +		bh = ERR_PTR(-EIO);
> +	else
> +		bh = ext4_bread(NULL, inode, block, 0);
>   	if (IS_ERR(bh)) {
>   		__ext4_warning(inode->i_sb, func, line,
>   			       "inode #%lu: lblock %lu: comm %s: "
> @@ -153,7 +156,8 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
>   	 * caller is sure it should be an index block.
>   	 */
>   	if (is_dx_block && type == INDEX) {
> -		if (ext4_dx_csum_verify(inode, dirent))
> +		if (ext4_dx_csum_verify(inode, dirent) &&
> +		    !ext4_simulate_fail(inode->i_sb, EXT4_SIM_DIRBLOCK_CRC))
>   			set_buffer_verified(bh);
>   		else {
>   			ext4_set_errno(inode->i_sb, EFSBADCRC);
> @@ -164,7 +168,8 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
>   		}
>   	}
>   	if (!is_dx_block) {
> -		if (ext4_dirblock_csum_verify(inode, bh))
> +		if (ext4_dirblock_csum_verify(inode, bh) &&
> +		    !ext4_simulate_fail(inode->i_sb, EXT4_SIM_DIRBLOCK_CRC))
>   			set_buffer_verified(bh);
>   		else {
>   			ext4_set_errno(inode->i_sb, EFSBADCRC);
> diff --git a/fs/ext4/sysfs.c b/fs/ext4/sysfs.c
> index 9394360ff137..174322d2ce23 100644
> --- a/fs/ext4/sysfs.c
> +++ b/fs/ext4/sysfs.c
> @@ -30,6 +30,7 @@ typedef enum {
>   	attr_last_error_time,
>   	attr_feature,
>   	attr_pointer_ui,
> +	attr_pointer_ul,
>   	attr_pointer_atomic,
>   	attr_journal_task,
>   } attr_id_t;
> @@ -161,6 +162,9 @@ static struct ext4_attr ext4_attr_##_name = {			\
>   #define EXT4_RW_ATTR_SBI_UI(_name,_elname)	\
>   	EXT4_ATTR_OFFSET(_name, 0644, pointer_ui, ext4_sb_info, _elname)
>   
> +#define EXT4_RW_ATTR_SBI_UL(_name,_elname)	\
> +	EXT4_ATTR_OFFSET(_name, 0644, pointer_ul, ext4_sb_info, _elname)
> +
>   #define EXT4_ATTR_PTR(_name,_mode,_id,_ptr) \
>   static struct ext4_attr ext4_attr_##_name = {			\
>   	.attr = {.name = __stringify(_name), .mode = _mode },	\
> @@ -196,6 +200,9 @@ EXT4_RW_ATTR_SBI_UI(warning_ratelimit_interval_ms, s_warning_ratelimit_state.int
>   EXT4_RW_ATTR_SBI_UI(warning_ratelimit_burst, s_warning_ratelimit_state.burst);
>   EXT4_RW_ATTR_SBI_UI(msg_ratelimit_interval_ms, s_msg_ratelimit_state.interval);
>   EXT4_RW_ATTR_SBI_UI(msg_ratelimit_burst, s_msg_ratelimit_state.burst);
> +#ifdef CONFIG_EXT4_DEBUG
> +EXT4_RW_ATTR_SBI_UL(simulate_fail, s_simulate_fail);
> +#endif
>   EXT4_RO_ATTR_ES_UI(errors_count, s_error_count);
>   EXT4_ATTR(first_error_time, 0444, first_error_time);
>   EXT4_ATTR(last_error_time, 0444, last_error_time);
> @@ -231,6 +238,9 @@ static struct attribute *ext4_attrs[] = {
>   	ATTR_LIST(first_error_time),
>   	ATTR_LIST(last_error_time),
>   	ATTR_LIST(journal_task),
> +#ifdef CONFIG_EXT4_DEBUG
> +	ATTR_LIST(simulate_fail),
> +#endif
>   	NULL,
>   };
>   ATTRIBUTE_GROUPS(ext4);
> @@ -325,6 +335,11 @@ static ssize_t ext4_attr_show(struct kobject *kobj,
>   		else
>   			return snprintf(buf, PAGE_SIZE, "%u\n",
>   					*((unsigned int *) ptr));
> +	case attr_pointer_ul:
> +		if (!ptr)
> +			return 0;
> +		return snprintf(buf, PAGE_SIZE, "%lu\n",
> +				*((unsigned long *) ptr));
>   	case attr_pointer_atomic:
>   		if (!ptr)
>   			return 0;
> @@ -368,6 +383,14 @@ static ssize_t ext4_attr_store(struct kobject *kobj,
>   		else
>   			*((unsigned int *) ptr) = t;
>   		return len;
> +	case attr_pointer_ul:
> +		if (!ptr)
> +			return 0;
> +		ret = kstrtoul(skip_spaces(buf), 0, &t);
> +		if (ret)
> +			return ret;
> +		*((unsigned long *) ptr) = t;
> +		return len;
>   	case attr_inode_readahead:
>   		return inode_readahead_blks_store(sbi, buf, len);
>   	case attr_trigger_test_error:
> 

-- 
-----------
Tim Gardner
Canonical, Inc



More information about the kernel-team mailing list