diff mbox series

[5/5] ext4: add more mount time checks of the superblock

Message ID 20180618032232.25481-5-tytso@mit.edu
State Accepted, archived
Headers show
Series [1/5] ext4: never move the system.data xattr out of the inode body | expand

Commit Message

Theodore Ts'o June 18, 2018, 3:22 a.m. UTC
The kernel's ext4 mount-time checks were more permissive than
e2fsprogs's libext2fs checks when opening a file system.  The
superblock is considered too insane for debugfs or e2fsck to operate
on it, the kernel has no business trying to mount it.

This will make file system fuzzing tools work harder, but the failure
cases that they find will be more useful and be easier to evaluate.

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/super.c | 37 ++++++++++++++++++++++++++-----------
 1 file changed, 26 insertions(+), 11 deletions(-)

Comments

Andreas Dilger June 18, 2018, 5:55 p.m. UTC | #1
> On Jun 17, 2018, at 9:22 PM, Theodore Ts'o <tytso@mit.edu> wrote:
> 
> The kernel's ext4 mount-time checks were more permissive than
> e2fsprogs's libext2fs checks when opening a file system.  The
> superblock is considered too insane for debugfs or e2fsck to operate
> on it, the kernel has no business trying to mount it.
> 
> This will make file system fuzzing tools work harder, but the failure
> cases that they find will be more useful and be easier to evaluate.
> 
> Signed-off-by: Theodore Ts'o <tytso@mit.edu>

I thought the last hunk would conflict with Jan's patch series, but that
is for e2fsck and this is for ext4...

Reviewed-by: Andreas Dilger <adilger@dilger.ca>

> ---
> fs/ext4/super.c | 37 ++++++++++++++++++++++++++-----------
> 1 file changed, 26 insertions(+), 11 deletions(-)
> 
> diff --git a/fs/ext4/super.c b/fs/ext4/super.c
> index 1f955c128e0d..b37b00befd65 100644
> --- a/fs/ext4/super.c
> +++ b/fs/ext4/super.c
> @@ -3793,6 +3793,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
> 			 le32_to_cpu(es->s_log_block_size));
> 		goto failed_mount;
> 	}
> +	if (le32_to_cpu(es->s_log_cluster_size) >
> +	    (EXT4_MAX_CLUSTER_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {
> +		ext4_msg(sb, KERN_ERR,
> +			 "Invalid log cluster size: %u",
> +			 le32_to_cpu(es->s_log_cluster_size));
> +		goto failed_mount;
> +	}
> 
> 	if (le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks) > (blocksize / 4)) {
> 		ext4_msg(sb, KERN_ERR,
> @@ -3939,13 +3946,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
> 				 "block size (%d)", clustersize, blocksize);
> 			goto failed_mount;
> 		}
> -		if (le32_to_cpu(es->s_log_cluster_size) >
> -		    (EXT4_MAX_CLUSTER_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {
> -			ext4_msg(sb, KERN_ERR,
> -				 "Invalid log cluster size: %u",
> -				 le32_to_cpu(es->s_log_cluster_size));
> -			goto failed_mount;
> -		}
> 		sbi->s_cluster_bits = le32_to_cpu(es->s_log_cluster_size) -
> 			le32_to_cpu(es->s_log_block_size);
> 		sbi->s_clusters_per_group =
> @@ -3966,10 +3966,10 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
> 		}
> 	} else {
> 		if (clustersize != blocksize) {
> -			ext4_warning(sb, "fragment/cluster size (%d) != "
> -				     "block size (%d)", clustersize,
> -				     blocksize);
> -			clustersize = blocksize;
> +			ext4_msg(sb, KERN_ERR,
> +				 "fragment/cluster size (%d) != "
> +				 "block size (%d)", clustersize, blocksize);
> +			goto failed_mount;
> 		}
> 		if (sbi->s_blocks_per_group > blocksize * 8) {
> 			ext4_msg(sb, KERN_ERR,
> @@ -4023,6 +4023,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
> 			 ext4_blocks_count(es));
> 		goto failed_mount;
> 	}
> +	if ((es->s_first_data_block == 0) && (es->s_log_block_size == 0) &&
> +	    (sbi->s_cluster_ratio == 1)) {
> +		ext4_msg(sb, KERN_WARNING, "bad geometry: first data "
> +			 "block is 0 with a 1k block and cluster size");
> +		goto failed_mount;
> +	}
> +
> 	blocks_count = (ext4_blocks_count(es) -
> 			le32_to_cpu(es->s_first_data_block) +
> 			EXT4_BLOCKS_PER_GROUP(sb) - 1);
> @@ -4058,6 +4065,14 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
> 		ret = -ENOMEM;
> 		goto failed_mount;
> 	}
> +	if (((u64)sbi->s_groups_count * sbi->s_inodes_per_group) !=
> +	    le32_to_cpu(es->s_inodes_count)) {
> +		ext4_msg(sb, KERN_ERR, "inodes count not valid: %u vs %llu",
> +			 le32_to_cpu(es->s_inodes_count),
> +			 ((u64)sbi->s_groups_count * sbi->s_inodes_per_group));
> +		ret = -EINVAL;
> +		goto failed_mount;
> +	}

Cheers, Andreas
diff mbox series

Patch

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 1f955c128e0d..b37b00befd65 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -3793,6 +3793,13 @@  static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 			 le32_to_cpu(es->s_log_block_size));
 		goto failed_mount;
 	}
+	if (le32_to_cpu(es->s_log_cluster_size) >
+	    (EXT4_MAX_CLUSTER_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {
+		ext4_msg(sb, KERN_ERR,
+			 "Invalid log cluster size: %u",
+			 le32_to_cpu(es->s_log_cluster_size));
+		goto failed_mount;
+	}
 
 	if (le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks) > (blocksize / 4)) {
 		ext4_msg(sb, KERN_ERR,
@@ -3939,13 +3946,6 @@  static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 				 "block size (%d)", clustersize, blocksize);
 			goto failed_mount;
 		}
-		if (le32_to_cpu(es->s_log_cluster_size) >
-		    (EXT4_MAX_CLUSTER_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {
-			ext4_msg(sb, KERN_ERR,
-				 "Invalid log cluster size: %u",
-				 le32_to_cpu(es->s_log_cluster_size));
-			goto failed_mount;
-		}
 		sbi->s_cluster_bits = le32_to_cpu(es->s_log_cluster_size) -
 			le32_to_cpu(es->s_log_block_size);
 		sbi->s_clusters_per_group =
@@ -3966,10 +3966,10 @@  static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 		}
 	} else {
 		if (clustersize != blocksize) {
-			ext4_warning(sb, "fragment/cluster size (%d) != "
-				     "block size (%d)", clustersize,
-				     blocksize);
-			clustersize = blocksize;
+			ext4_msg(sb, KERN_ERR,
+				 "fragment/cluster size (%d) != "
+				 "block size (%d)", clustersize, blocksize);
+			goto failed_mount;
 		}
 		if (sbi->s_blocks_per_group > blocksize * 8) {
 			ext4_msg(sb, KERN_ERR,
@@ -4023,6 +4023,13 @@  static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 			 ext4_blocks_count(es));
 		goto failed_mount;
 	}
+	if ((es->s_first_data_block == 0) && (es->s_log_block_size == 0) &&
+	    (sbi->s_cluster_ratio == 1)) {
+		ext4_msg(sb, KERN_WARNING, "bad geometry: first data "
+			 "block is 0 with a 1k block and cluster size");
+		goto failed_mount;
+	}
+
 	blocks_count = (ext4_blocks_count(es) -
 			le32_to_cpu(es->s_first_data_block) +
 			EXT4_BLOCKS_PER_GROUP(sb) - 1);
@@ -4058,6 +4065,14 @@  static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 		ret = -ENOMEM;
 		goto failed_mount;
 	}
+	if (((u64)sbi->s_groups_count * sbi->s_inodes_per_group) !=
+	    le32_to_cpu(es->s_inodes_count)) {
+		ext4_msg(sb, KERN_ERR, "inodes count not valid: %u vs %llu",
+			 le32_to_cpu(es->s_inodes_count),
+			 ((u64)sbi->s_groups_count * sbi->s_inodes_per_group));
+		ret = -EINVAL;
+		goto failed_mount;
+	}
 
 	bgl_lock_init(sbi->s_blockgroup_lock);