Commit aab9458b authored by Qu Wenruo's avatar Qu Wenruo Committed by David Sterba
Browse files

btrfs: tree-checker: add inode extref checks



Like inode refs, inode extrefs have a variable length name, which means
we have to do a proper check to make sure no header nor name can exceed
the item limits.

The check itself is very similar to check_inode_ref(), just a different
structure (btrfs_inode_extref vs btrfs_inode_ref).

Reviewed-by: default avatarFilipe Manana <fdmanana@suse.com>
Signed-off-by: default avatarQu Wenruo <wqu@suse.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 0dc93e46
Loading
Loading
Loading
Loading
+37 −0
Original line number Diff line number Diff line
@@ -183,6 +183,7 @@ static bool check_prev_ino(struct extent_buffer *leaf,
	/* Only these key->types needs to be checked */
	ASSERT(key->type == BTRFS_XATTR_ITEM_KEY ||
	       key->type == BTRFS_INODE_REF_KEY ||
	       key->type == BTRFS_INODE_EXTREF_KEY ||
	       key->type == BTRFS_DIR_INDEX_KEY ||
	       key->type == BTRFS_DIR_ITEM_KEY ||
	       key->type == BTRFS_EXTENT_DATA_KEY);
@@ -1782,6 +1783,39 @@ static int check_inode_ref(struct extent_buffer *leaf,
	return 0;
}

static int check_inode_extref(struct extent_buffer *leaf,
			      struct btrfs_key *key, struct btrfs_key *prev_key,
			      int slot)
{
	unsigned long ptr = btrfs_item_ptr_offset(leaf, slot);
	unsigned long end = ptr + btrfs_item_size(leaf, slot);

	if (unlikely(!check_prev_ino(leaf, key, slot, prev_key)))
		return -EUCLEAN;

	while (ptr < end) {
		struct btrfs_inode_extref *extref = (struct btrfs_inode_extref *)ptr;
		u16 namelen;

		if (unlikely(ptr + sizeof(*extref)) > end) {
			inode_ref_err(leaf, slot,
			"inode extref overflow, ptr %lu end %lu inode_extref size %zu",
				      ptr, end, sizeof(*extref));
			return -EUCLEAN;
		}

		namelen = btrfs_inode_extref_name_len(leaf, extref);
		if (unlikely(ptr + sizeof(*extref) + namelen > end)) {
			inode_ref_err(leaf, slot,
				"inode extref overflow, ptr %lu end %lu namelen %u",
				ptr, end, namelen);
			return -EUCLEAN;
		}
		ptr += sizeof(*extref) + namelen;
	}
	return 0;
}

static int check_raid_stripe_extent(const struct extent_buffer *leaf,
				    const struct btrfs_key *key, int slot)
{
@@ -1893,6 +1927,9 @@ static enum btrfs_tree_block_status check_leaf_item(struct extent_buffer *leaf,
	case BTRFS_INODE_REF_KEY:
		ret = check_inode_ref(leaf, key, prev_key, slot);
		break;
	case BTRFS_INODE_EXTREF_KEY:
		ret = check_inode_extref(leaf, key, prev_key, slot);
		break;
	case BTRFS_BLOCK_GROUP_ITEM_KEY:
		ret = check_block_group_item(leaf, key, slot);
		break;