lkml.org 
[lkml]   [2018]   [Feb]   [21]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.15 139/163] Btrfs: fix use-after-free on root->orphan_block_rsv
    Date
    4.15-stable review patch.  If anyone has any objections, please let me know.

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

    From: Liu Bo <bo.li.liu@oracle.com>

    commit 1a932ef4e47984dee227834667b5ff5a334e4805 upstream.

    I got these from running generic/475,

    WARNING: CPU: 0 PID: 26384 at fs/btrfs/inode.c:3326 btrfs_orphan_commit_root+0x1ac/0x2b0 [btrfs]
    BUG: unable to handle kernel NULL pointer dereference at 0000000000000010
    IP: btrfs_block_rsv_release+0x1c/0x70 [btrfs]
    Call Trace:
    btrfs_orphan_release_metadata+0x9f/0x200 [btrfs]
    btrfs_orphan_del+0x10d/0x170 [btrfs]
    btrfs_setattr+0x500/0x640 [btrfs]
    notify_change+0x7ae/0x870
    do_truncate+0xca/0x130
    vfs_truncate+0x2ee/0x3d0
    do_sys_truncate+0xaf/0xf0
    SyS_truncate+0xe/0x10
    entry_SYSCALL_64_fastpath+0x1f/0x96

    The race is between btrfs_orphan_commit_root and btrfs_orphan_del,
    t1 t2
    btrfs_orphan_commit_root btrfs_orphan_del
    spin_lock
    check (&root->orphan_inodes)
    root->orphan_block_rsv = NULL;
    spin_unlock
    atomic_dec(&root->orphan_inodes);
    access root->orphan_block_rsv

    Accessing root->orphan_block_rsv must be done before decreasing
    root->orphan_inodes.

    cc: <stable@vger.kernel.org> v3.12+
    Fixes: 703c88e03524 ("Btrfs: fix tracking of orphan inode count")
    Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
    Reviewed-by: Josef Bacik <jbacik@fb.com>
    Signed-off-by: David Sterba <dsterba@suse.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    fs/btrfs/inode.c | 34 +++++++++++++++++++++-------------
    1 file changed, 21 insertions(+), 13 deletions(-)

    --- a/fs/btrfs/inode.c
    +++ b/fs/btrfs/inode.c
    @@ -3369,6 +3369,11 @@ int btrfs_orphan_add(struct btrfs_trans_
    ret = btrfs_orphan_reserve_metadata(trans, inode);
    ASSERT(!ret);
    if (ret) {
    + /*
    + * dec doesn't need spin_lock as ->orphan_block_rsv
    + * would be released only if ->orphan_inodes is
    + * zero.
    + */
    atomic_dec(&root->orphan_inodes);
    clear_bit(BTRFS_INODE_ORPHAN_META_RESERVED,
    &inode->runtime_flags);
    @@ -3383,12 +3388,17 @@ int btrfs_orphan_add(struct btrfs_trans_
    if (insert >= 1) {
    ret = btrfs_insert_orphan_item(trans, root, btrfs_ino(inode));
    if (ret) {
    - atomic_dec(&root->orphan_inodes);
    if (reserve) {
    clear_bit(BTRFS_INODE_ORPHAN_META_RESERVED,
    &inode->runtime_flags);
    btrfs_orphan_release_metadata(inode);
    }
    + /*
    + * btrfs_orphan_commit_root may race with us and set
    + * ->orphan_block_rsv to zero, in order to avoid that,
    + * decrease ->orphan_inodes after everything is done.
    + */
    + atomic_dec(&root->orphan_inodes);
    if (ret != -EEXIST) {
    clear_bit(BTRFS_INODE_HAS_ORPHAN_ITEM,
    &inode->runtime_flags);
    @@ -3420,28 +3430,26 @@ static int btrfs_orphan_del(struct btrfs
    {
    struct btrfs_root *root = inode->root;
    int delete_item = 0;
    - int release_rsv = 0;
    int ret = 0;

    - spin_lock(&root->orphan_lock);
    if (test_and_clear_bit(BTRFS_INODE_HAS_ORPHAN_ITEM,
    &inode->runtime_flags))
    delete_item = 1;

    + if (delete_item && trans)
    + ret = btrfs_del_orphan_item(trans, root, btrfs_ino(inode));
    +
    if (test_and_clear_bit(BTRFS_INODE_ORPHAN_META_RESERVED,
    &inode->runtime_flags))
    - release_rsv = 1;
    - spin_unlock(&root->orphan_lock);
    + btrfs_orphan_release_metadata(inode);

    - if (delete_item) {
    + /*
    + * btrfs_orphan_commit_root may race with us and set ->orphan_block_rsv
    + * to zero, in order to avoid that, decrease ->orphan_inodes after
    + * everything is done.
    + */
    + if (delete_item)
    atomic_dec(&root->orphan_inodes);
    - if (trans)
    - ret = btrfs_del_orphan_item(trans, root,
    - btrfs_ino(inode));
    - }
    -
    - if (release_rsv)
    - btrfs_orphan_release_metadata(inode);

    return ret;
    }

    \
     
     \ /
      Last update: 2018-02-21 14:23    [W:3.708 / U:0.244 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site