Skip to content

Commit 75cb857

Browse files
adam900710kdave
authored andcommitted
btrfs: Do super block verification before writing it to disk
There are already 2 reports about strangely corrupted super blocks, where csum still matches but extra garbage gets slipped into super block. The corruption would looks like: ------ superblock: bytenr=65536, device=/dev/sdc1 --------------------------------------------------------- csum_type 41700 (INVALID) csum 0x3b252d3a [match] bytenr 65536 flags 0x1 ( WRITTEN ) magic _BHRfS_M [match] ... incompat_flags 0x5b22400000000169 ( MIXED_BACKREF | COMPRESS_LZO | BIG_METADATA | EXTENDED_IREF | SKINNY_METADATA | unknown flag: 0x5b22400000000000 ) ... ------ Or ------ superblock: bytenr=65536, device=/dev/mapper/x --------------------------------------------------------- csum_type 35355 (INVALID) csum_size 32 csum 0xf0dbeddd [match] bytenr 65536 flags 0x1 ( WRITTEN ) magic _BHRfS_M [match] ... incompat_flags 0x176d200000000169 ( MIXED_BACKREF | COMPRESS_LZO | BIG_METADATA | EXTENDED_IREF | SKINNY_METADATA | unknown flag: 0x176d200000000000 ) ------ Obviously, csum_type and incompat_flags get some garbage, but its csum still matches, which means kernel calculates the csum based on corrupted super block memory. And after manually fixing these values, the filesystem is completely healthy without any problem exposed by btrfs check. Although the cause is still unknown, at least detect it and prevent further corruption. Both reports have same symptoms, there's an overwrite on offset 192 of the superblock, by 4 bytes. The superblock structure is not allocated or freed and stays in the memory for the whole filesystem lifetime, so it's not a use-after-free kind of error on someone else's leaked page. As a vague point for the problable cause is mentioning of other system freezing related to graphic card drivers. Reported-by: Ken Swenson <[email protected]> Reported-by: Ben Parsons <[email protected]> Signed-off-by: Qu Wenruo <[email protected]> Reviewed-by: David Sterba <[email protected]> [ add brief analysis of the reports ] Signed-off-by: David Sterba <[email protected]>
1 parent 069ec95 commit 75cb857

File tree

1 file changed

+43
-0
lines changed

1 file changed

+43
-0
lines changed

fs/btrfs/disk-io.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2610,6 +2610,41 @@ static int btrfs_validate_mount_super(struct btrfs_fs_info *fs_info)
26102610
return validate_super(fs_info, fs_info->super_copy, 0);
26112611
}
26122612

2613+
/*
2614+
* Validation of super block at write time.
2615+
* Some checks like bytenr check will be skipped as their values will be
2616+
* overwritten soon.
2617+
* Extra checks like csum type and incompat flags will be done here.
2618+
*/
2619+
static int btrfs_validate_write_super(struct btrfs_fs_info *fs_info,
2620+
struct btrfs_super_block *sb)
2621+
{
2622+
int ret;
2623+
2624+
ret = validate_super(fs_info, sb, -1);
2625+
if (ret < 0)
2626+
goto out;
2627+
if (btrfs_super_csum_type(sb) != BTRFS_CSUM_TYPE_CRC32) {
2628+
ret = -EUCLEAN;
2629+
btrfs_err(fs_info, "invalid csum type, has %u want %u",
2630+
btrfs_super_csum_type(sb), BTRFS_CSUM_TYPE_CRC32);
2631+
goto out;
2632+
}
2633+
if (btrfs_super_incompat_flags(sb) & ~BTRFS_FEATURE_INCOMPAT_SUPP) {
2634+
ret = -EUCLEAN;
2635+
btrfs_err(fs_info,
2636+
"invalid incompat flags, has 0x%llx valid mask 0x%llx",
2637+
btrfs_super_incompat_flags(sb),
2638+
(unsigned long long)BTRFS_FEATURE_INCOMPAT_SUPP);
2639+
goto out;
2640+
}
2641+
out:
2642+
if (ret < 0)
2643+
btrfs_err(fs_info,
2644+
"super block corruption detected before writing it to disk");
2645+
return ret;
2646+
}
2647+
26132648
int open_ctree(struct super_block *sb,
26142649
struct btrfs_fs_devices *fs_devices,
26152650
char *options)
@@ -3770,6 +3805,14 @@ int write_all_supers(struct btrfs_fs_info *fs_info, int max_mirrors)
37703805
flags = btrfs_super_flags(sb);
37713806
btrfs_set_super_flags(sb, flags | BTRFS_HEADER_FLAG_WRITTEN);
37723807

3808+
ret = btrfs_validate_write_super(fs_info, sb);
3809+
if (ret < 0) {
3810+
mutex_unlock(&fs_info->fs_devices->device_list_mutex);
3811+
btrfs_handle_fs_error(fs_info, -EUCLEAN,
3812+
"unexpected superblock corruption detected");
3813+
return -EUCLEAN;
3814+
}
3815+
37733816
ret = write_dev_supers(dev, sb, max_mirrors);
37743817
if (ret)
37753818
total_errors++;

0 commit comments

Comments
 (0)