• Qu Wenruo's avatar
    btrfs: qgroup: Check bg while resuming relocation to avoid NULL pointer dereference · 57949d03
    Qu Wenruo authored
    [BUG]
    When mounting a fs with reloc tree and has qgroup enabled, it can cause
    NULL pointer dereference at mount time:
    
      BUG: kernel NULL pointer dereference, address: 00000000000000a8
      #PF: supervisor read access in kernel mode
      #PF: error_code(0x0000) - not-present page
      PGD 0 P4D 0
      Oops: 0000 [#1] PREEMPT SMP NOPTI
      RIP: 0010:btrfs_qgroup_add_swapped_blocks+0x186/0x300 [btrfs]
      Call Trace:
       replace_path.isra.23+0x685/0x900 [btrfs]
       merge_reloc_root+0x26e/0x5f0 [btrfs]
       merge_reloc_roots+0x10a/0x1a0 [btrfs]
       btrfs_recover_relocation+0x3cd/0x420 [btrfs]
       open_ctree+0x1bc8/0x1ed0 [btrfs]
       btrfs_mount_root+0x544/0x680 [btrfs]
       legacy_get_tree+0x34/0x60
       vfs_get_tree+0x2d/0xf0
       fc_mount+0x12/0x40
       vfs_kern_mount.part.12+0x61/0xa0
       vfs_kern_mount+0x13/0x20
       btrfs_mount+0x16f/0x860 [btrfs]
       legacy_get_tree+0x34/0x60
       vfs_get_tree+0x2d/0xf0
       do_mount+0x81f/0xac0
       ksys_mount+0xbf/0xe0
       __x64_sys_mount+0x25/0x30
       do_syscall_64+0x65/0x240
       entry_SYSCALL_64_after_hwframe+0x49/0xbe
    
    [CAUSE]
    In btrfs_recover_relocation(), we don't have enough info to determine
    which block group we're relocating, but only to merge existing reloc
    trees.
    
    Thus in btrfs_recover_relocation(), rc->block_group is NULL.
    btrfs_qgroup_add_swapped_blocks() hasn't taken this into consideration,
    and causes a NULL pointer dereference.
    
    The bug is introduced by commit 3d0174f7
    
     ("btrfs: qgroup: Only trace
    data extents in leaves if we're relocating data block group"), and
    later qgroup refactoring still keeps this optimization.
    
    [FIX]
    Thankfully in the context of btrfs_recover_relocation(), there is no
    other progress can modify tree blocks, thus those swapped tree blocks
    pair will never affect qgroup numbers, no matter whatever we set for
    block->trace_leaf.
    
    So we only need to check if @bg is NULL before accessing @bg->flags.
    
    Reported-by: default avatarJuan Erbes <jerbes@gmail.com>
    Link: https://bugzilla.opensuse.org/show_bug.cgi?id=1134806
    Fixes: 3d0174f7
    
     ("btrfs: qgroup: Only trace data extents in leaves if we're relocating data block group")
    CC: stable@vger.kernel.org # 4.20+
    Signed-off-by: default avatarQu Wenruo <wqu@suse.com>
    Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
    57949d03