lkml.org 
[lkml]   [2018]   [Nov]   [12]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.14 197/222] btrfs: Handle owner mismatch gracefully when walking up tree
    Date
    4.14-stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Qu Wenruo <wqu@suse.com>

    commit 65c6e82becec33731f48786e5a30f98662c86b16 upstream.

    [BUG]
    When mounting certain crafted image, btrfs will trigger kernel BUG_ON()
    when trying to recover balance:

    kernel BUG at fs/btrfs/extent-tree.c:8956!
    invalid opcode: 0000 [#1] PREEMPT SMP NOPTI
    CPU: 1 PID: 662 Comm: mount Not tainted 4.18.0-rc1-custom+ #10
    RIP: 0010:walk_up_proc+0x336/0x480 [btrfs]
    RSP: 0018:ffffb53540c9b890 EFLAGS: 00010202
    Call Trace:
    walk_up_tree+0x172/0x1f0 [btrfs]
    btrfs_drop_snapshot+0x3a4/0x830 [btrfs]
    merge_reloc_roots+0xe1/0x1d0 [btrfs]
    btrfs_recover_relocation+0x3ea/0x420 [btrfs]
    open_ctree+0x1af3/0x1dd0 [btrfs]
    btrfs_mount_root+0x66b/0x740 [btrfs]
    mount_fs+0x3b/0x16a
    vfs_kern_mount.part.9+0x54/0x140
    btrfs_mount+0x16d/0x890 [btrfs]
    mount_fs+0x3b/0x16a
    vfs_kern_mount.part.9+0x54/0x140
    do_mount+0x1fd/0xda0
    ksys_mount+0xba/0xd0
    __x64_sys_mount+0x21/0x30
    do_syscall_64+0x60/0x210
    entry_SYSCALL_64_after_hwframe+0x49/0xbe

    [CAUSE]
    Extent tree corruption. In this particular case, reloc tree root's
    owner is DATA_RELOC_TREE (should be TREE_RELOC), thus its backref is
    corrupted and we failed the owner check in walk_up_tree().

    [FIX]
    It's pretty hard to take care of every extent tree corruption, but at
    least we can remove such BUG_ON() and exit more gracefully.

    And since in this particular image, DATA_RELOC_TREE and TREE_RELOC share
    the same root (which is obviously invalid), we needs to make
    __del_reloc_root() more robust to detect such invalid sharing to avoid
    possible NULL dereference as root->node can be NULL in this case.

    Link: https://bugzilla.kernel.org/show_bug.cgi?id=200411
    Reported-by: Xu Wen <wen.xu@gatech.edu>
    CC: stable@vger.kernel.org # 4.4+
    Signed-off-by: Qu Wenruo <wqu@suse.com>
    Reviewed-by: David Sterba <dsterba@suse.com>
    Signed-off-by: David Sterba <dsterba@suse.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    fs/btrfs/extent-tree.c | 18 ++++++++++++------
    fs/btrfs/relocation.c | 2 +-
    2 files changed, 13 insertions(+), 7 deletions(-)

    --- a/fs/btrfs/extent-tree.c
    +++ b/fs/btrfs/extent-tree.c
    @@ -9028,15 +9028,14 @@ static noinline int walk_up_proc(struct
    if (eb == root->node) {
    if (wc->flags[level] & BTRFS_BLOCK_FLAG_FULL_BACKREF)
    parent = eb->start;
    - else
    - BUG_ON(root->root_key.objectid !=
    - btrfs_header_owner(eb));
    + else if (root->root_key.objectid != btrfs_header_owner(eb))
    + goto owner_mismatch;
    } else {
    if (wc->flags[level + 1] & BTRFS_BLOCK_FLAG_FULL_BACKREF)
    parent = path->nodes[level + 1]->start;
    - else
    - BUG_ON(root->root_key.objectid !=
    - btrfs_header_owner(path->nodes[level + 1]));
    + else if (root->root_key.objectid !=
    + btrfs_header_owner(path->nodes[level + 1]))
    + goto owner_mismatch;
    }

    btrfs_free_tree_block(trans, root, eb, parent, wc->refs[level] == 1);
    @@ -9044,6 +9043,11 @@ out:
    wc->refs[level] = 0;
    wc->flags[level] = 0;
    return 0;
    +
    +owner_mismatch:
    + btrfs_err_rl(fs_info, "unexpected tree owner, have %llu expect %llu",
    + btrfs_header_owner(eb), root->root_key.objectid);
    + return -EUCLEAN;
    }

    static noinline int walk_down_tree(struct btrfs_trans_handle *trans,
    @@ -9097,6 +9101,8 @@ static noinline int walk_up_tree(struct
    ret = walk_up_proc(trans, root, path, wc);
    if (ret > 0)
    return 0;
    + if (ret < 0)
    + return ret;

    if (path->locks[level]) {
    btrfs_tree_unlock_rw(path->nodes[level],
    --- a/fs/btrfs/relocation.c
    +++ b/fs/btrfs/relocation.c
    @@ -1334,7 +1334,7 @@ static void __del_reloc_root(struct btrf
    struct mapping_node *node = NULL;
    struct reloc_control *rc = fs_info->reloc_ctl;

    - if (rc) {
    + if (rc && root->node) {
    spin_lock(&rc->reloc_root_tree.lock);
    rb_node = tree_search(&rc->reloc_root_tree.rb_root,
    root->node->start);

    \
     
     \ /
      Last update: 2018-11-11 23:52    [W:4.158 / U:0.044 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site