Skip to content

Commit f333a3c

Browse files
adam900710kdave
authored andcommitted
btrfs: tree-checker: validate dref root and objectid
[CORRUPTION] There is a bug report that btrfs flips RO due to a corruption in the extent tree, the involved dumps looks like this: item 188 key (402811572224 168 4096) itemoff 14598 itemsize 79 extent refs 3 gen 3678544 flags 1 ref#0: extent data backref root 13835058055282163977 objectid 281473384125923 offset 81432576 count 1 ref#1: shared data backref parent 1947073626112 count 1 ref#2: shared data backref parent 1156030103552 count 1 BTRFS critical (device vdc1: state EA): unable to find ref byte nr 402811572224 parent 0 root 265 owner 28703026 offset 81432576 slot 189 BTRFS error (device vdc1: state EA): failed to run delayed ref for logical 402811572224 num_bytes 4096 type 178 action 2 ref_mod 1: -2 [CAUSE] The corrupted entry is ref#0 of item 188. The root number 13835058055282163977 is beyond the upper limit for root items (the current limit is 1 << 48), and the objectid also looks suspicious. Only the offset and count is correct. [ENHANCEMENT] Although it's still unknown why we have such many bytes corrupted randomly, we can still enhance the tree-checker for data backrefs by: - Validate the root value For now there should only be 3 types of roots can have data backref: * subvolume trees * data reloc trees * root tree Only for v1 space cache - validate the objectid value The objectid should be a valid inode number. Hopefully we can catch such problem in the future with the new checkers. Reported-by: Kai Krakow <[email protected]> Link: https://lore.kernel.org/linux-btrfs/CAMthOuPjg5RDT-G_LXeBBUUtzt3cq=JywF+D1_h+JYxe=WKp-Q@mail.gmail.com/#t Reviewed-by: Filipe Manana <[email protected]> Signed-off-by: Qu Wenruo <[email protected]> Reviewed-by: David Sterba <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent c3ece6b commit f333a3c

File tree

1 file changed

+47
-0
lines changed

1 file changed

+47
-0
lines changed

fs/btrfs/tree-checker.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,6 +1289,19 @@ static void extent_err(const struct extent_buffer *eb, int slot,
12891289
va_end(args);
12901290
}
12911291

1292+
static bool is_valid_dref_root(u64 rootid)
1293+
{
1294+
/*
1295+
* The following tree root objectids are allowed to have a data backref:
1296+
* - subvolume trees
1297+
* - data reloc tree
1298+
* - tree root
1299+
* For v1 space cache
1300+
*/
1301+
return is_fstree(rootid) || rootid == BTRFS_DATA_RELOC_TREE_OBJECTID ||
1302+
rootid == BTRFS_ROOT_TREE_OBJECTID;
1303+
}
1304+
12921305
static int check_extent_item(struct extent_buffer *leaf,
12931306
struct btrfs_key *key, int slot,
12941307
struct btrfs_key *prev_key)
@@ -1441,6 +1454,8 @@ static int check_extent_item(struct extent_buffer *leaf,
14411454
struct btrfs_extent_data_ref *dref;
14421455
struct btrfs_shared_data_ref *sref;
14431456
u64 seq;
1457+
u64 dref_root;
1458+
u64 dref_objectid;
14441459
u64 dref_offset;
14451460
u64 inline_offset;
14461461
u8 inline_type;
@@ -1484,11 +1499,26 @@ static int check_extent_item(struct extent_buffer *leaf,
14841499
*/
14851500
case BTRFS_EXTENT_DATA_REF_KEY:
14861501
dref = (struct btrfs_extent_data_ref *)(&iref->offset);
1502+
dref_root = btrfs_extent_data_ref_root(leaf, dref);
1503+
dref_objectid = btrfs_extent_data_ref_objectid(leaf, dref);
14871504
dref_offset = btrfs_extent_data_ref_offset(leaf, dref);
14881505
seq = hash_extent_data_ref(
14891506
btrfs_extent_data_ref_root(leaf, dref),
14901507
btrfs_extent_data_ref_objectid(leaf, dref),
14911508
btrfs_extent_data_ref_offset(leaf, dref));
1509+
if (unlikely(!is_valid_dref_root(dref_root))) {
1510+
extent_err(leaf, slot,
1511+
"invalid data ref root value %llu",
1512+
dref_root);
1513+
return -EUCLEAN;
1514+
}
1515+
if (unlikely(dref_objectid < BTRFS_FIRST_FREE_OBJECTID ||
1516+
dref_objectid > BTRFS_LAST_FREE_OBJECTID)) {
1517+
extent_err(leaf, slot,
1518+
"invalid data ref objectid value %llu",
1519+
dref_root);
1520+
return -EUCLEAN;
1521+
}
14921522
if (unlikely(!IS_ALIGNED(dref_offset,
14931523
fs_info->sectorsize))) {
14941524
extent_err(leaf, slot,
@@ -1627,14 +1657,31 @@ static int check_extent_data_ref(struct extent_buffer *leaf,
16271657
return -EUCLEAN;
16281658
}
16291659
for (; ptr < end; ptr += sizeof(*dref)) {
1660+
u64 root;
1661+
u64 objectid;
16301662
u64 offset;
16311663

16321664
/*
16331665
* We cannot check the extent_data_ref hash due to possible
16341666
* overflow from the leaf due to hash collisions.
16351667
*/
16361668
dref = (struct btrfs_extent_data_ref *)ptr;
1669+
root = btrfs_extent_data_ref_root(leaf, dref);
1670+
objectid = btrfs_extent_data_ref_objectid(leaf, dref);
16371671
offset = btrfs_extent_data_ref_offset(leaf, dref);
1672+
if (unlikely(!is_valid_dref_root(root))) {
1673+
extent_err(leaf, slot,
1674+
"invalid extent data backref root value %llu",
1675+
root);
1676+
return -EUCLEAN;
1677+
}
1678+
if (unlikely(objectid < BTRFS_FIRST_FREE_OBJECTID ||
1679+
objectid > BTRFS_LAST_FREE_OBJECTID)) {
1680+
extent_err(leaf, slot,
1681+
"invalid extent data backref objectid value %llu",
1682+
root);
1683+
return -EUCLEAN;
1684+
}
16381685
if (unlikely(!IS_ALIGNED(offset, leaf->fs_info->sectorsize))) {
16391686
extent_err(leaf, slot,
16401687
"invalid extent data backref offset, have %llu expect aligned to %u",

0 commit comments

Comments
 (0)