]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
btrfs: tree-checker: add inode extref checks
authorQu Wenruo <wqu@suse.com>
Mon, 15 Sep 2025 23:04:05 +0000 (08:34 +0930)
committerDavid Sterba <dsterba@suse.com>
Tue, 23 Sep 2025 06:49:21 +0000 (08:49 +0200)
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: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/tree-checker.c

index c2aac08055fb840d41a27dc1296a1c78b03d4c78..ca30b15ea452343799f5421be0e68be7637da235 100644 (file)
@@ -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;