]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
btrfs: skip global block reserve accounting for rescue mounts
authorDongjiang Zhu <zhudongjiang@fnnas.com>
Mon, 13 Jul 2026 08:50:08 +0000 (16:50 +0800)
committerDavid Sterba <dsterba@suse.com>
Tue, 21 Jul 2026 04:40:18 +0000 (06:40 +0200)
[BUG]
Mounting with rescue=ibadroots after corrupting the block group tree
root triggers a NULL pointer dereference:

  BUG: kernel NULL pointer dereference, address: 0000000000000100
  RIP: 0010:btrfs_update_global_block_rsv+0x9d/0x1c0 [btrfs]
  Call Trace:
   fill_dummy_bgs+0xd4/0x120 [btrfs]
   open_ctree+0xc6e/0x1ca0 [btrfs]
   btrfs_get_tree+0x50d/0xa40 [btrfs]

The same crash occurs with a corrupted raid stripe tree root, via
btrfs_read_block_groups() instead of fill_dummy_bgs().

[CAUSE]
With rescue=ibadroots, btrfs_read_roots() allows the mount to continue
when either root cannot be read, leaving the corresponding root pointer
NULL while its on-disk feature bit remains set.

btrfs_update_global_block_rsv() then dereferences the missing root based
on the feature bit alone.

[FIX]
Rescue mounts are fully read-only and cannot start transactions, so the
global reserve is never consumed. Under btrfs_is_full_ro(), mark the
reserve as full and return before performing the accounting.

And since we need to check if the fs is mount fully RO, export
fs_is_full_ro() as btrfs_is_full_ro(), and move it to fs.h.

Fixes: 8dbfc14fc736 ("btrfs: account block group tree when calculating global reserve size")
Fixes: 515020900d44 ("btrfs: read raid stripe tree from disk")
Suggested-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Dongjiang Zhu <zhudongjiang@fnnas.com>
[ Squash the fs_is_full_ro() export commit into this one. ]
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/block-rsv.c
fs/btrfs/disk-io.c
fs/btrfs/fs.h

index 9efb3016ef11656f4726ae53f1edccd4e06a3ab4..c68a8f4b7d19c3de7a25c8ce0a8cf58cf4745337 100644 (file)
@@ -322,10 +322,25 @@ void btrfs_block_rsv_add_bytes(struct btrfs_block_rsv *block_rsv,
 void btrfs_update_global_block_rsv(struct btrfs_fs_info *fs_info)
 {
        struct btrfs_block_rsv *block_rsv = &fs_info->global_block_rsv;
-       struct btrfs_space_info *sinfo = block_rsv->space_info;
+       struct btrfs_space_info *sinfo;
        struct btrfs_root *root, *tmp;
-       u64 num_bytes = btrfs_root_used(&fs_info->tree_root->root_item);
        unsigned int min_items = 1;
+       u64 num_bytes;
+
+       /*
+        * A full read-only mount (rescue options) cannot start transactions,
+        * so the global reserve is never consumed. Mark it as full and skip
+        * the accounting.
+        */
+       if (btrfs_is_full_ro(fs_info)) {
+               spin_lock(&block_rsv->lock);
+               block_rsv->full = true;
+               spin_unlock(&block_rsv->lock);
+               return;
+       }
+
+       sinfo = block_rsv->space_info;
+       num_bytes = btrfs_root_used(&fs_info->tree_root->root_item);
 
        /*
         * The global block rsv is based on the size of the extent tree, the
index 0a7d80da9c9405bfcacc5b32dd7c0e6770f57731..36332df9a0f1c0736c9e9dc56f9fe5a71a3e690d 100644 (file)
@@ -3288,15 +3288,6 @@ int btrfs_check_features(struct btrfs_fs_info *fs_info, bool is_rw_mount)
        return 0;
 }
 
-static bool fs_is_full_ro(const struct btrfs_fs_info *fs_info)
-{
-       if (!sb_rdonly(fs_info->sb))
-               return false;
-       if (unlikely(fs_info->mount_opt & BTRFS_MOUNT_FULL_RO_MASK))
-               return true;
-       return false;
-}
-
 /*
  * Try to wait for any metadata readahead, and invalidate all btree folios.
  *
@@ -3462,7 +3453,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
                WRITE_ONCE(fs_info->fs_error, -EUCLEAN);
 
        /* If the fs has any rescue options, no transaction is allowed. */
-       if (fs_is_full_ro(fs_info))
+       if (btrfs_is_full_ro(fs_info))
                WRITE_ONCE(fs_info->fs_error, -EROFS);
 
        /* Set up fs_info before parsing mount options */
index 5f0cfb0b5466dfa5903af09b3ac1de0ddb73831e..7ee9ec2b0efba1df02ed4b54ccb5376f25f1c72b 100644 (file)
@@ -1159,6 +1159,15 @@ void __btrfs_clear_fs_compat_ro(struct btrfs_fs_info *fs_info, u64 flag,
 #define btrfs_test_opt(fs_info, opt)   ((fs_info)->mount_opt & \
                                         BTRFS_MOUNT_##opt)
 
+static inline bool btrfs_is_full_ro(const struct btrfs_fs_info *fs_info)
+{
+       if (!sb_rdonly(fs_info->sb))
+               return false;
+       if (unlikely(fs_info->mount_opt & BTRFS_MOUNT_FULL_RO_MASK))
+               return true;
+       return false;
+}
+
 static inline bool btrfs_fs_closing(const struct btrfs_fs_info *fs_info)
 {
        return unlikely(test_bit(BTRFS_FS_CLOSING_START, &fs_info->flags));