Skip to content

Commit f82d1c7

Browse files
adam900710kdave
authored andcommitted
btrfs: tree-checker: Add EXTENT_ITEM and METADATA_ITEM check
This patch introduces the ability to check extent items. This check involves: - key->objectid check Basic alignment check. - key->type check Against btrfs_extent_item::type and SKINNY_METADATA feature. - key->offset alignment check for EXTENT_ITEM - key->offset check for METADATA_ITEM - item size check Both against minimal size and stepping check. - btrfs_extent_item check Checks its flags and generation. - btrfs_extent_inline_ref checks Against 4 types inline ref. Checks bytenr alignment and tree level. - btrfs_extent_item::refs check Check against total refs found in inline refs. This check would be the most complex single item check due to its nature of inlined items. Signed-off-by: Qu Wenruo <[email protected]> Reviewed-by: David Sterba <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent f113698 commit f82d1c7

File tree

1 file changed

+248
-0
lines changed

1 file changed

+248
-0
lines changed

fs/btrfs/tree-checker.c

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,250 @@ static int check_root_item(struct extent_buffer *leaf, struct btrfs_key *key,
910910
return 0;
911911
}
912912

913+
__printf(3,4)
914+
__cold
915+
static void extent_err(const struct extent_buffer *eb, int slot,
916+
const char *fmt, ...)
917+
{
918+
struct btrfs_key key;
919+
struct va_format vaf;
920+
va_list args;
921+
u64 bytenr;
922+
u64 len;
923+
924+
btrfs_item_key_to_cpu(eb, &key, slot);
925+
bytenr = key.objectid;
926+
if (key.type == BTRFS_METADATA_ITEM_KEY)
927+
len = eb->fs_info->nodesize;
928+
else
929+
len = key.offset;
930+
va_start(args, fmt);
931+
932+
vaf.fmt = fmt;
933+
vaf.va = &args;
934+
935+
btrfs_crit(eb->fs_info,
936+
"corrupt %s: block=%llu slot=%d extent bytenr=%llu len=%llu %pV",
937+
btrfs_header_level(eb) == 0 ? "leaf" : "node",
938+
eb->start, slot, bytenr, len, &vaf);
939+
va_end(args);
940+
}
941+
942+
static int check_extent_item(struct extent_buffer *leaf,
943+
struct btrfs_key *key, int slot)
944+
{
945+
struct btrfs_fs_info *fs_info = leaf->fs_info;
946+
struct btrfs_extent_item *ei;
947+
bool is_tree_block = false;
948+
unsigned long ptr; /* Current pointer inside inline refs */
949+
unsigned long end; /* Extent item end */
950+
const u32 item_size = btrfs_item_size_nr(leaf, slot);
951+
u64 flags;
952+
u64 generation;
953+
u64 total_refs; /* Total refs in btrfs_extent_item */
954+
u64 inline_refs = 0; /* found total inline refs */
955+
956+
if (key->type == BTRFS_METADATA_ITEM_KEY &&
957+
!btrfs_fs_incompat(fs_info, SKINNY_METADATA)) {
958+
generic_err(leaf, slot,
959+
"invalid key type, METADATA_ITEM type invalid when SKINNY_METADATA feature disabled");
960+
return -EUCLEAN;
961+
}
962+
/* key->objectid is the bytenr for both key types */
963+
if (!IS_ALIGNED(key->objectid, fs_info->sectorsize)) {
964+
generic_err(leaf, slot,
965+
"invalid key objectid, have %llu expect to be aligned to %u",
966+
key->objectid, fs_info->sectorsize);
967+
return -EUCLEAN;
968+
}
969+
970+
/* key->offset is tree level for METADATA_ITEM_KEY */
971+
if (key->type == BTRFS_METADATA_ITEM_KEY &&
972+
key->offset >= BTRFS_MAX_LEVEL) {
973+
extent_err(leaf, slot,
974+
"invalid tree level, have %llu expect [0, %u]",
975+
key->offset, BTRFS_MAX_LEVEL - 1);
976+
return -EUCLEAN;
977+
}
978+
979+
/*
980+
* EXTENT/METADATA_ITEM consists of:
981+
* 1) One btrfs_extent_item
982+
* Records the total refs, type and generation of the extent.
983+
*
984+
* 2) One btrfs_tree_block_info (for EXTENT_ITEM and tree backref only)
985+
* Records the first key and level of the tree block.
986+
*
987+
* 2) Zero or more btrfs_extent_inline_ref(s)
988+
* Each inline ref has one btrfs_extent_inline_ref shows:
989+
* 2.1) The ref type, one of the 4
990+
* TREE_BLOCK_REF Tree block only
991+
* SHARED_BLOCK_REF Tree block only
992+
* EXTENT_DATA_REF Data only
993+
* SHARED_DATA_REF Data only
994+
* 2.2) Ref type specific data
995+
* Either using btrfs_extent_inline_ref::offset, or specific
996+
* data structure.
997+
*/
998+
if (item_size < sizeof(*ei)) {
999+
extent_err(leaf, slot,
1000+
"invalid item size, have %u expect [%zu, %u)",
1001+
item_size, sizeof(*ei),
1002+
BTRFS_LEAF_DATA_SIZE(fs_info));
1003+
return -EUCLEAN;
1004+
}
1005+
end = item_size + btrfs_item_ptr_offset(leaf, slot);
1006+
1007+
/* Checks against extent_item */
1008+
ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item);
1009+
flags = btrfs_extent_flags(leaf, ei);
1010+
total_refs = btrfs_extent_refs(leaf, ei);
1011+
generation = btrfs_extent_generation(leaf, ei);
1012+
if (generation > btrfs_super_generation(fs_info->super_copy) + 1) {
1013+
extent_err(leaf, slot,
1014+
"invalid generation, have %llu expect (0, %llu]",
1015+
generation,
1016+
btrfs_super_generation(fs_info->super_copy) + 1);
1017+
return -EUCLEAN;
1018+
}
1019+
if (!is_power_of_2(flags & (BTRFS_EXTENT_FLAG_DATA |
1020+
BTRFS_EXTENT_FLAG_TREE_BLOCK))) {
1021+
extent_err(leaf, slot,
1022+
"invalid extent flag, have 0x%llx expect 1 bit set in 0x%llx",
1023+
flags, BTRFS_EXTENT_FLAG_DATA |
1024+
BTRFS_EXTENT_FLAG_TREE_BLOCK);
1025+
return -EUCLEAN;
1026+
}
1027+
is_tree_block = !!(flags & BTRFS_EXTENT_FLAG_TREE_BLOCK);
1028+
if (is_tree_block) {
1029+
if (key->type == BTRFS_EXTENT_ITEM_KEY &&
1030+
key->offset != fs_info->nodesize) {
1031+
extent_err(leaf, slot,
1032+
"invalid extent length, have %llu expect %u",
1033+
key->offset, fs_info->nodesize);
1034+
return -EUCLEAN;
1035+
}
1036+
} else {
1037+
if (key->type != BTRFS_EXTENT_ITEM_KEY) {
1038+
extent_err(leaf, slot,
1039+
"invalid key type, have %u expect %u for data backref",
1040+
key->type, BTRFS_EXTENT_ITEM_KEY);
1041+
return -EUCLEAN;
1042+
}
1043+
if (!IS_ALIGNED(key->offset, fs_info->sectorsize)) {
1044+
extent_err(leaf, slot,
1045+
"invalid extent length, have %llu expect aligned to %u",
1046+
key->offset, fs_info->sectorsize);
1047+
return -EUCLEAN;
1048+
}
1049+
}
1050+
ptr = (unsigned long)(struct btrfs_extent_item *)(ei + 1);
1051+
1052+
/* Check the special case of btrfs_tree_block_info */
1053+
if (is_tree_block && key->type != BTRFS_METADATA_ITEM_KEY) {
1054+
struct btrfs_tree_block_info *info;
1055+
1056+
info = (struct btrfs_tree_block_info *)ptr;
1057+
if (btrfs_tree_block_level(leaf, info) >= BTRFS_MAX_LEVEL) {
1058+
extent_err(leaf, slot,
1059+
"invalid tree block info level, have %u expect [0, %u]",
1060+
btrfs_tree_block_level(leaf, info),
1061+
BTRFS_MAX_LEVEL - 1);
1062+
return -EUCLEAN;
1063+
}
1064+
ptr = (unsigned long)(struct btrfs_tree_block_info *)(info + 1);
1065+
}
1066+
1067+
/* Check inline refs */
1068+
while (ptr < end) {
1069+
struct btrfs_extent_inline_ref *iref;
1070+
struct btrfs_extent_data_ref *dref;
1071+
struct btrfs_shared_data_ref *sref;
1072+
u64 dref_offset;
1073+
u64 inline_offset;
1074+
u8 inline_type;
1075+
1076+
if (ptr + sizeof(*iref) > end) {
1077+
extent_err(leaf, slot,
1078+
"inline ref item overflows extent item, ptr %lu iref size %zu end %lu",
1079+
ptr, sizeof(*iref), end);
1080+
return -EUCLEAN;
1081+
}
1082+
iref = (struct btrfs_extent_inline_ref *)ptr;
1083+
inline_type = btrfs_extent_inline_ref_type(leaf, iref);
1084+
inline_offset = btrfs_extent_inline_ref_offset(leaf, iref);
1085+
if (ptr + btrfs_extent_inline_ref_size(inline_type) > end) {
1086+
extent_err(leaf, slot,
1087+
"inline ref item overflows extent item, ptr %lu iref size %u end %lu",
1088+
ptr, inline_type, end);
1089+
return -EUCLEAN;
1090+
}
1091+
1092+
switch (inline_type) {
1093+
/* inline_offset is subvolid of the owner, no need to check */
1094+
case BTRFS_TREE_BLOCK_REF_KEY:
1095+
inline_refs++;
1096+
break;
1097+
/* Contains parent bytenr */
1098+
case BTRFS_SHARED_BLOCK_REF_KEY:
1099+
if (!IS_ALIGNED(inline_offset, fs_info->sectorsize)) {
1100+
extent_err(leaf, slot,
1101+
"invalid tree parent bytenr, have %llu expect aligned to %u",
1102+
inline_offset, fs_info->sectorsize);
1103+
return -EUCLEAN;
1104+
}
1105+
inline_refs++;
1106+
break;
1107+
/*
1108+
* Contains owner subvolid, owner key objectid, adjusted offset.
1109+
* The only obvious corruption can happen in that offset.
1110+
*/
1111+
case BTRFS_EXTENT_DATA_REF_KEY:
1112+
dref = (struct btrfs_extent_data_ref *)(&iref->offset);
1113+
dref_offset = btrfs_extent_data_ref_offset(leaf, dref);
1114+
if (!IS_ALIGNED(dref_offset, fs_info->sectorsize)) {
1115+
extent_err(leaf, slot,
1116+
"invalid data ref offset, have %llu expect aligned to %u",
1117+
dref_offset, fs_info->sectorsize);
1118+
return -EUCLEAN;
1119+
}
1120+
inline_refs += btrfs_extent_data_ref_count(leaf, dref);
1121+
break;
1122+
/* Contains parent bytenr and ref count */
1123+
case BTRFS_SHARED_DATA_REF_KEY:
1124+
sref = (struct btrfs_shared_data_ref *)(iref + 1);
1125+
if (!IS_ALIGNED(inline_offset, fs_info->sectorsize)) {
1126+
extent_err(leaf, slot,
1127+
"invalid data parent bytenr, have %llu expect aligned to %u",
1128+
inline_offset, fs_info->sectorsize);
1129+
return -EUCLEAN;
1130+
}
1131+
inline_refs += btrfs_shared_data_ref_count(leaf, sref);
1132+
break;
1133+
default:
1134+
extent_err(leaf, slot, "unknown inline ref type: %u",
1135+
inline_type);
1136+
return -EUCLEAN;
1137+
}
1138+
ptr += btrfs_extent_inline_ref_size(inline_type);
1139+
}
1140+
/* No padding is allowed */
1141+
if (ptr != end) {
1142+
extent_err(leaf, slot,
1143+
"invalid extent item size, padding bytes found");
1144+
return -EUCLEAN;
1145+
}
1146+
1147+
/* Finally, check the inline refs against total refs */
1148+
if (inline_refs > total_refs) {
1149+
extent_err(leaf, slot,
1150+
"invalid extent refs, have %llu expect >= inline %llu",
1151+
total_refs, inline_refs);
1152+
return -EUCLEAN;
1153+
}
1154+
return 0;
1155+
}
1156+
9131157
/*
9141158
* Common point to switch the item-specific validation.
9151159
*/
@@ -948,6 +1192,10 @@ static int check_leaf_item(struct extent_buffer *leaf,
9481192
case BTRFS_ROOT_ITEM_KEY:
9491193
ret = check_root_item(leaf, key, slot);
9501194
break;
1195+
case BTRFS_EXTENT_ITEM_KEY:
1196+
case BTRFS_METADATA_ITEM_KEY:
1197+
ret = check_extent_item(leaf, key, slot);
1198+
break;
9511199
}
9521200
return ret;
9531201
}

0 commit comments

Comments
 (0)