diff options
Diffstat (limited to 'fs/btrfs/tree-checker.c')
| -rw-r--r-- | fs/btrfs/tree-checker.c | 123 | 
1 files changed, 120 insertions, 3 deletions
| diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index 6388786fd8b5..634d69964fe4 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -569,9 +569,10 @@ static int check_dir_item(struct extent_buffer *leaf,  		/* dir type check */  		dir_type = btrfs_dir_ftype(leaf, di); -		if (unlikely(dir_type >= BTRFS_FT_MAX)) { +		if (unlikely(dir_type <= BTRFS_FT_UNKNOWN || +			     dir_type >= BTRFS_FT_MAX)) {  			dir_item_err(leaf, slot, -			"invalid dir item type, have %u expect [0, %u)", +			"invalid dir item type, have %u expect (0, %u)",  				dir_type, BTRFS_FT_MAX);  			return -EUCLEAN;  		} @@ -634,7 +635,7 @@ static int check_dir_item(struct extent_buffer *leaf,  		 */  		if (key->type == BTRFS_DIR_ITEM_KEY ||  		    key->type == BTRFS_XATTR_ITEM_KEY) { -			char namebuf[max(BTRFS_NAME_LEN, XATTR_NAME_MAX)]; +			char namebuf[MAX(BTRFS_NAME_LEN, XATTR_NAME_MAX)];  			read_extent_buffer(leaf, namebuf,  					(unsigned long)(di + 1), name_len); @@ -1289,6 +1290,19 @@ static void extent_err(const struct extent_buffer *eb, int slot,  	va_end(args);  } +static bool is_valid_dref_root(u64 rootid) +{ +	/* +	 * The following tree root objectids are allowed to have a data backref: +	 * - subvolume trees +	 * - data reloc tree +	 * - tree root +	 *   For v1 space cache +	 */ +	return is_fstree(rootid) || rootid == BTRFS_DATA_RELOC_TREE_OBJECTID || +	       rootid == BTRFS_ROOT_TREE_OBJECTID; +} +  static int check_extent_item(struct extent_buffer *leaf,  			     struct btrfs_key *key, int slot,  			     struct btrfs_key *prev_key) @@ -1441,6 +1455,8 @@ static int check_extent_item(struct extent_buffer *leaf,  		struct btrfs_extent_data_ref *dref;  		struct btrfs_shared_data_ref *sref;  		u64 seq; +		u64 dref_root; +		u64 dref_objectid;  		u64 dref_offset;  		u64 inline_offset;  		u8 inline_type; @@ -1484,11 +1500,26 @@ static int check_extent_item(struct extent_buffer *leaf,  		 */  		case BTRFS_EXTENT_DATA_REF_KEY:  			dref = (struct btrfs_extent_data_ref *)(&iref->offset); +			dref_root = btrfs_extent_data_ref_root(leaf, dref); +			dref_objectid = btrfs_extent_data_ref_objectid(leaf, dref);  			dref_offset = btrfs_extent_data_ref_offset(leaf, dref);  			seq = hash_extent_data_ref(  					btrfs_extent_data_ref_root(leaf, dref),  					btrfs_extent_data_ref_objectid(leaf, dref),  					btrfs_extent_data_ref_offset(leaf, dref)); +			if (unlikely(!is_valid_dref_root(dref_root))) { +				extent_err(leaf, slot, +					   "invalid data ref root value %llu", +					   dref_root); +				return -EUCLEAN; +			} +			if (unlikely(dref_objectid < BTRFS_FIRST_FREE_OBJECTID || +				     dref_objectid > BTRFS_LAST_FREE_OBJECTID)) { +				extent_err(leaf, slot, +					   "invalid data ref objectid value %llu", +					   dref_root); +				return -EUCLEAN; +			}  			if (unlikely(!IS_ALIGNED(dref_offset,  						 fs_info->sectorsize))) {  				extent_err(leaf, slot, @@ -1627,6 +1658,8 @@ static int check_extent_data_ref(struct extent_buffer *leaf,  		return -EUCLEAN;  	}  	for (; ptr < end; ptr += sizeof(*dref)) { +		u64 root; +		u64 objectid;  		u64 offset;  		/* @@ -1634,7 +1667,22 @@ static int check_extent_data_ref(struct extent_buffer *leaf,  		 * overflow from the leaf due to hash collisions.  		 */  		dref = (struct btrfs_extent_data_ref *)ptr; +		root = btrfs_extent_data_ref_root(leaf, dref); +		objectid = btrfs_extent_data_ref_objectid(leaf, dref);  		offset = btrfs_extent_data_ref_offset(leaf, dref); +		if (unlikely(!is_valid_dref_root(root))) { +			extent_err(leaf, slot, +				   "invalid extent data backref root value %llu", +				   root); +			return -EUCLEAN; +		} +		if (unlikely(objectid < BTRFS_FIRST_FREE_OBJECTID || +			     objectid > BTRFS_LAST_FREE_OBJECTID)) { +			extent_err(leaf, slot, +				   "invalid extent data backref objectid value %llu", +				   root); +			return -EUCLEAN; +		}  		if (unlikely(!IS_ALIGNED(offset, leaf->fs_info->sectorsize))) {  			extent_err(leaf, slot,  	"invalid extent data backref offset, have %llu expect aligned to %u", @@ -1716,6 +1764,72 @@ static int check_raid_stripe_extent(const struct extent_buffer *leaf,  	return 0;  } +static int check_dev_extent_item(const struct extent_buffer *leaf, +				 const struct btrfs_key *key, +				 int slot, +				 struct btrfs_key *prev_key) +{ +	struct btrfs_dev_extent *de; +	const u32 sectorsize = leaf->fs_info->sectorsize; + +	de = btrfs_item_ptr(leaf, slot, struct btrfs_dev_extent); +	/* Basic fixed member checks. */ +	if (unlikely(btrfs_dev_extent_chunk_tree(leaf, de) != +		     BTRFS_CHUNK_TREE_OBJECTID)) { +		generic_err(leaf, slot, +			    "invalid dev extent chunk tree id, has %llu expect %llu", +			    btrfs_dev_extent_chunk_tree(leaf, de), +			    BTRFS_CHUNK_TREE_OBJECTID); +		return -EUCLEAN; +	} +	if (unlikely(btrfs_dev_extent_chunk_objectid(leaf, de) != +		     BTRFS_FIRST_CHUNK_TREE_OBJECTID)) { +		generic_err(leaf, slot, +			    "invalid dev extent chunk objectid, has %llu expect %llu", +			    btrfs_dev_extent_chunk_objectid(leaf, de), +			    BTRFS_FIRST_CHUNK_TREE_OBJECTID); +		return -EUCLEAN; +	} +	/* Alignment check. */ +	if (unlikely(!IS_ALIGNED(key->offset, sectorsize))) { +		generic_err(leaf, slot, +			    "invalid dev extent key.offset, has %llu not aligned to %u", +			    key->offset, sectorsize); +		return -EUCLEAN; +	} +	if (unlikely(!IS_ALIGNED(btrfs_dev_extent_chunk_offset(leaf, de), +				 sectorsize))) { +		generic_err(leaf, slot, +			    "invalid dev extent chunk offset, has %llu not aligned to %u", +			    btrfs_dev_extent_chunk_objectid(leaf, de), +			    sectorsize); +		return -EUCLEAN; +	} +	if (unlikely(!IS_ALIGNED(btrfs_dev_extent_length(leaf, de), +				 sectorsize))) { +		generic_err(leaf, slot, +			    "invalid dev extent length, has %llu not aligned to %u", +			    btrfs_dev_extent_length(leaf, de), sectorsize); +		return -EUCLEAN; +	} +	/* Overlap check with previous dev extent. */ +	if (slot && prev_key->objectid == key->objectid && +	    prev_key->type == key->type) { +		struct btrfs_dev_extent *prev_de; +		u64 prev_len; + +		prev_de = btrfs_item_ptr(leaf, slot - 1, struct btrfs_dev_extent); +		prev_len = btrfs_dev_extent_length(leaf, prev_de); +		if (unlikely(prev_key->offset + prev_len > key->offset)) { +			generic_err(leaf, slot, +		"dev extent overlap, prev offset %llu len %llu current offset %llu", +				    prev_key->objectid, prev_len, key->offset); +			return -EUCLEAN; +		} +	} +	return 0; +} +  /*   * Common point to switch the item-specific validation.   */ @@ -1752,6 +1866,9 @@ static enum btrfs_tree_block_status check_leaf_item(struct extent_buffer *leaf,  	case BTRFS_DEV_ITEM_KEY:  		ret = check_dev_item(leaf, key, slot);  		break; +	case BTRFS_DEV_EXTENT_KEY: +		ret = check_dev_extent_item(leaf, key, slot, prev_key); +		break;  	case BTRFS_INODE_ITEM_KEY:  		ret = check_inode_item(leaf, key, slot);  		break; |