]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
btrfs: check for NULL root after calls to btrfs_extent_root()
authorFilipe Manana <fdmanana@suse.com>
Sun, 8 Feb 2026 19:24:16 +0000 (19:24 +0000)
committerDavid Sterba <dsterba@suse.com>
Tue, 17 Mar 2026 10:22:49 +0000 (11:22 +0100)
btrfs_extent_root() can return a NULL pointer in case the root we are
looking for is not in the rb tree that tracks roots. So add checks to
every caller that is missing such check to log a message and return
an error. The same applies to callers of btrfs_block_group_root(),
since it calls btrfs_extent_root().

Reported-by: Chris Mason <clm@meta.com>
Link: https://lore.kernel.org/linux-btrfs/20260208161657.3972997-1-clm@meta.com/
Reviewed-by: Boris Burkov <boris@bur.io>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/backref.c
fs/btrfs/block-group.c
fs/btrfs/disk-io.c
fs/btrfs/extent-tree.c
fs/btrfs/free-space-tree.c
fs/btrfs/qgroup.c
fs/btrfs/relocation.c
fs/btrfs/zoned.c

index 9bb406f7dd302d2d43bbf5168af3dbd8a8e0e11c..7921a926f6762c04161320dfb6be571d42451755 100644 (file)
@@ -1393,6 +1393,13 @@ static int find_parent_nodes(struct btrfs_backref_walk_ctx *ctx,
                .indirect_missing_keys = PREFTREE_INIT
        };
 
+       if (unlikely(!root)) {
+               btrfs_err(ctx->fs_info,
+                         "missing extent root for extent at bytenr %llu",
+                         ctx->bytenr);
+               return -EUCLEAN;
+       }
+
        /* Roots ulist is not needed when using a sharedness check context. */
        if (sc)
                ASSERT(ctx->roots == NULL);
@@ -2204,6 +2211,13 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
        struct btrfs_extent_item *ei;
        struct btrfs_key key;
 
+       if (unlikely(!extent_root)) {
+               btrfs_err(fs_info,
+                         "missing extent root for extent at bytenr %llu",
+                         logical);
+               return -EUCLEAN;
+       }
+
        key.objectid = logical;
        if (btrfs_fs_incompat(fs_info, SKINNY_METADATA))
                key.type = BTRFS_METADATA_ITEM_KEY;
@@ -2851,6 +2865,13 @@ int btrfs_backref_iter_start(struct btrfs_backref_iter *iter, u64 bytenr)
        struct btrfs_key key;
        int ret;
 
+       if (unlikely(!extent_root)) {
+               btrfs_err(fs_info,
+                         "missing extent root for extent at bytenr %llu",
+                         bytenr);
+               return -EUCLEAN;
+       }
+
        key.objectid = bytenr;
        key.type = BTRFS_METADATA_ITEM_KEY;
        key.offset = (u64)-1;
@@ -2987,6 +3008,13 @@ int btrfs_backref_iter_next(struct btrfs_backref_iter *iter)
 
        /* We're at keyed items, there is no inline item, go to the next one */
        extent_root = btrfs_extent_root(iter->fs_info, iter->bytenr);
+       if (unlikely(!extent_root)) {
+               btrfs_err(iter->fs_info,
+                         "missing extent root for extent at bytenr %llu",
+                         iter->bytenr);
+               return -EUCLEAN;
+       }
+
        ret = btrfs_next_item(extent_root, iter->path);
        if (ret)
                return ret;
index 77285ade3a0e73c7da3e0d3bfff1e7e0307917c4..fa55d868ecd82dfb979e59727ded6897c0bf5333 100644 (file)
@@ -739,6 +739,12 @@ static int load_extent_tree_free(struct btrfs_caching_control *caching_ctl)
 
        last = max_t(u64, block_group->start, BTRFS_SUPER_INFO_OFFSET);
        extent_root = btrfs_extent_root(fs_info, last);
+       if (unlikely(!extent_root)) {
+               btrfs_err(fs_info,
+                         "missing extent root for block group at offset %llu",
+                         block_group->start);
+               return -EUCLEAN;
+       }
 
 #ifdef CONFIG_BTRFS_DEBUG
        /*
@@ -1061,6 +1067,11 @@ static int remove_block_group_item(struct btrfs_trans_handle *trans,
        int ret;
 
        root = btrfs_block_group_root(fs_info);
+       if (unlikely(!root)) {
+               btrfs_err(fs_info, "missing block group root");
+               return -EUCLEAN;
+       }
+
        key.objectid = block_group->start;
        key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
        key.offset = block_group->length;
@@ -1349,6 +1360,11 @@ struct btrfs_trans_handle *btrfs_start_trans_remove_block_group(
        struct btrfs_chunk_map *map;
        unsigned int num_items;
 
+       if (unlikely(!root)) {
+               btrfs_err(fs_info, "missing block group root");
+               return ERR_PTR(-EUCLEAN);
+       }
+
        map = btrfs_find_chunk_map(fs_info, chunk_offset, 1);
        ASSERT(map != NULL);
        ASSERT(map->start == chunk_offset);
@@ -2140,6 +2156,11 @@ static int find_first_block_group(struct btrfs_fs_info *fs_info,
        int ret;
        struct btrfs_key found_key;
 
+       if (unlikely(!root)) {
+               btrfs_err(fs_info, "missing block group root");
+               return -EUCLEAN;
+       }
+
        btrfs_for_each_slot(root, key, &found_key, path, ret) {
                if (found_key.objectid >= key->objectid &&
                    found_key.type == BTRFS_BLOCK_GROUP_ITEM_KEY) {
@@ -2714,6 +2735,11 @@ static int insert_block_group_item(struct btrfs_trans_handle *trans,
        size_t size;
        int ret;
 
+       if (unlikely(!root)) {
+               btrfs_err(fs_info, "missing block group root");
+               return -EUCLEAN;
+       }
+
        spin_lock(&block_group->lock);
        btrfs_set_stack_block_group_v2_used(&bgi, block_group->used);
        btrfs_set_stack_block_group_v2_chunk_objectid(&bgi, block_group->global_root_id);
@@ -3049,6 +3075,11 @@ int btrfs_inc_block_group_ro(struct btrfs_block_group *cache,
        int ret;
        bool dirty_bg_running;
 
+       if (unlikely(!root)) {
+               btrfs_err(fs_info, "missing block group root");
+               return -EUCLEAN;
+       }
+
        /*
         * This can only happen when we are doing read-only scrub on read-only
         * mount.
@@ -3193,6 +3224,11 @@ static int update_block_group_item(struct btrfs_trans_handle *trans,
        u64 used, remap_bytes;
        u32 identity_remap_count;
 
+       if (unlikely(!root)) {
+               btrfs_err(fs_info, "missing block group root");
+               return -EUCLEAN;
+       }
+
        /*
         * Block group items update can be triggered out of commit transaction
         * critical section, thus we need a consistent view of used bytes.
index 2aeb85e3ece97587899695f74d987643570f7bfb..08368247668e3c41df6be3c2a2cf7768c29a80ad 100644 (file)
@@ -1591,7 +1591,7 @@ static int find_newest_super_backup(struct btrfs_fs_info *info)
  * this will bump the backup pointer by one when it is
  * done
  */
-static void backup_super_roots(struct btrfs_fs_info *info)
+static int backup_super_roots(struct btrfs_fs_info *info)
 {
        const int next_backup = info->backup_root_index;
        struct btrfs_root_backup *root_backup;
@@ -1623,6 +1623,11 @@ static void backup_super_roots(struct btrfs_fs_info *info)
                struct btrfs_root *extent_root = btrfs_extent_root(info, 0);
                struct btrfs_root *csum_root = btrfs_csum_root(info, 0);
 
+               if (unlikely(!extent_root)) {
+                       btrfs_err(info, "missing extent root for extent at bytenr 0");
+                       return -EUCLEAN;
+               }
+
                btrfs_set_backup_extent_root(root_backup,
                                             extent_root->node->start);
                btrfs_set_backup_extent_root_gen(root_backup,
@@ -1670,6 +1675,8 @@ static void backup_super_roots(struct btrfs_fs_info *info)
        memcpy(&info->super_copy->super_roots,
               &info->super_for_commit->super_roots,
               sizeof(*root_backup) * BTRFS_NUM_BACKUP_ROOTS);
+
+       return 0;
 }
 
 /*
@@ -4051,8 +4058,11 @@ int write_all_supers(struct btrfs_fs_info *fs_info, int max_mirrors)
         * not from fsync where the tree roots in fs_info have not
         * been consistent on disk.
         */
-       if (max_mirrors == 0)
-               backup_super_roots(fs_info);
+       if (max_mirrors == 0) {
+               ret = backup_super_roots(fs_info);
+               if (ret < 0)
+                       return ret;
+       }
 
        sb = fs_info->super_for_commit;
        dev_item = &sb->dev_item;
index b0d9baf5b41288242ccee86d5538098712b087f8..7fcd83f32014a1f6f71df664c0360a608bf23424 100644 (file)
@@ -75,6 +75,12 @@ int btrfs_lookup_data_extent(struct btrfs_fs_info *fs_info, u64 start, u64 len)
        struct btrfs_key key;
        BTRFS_PATH_AUTO_FREE(path);
 
+       if (unlikely(!root)) {
+               btrfs_err(fs_info,
+                         "missing extent root for extent at bytenr %llu", start);
+               return -EUCLEAN;
+       }
+
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
@@ -131,6 +137,12 @@ search_again:
        key.offset = offset;
 
        extent_root = btrfs_extent_root(fs_info, bytenr);
+       if (unlikely(!extent_root)) {
+               btrfs_err(fs_info,
+                         "missing extent root for extent at bytenr %llu", bytenr);
+               return -EUCLEAN;
+       }
+
        ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0);
        if (ret < 0)
                return ret;
@@ -436,6 +448,12 @@ static noinline int lookup_extent_data_ref(struct btrfs_trans_handle *trans,
        int recow;
        int ret;
 
+       if (unlikely(!root)) {
+               btrfs_err(trans->fs_info,
+                         "missing extent root for extent at bytenr %llu", bytenr);
+               return -EUCLEAN;
+       }
+
        key.objectid = bytenr;
        if (parent) {
                key.type = BTRFS_SHARED_DATA_REF_KEY;
@@ -510,6 +528,12 @@ static noinline int insert_extent_data_ref(struct btrfs_trans_handle *trans,
        u32 num_refs;
        int ret;
 
+       if (unlikely(!root)) {
+               btrfs_err(trans->fs_info,
+                         "missing extent root for extent at bytenr %llu", bytenr);
+               return -EUCLEAN;
+       }
+
        key.objectid = bytenr;
        if (node->parent) {
                key.type = BTRFS_SHARED_DATA_REF_KEY;
@@ -668,6 +692,12 @@ static noinline int lookup_tree_block_ref(struct btrfs_trans_handle *trans,
        struct btrfs_key key;
        int ret;
 
+       if (unlikely(!root)) {
+               btrfs_err(trans->fs_info,
+                         "missing extent root for extent at bytenr %llu", bytenr);
+               return -EUCLEAN;
+       }
+
        key.objectid = bytenr;
        if (parent) {
                key.type = BTRFS_SHARED_BLOCK_REF_KEY;
@@ -692,6 +722,12 @@ static noinline int insert_tree_block_ref(struct btrfs_trans_handle *trans,
        struct btrfs_key key;
        int ret;
 
+       if (unlikely(!root)) {
+               btrfs_err(trans->fs_info,
+                         "missing extent root for extent at bytenr %llu", bytenr);
+               return -EUCLEAN;
+       }
+
        key.objectid = bytenr;
        if (node->parent) {
                key.type = BTRFS_SHARED_BLOCK_REF_KEY;
@@ -782,6 +818,12 @@ int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
        bool skinny_metadata = btrfs_fs_incompat(fs_info, SKINNY_METADATA);
        int needed;
 
+       if (unlikely(!root)) {
+               btrfs_err(fs_info,
+                         "missing extent root for extent at bytenr %llu", bytenr);
+               return -EUCLEAN;
+       }
+
        key.objectid = bytenr;
        key.type = BTRFS_EXTENT_ITEM_KEY;
        key.offset = num_bytes;
@@ -1680,6 +1722,12 @@ static int run_delayed_extent_op(struct btrfs_trans_handle *trans,
        }
 
        root = btrfs_extent_root(fs_info, key.objectid);
+       if (unlikely(!root)) {
+               btrfs_err(fs_info,
+                         "missing extent root for extent at bytenr %llu",
+                         key.objectid);
+               return -EUCLEAN;
+       }
 again:
        ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
        if (ret < 0) {
@@ -2379,6 +2427,12 @@ static noinline int check_committed_ref(struct btrfs_inode *inode,
        int type;
        int ret;
 
+       if (unlikely(!extent_root)) {
+               btrfs_err(fs_info,
+                         "missing extent root for extent at bytenr %llu", bytenr);
+               return -EUCLEAN;
+       }
+
        key.objectid = bytenr;
        key.type = BTRFS_EXTENT_ITEM_KEY;
        key.offset = (u64)-1;
@@ -3222,7 +3276,11 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
        u64 delayed_ref_root = href->owning_root;
 
        extent_root = btrfs_extent_root(info, bytenr);
-       ASSERT(extent_root);
+       if (unlikely(!extent_root)) {
+               btrfs_err(info,
+                         "missing extent root for extent at bytenr %llu", bytenr);
+               return -EUCLEAN;
+       }
 
        path = btrfs_alloc_path();
        if (!path)
@@ -4939,11 +4997,18 @@ static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans,
                size += btrfs_extent_inline_ref_size(BTRFS_EXTENT_OWNER_REF_KEY);
        size += btrfs_extent_inline_ref_size(type);
 
+       extent_root = btrfs_extent_root(fs_info, ins->objectid);
+       if (unlikely(!extent_root)) {
+               btrfs_err(fs_info,
+                         "missing extent root for extent at bytenr %llu",
+                         ins->objectid);
+               return -EUCLEAN;
+       }
+
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
 
-       extent_root = btrfs_extent_root(fs_info, ins->objectid);
        ret = btrfs_insert_empty_item(trans, extent_root, path, ins, size);
        if (ret) {
                btrfs_free_path(path);
@@ -5019,11 +5084,18 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
                size += sizeof(*block_info);
        }
 
+       extent_root = btrfs_extent_root(fs_info, extent_key.objectid);
+       if (unlikely(!extent_root)) {
+               btrfs_err(fs_info,
+                         "missing extent root for extent at bytenr %llu",
+                         extent_key.objectid);
+               return -EUCLEAN;
+       }
+
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
 
-       extent_root = btrfs_extent_root(fs_info, extent_key.objectid);
        ret = btrfs_insert_empty_item(trans, extent_root, path, &extent_key,
                                      size);
        if (ret) {
index ecddfca92b2b533dbce3617d5c18298ddde8d076..9efd1ec90f031fa038608ba3d232223346f49bd3 100644 (file)
@@ -1073,6 +1073,14 @@ static int populate_free_space_tree(struct btrfs_trans_handle *trans,
        if (ret)
                return ret;
 
+       extent_root = btrfs_extent_root(trans->fs_info, block_group->start);
+       if (unlikely(!extent_root)) {
+               btrfs_err(trans->fs_info,
+                         "missing extent root for block group at offset %llu",
+                         block_group->start);
+               return -EUCLEAN;
+       }
+
        mutex_lock(&block_group->free_space_lock);
 
        /*
@@ -1086,7 +1094,6 @@ static int populate_free_space_tree(struct btrfs_trans_handle *trans,
        key.type = BTRFS_EXTENT_ITEM_KEY;
        key.offset = 0;
 
-       extent_root = btrfs_extent_root(trans->fs_info, key.objectid);
        ret = btrfs_search_slot_for_read(extent_root, &key, path, 1, 0);
        if (ret < 0)
                goto out_locked;
index 19edd25ff5d1ae020e5eae4127a11621ded97af3..0fa66fe4fbb07b4df7441e53a30aa028f1b783da 100644 (file)
@@ -3740,6 +3740,14 @@ static int qgroup_rescan_leaf(struct btrfs_trans_handle *trans,
        mutex_lock(&fs_info->qgroup_rescan_lock);
        extent_root = btrfs_extent_root(fs_info,
                                fs_info->qgroup_rescan_progress.objectid);
+       if (unlikely(!extent_root)) {
+               btrfs_err(fs_info,
+                         "missing extent root for extent at bytenr %llu",
+                         fs_info->qgroup_rescan_progress.objectid);
+               mutex_unlock(&fs_info->qgroup_rescan_lock);
+               return -EUCLEAN;
+       }
+
        ret = btrfs_search_slot_for_read(extent_root,
                                         &fs_info->qgroup_rescan_progress,
                                         path, 1, 0);
index fcfbe1b1dab44e8b262680f5773faa158a311bc9..93a5ae23406df4a550eaa4799cb66cddba36e245 100644 (file)
@@ -4954,6 +4954,12 @@ static int do_remap_reloc_trans(struct btrfs_fs_info *fs_info,
        struct btrfs_space_info *sinfo = src_bg->space_info;
 
        extent_root = btrfs_extent_root(fs_info, src_bg->start);
+       if (unlikely(!extent_root)) {
+               btrfs_err(fs_info,
+                         "missing extent root for block group at offset %llu",
+                         src_bg->start);
+               return -EUCLEAN;
+       }
 
        trans = btrfs_start_transaction(extent_root, 0);
        if (IS_ERR(trans))
@@ -5306,6 +5312,13 @@ int btrfs_relocate_block_group(struct btrfs_fs_info *fs_info, u64 group_start,
        int ret;
        bool bg_is_ro = false;
 
+       if (unlikely(!extent_root)) {
+               btrfs_err(fs_info,
+                         "missing extent root for block group at offset %llu",
+                         group_start);
+               return -EUCLEAN;
+       }
+
        /*
         * This only gets set if we had a half-deleted snapshot on mount.  We
         * cannot allow relocation to start while we're still trying to clean up
@@ -5536,12 +5549,17 @@ int btrfs_recover_relocation(struct btrfs_fs_info *fs_info)
                goto out;
        }
 
+       rc->extent_root = btrfs_extent_root(fs_info, 0);
+       if (unlikely(!rc->extent_root)) {
+               btrfs_err(fs_info, "missing extent root for extent at bytenr 0");
+               ret = -EUCLEAN;
+               goto out;
+       }
+
        ret = reloc_chunk_start(fs_info);
        if (ret < 0)
                goto out_end;
 
-       rc->extent_root = btrfs_extent_root(fs_info, 0);
-
        set_reloc_control(rc);
 
        trans = btrfs_join_transaction(rc->extent_root);
index 92b5ac8fac3786ba7eab03eecc6777b38d1c0be8..afb5924018ac196c097ce6981c6bec86a9896748 100644 (file)
@@ -1261,6 +1261,13 @@ static int calculate_alloc_pointer(struct btrfs_block_group *cache,
        key.offset = 0;
 
        root = btrfs_extent_root(fs_info, key.objectid);
+       if (unlikely(!root)) {
+               btrfs_err(fs_info,
+                         "missing extent root for extent at bytenr %llu",
+                         key.objectid);
+               return -EUCLEAN;
+       }
+
        ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
        /* We should not find the exact match */
        if (unlikely(!ret))