lkml.org 
[lkml]   [2018]   [Sep]   [24]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH AUTOSEL 4.18 06/76] btrfs: btrfs_shrink_device should call commit transaction at the end
    Date
    From: Anand Jain <anand.jain@oracle.com>

    [ Upstream commit 801660b040d132f67fac6a95910ad307c5929b49 ]

    Test case btrfs/164 reports use-after-free:

    [ 6712.084324] general protection fault: 0000 [#1] PREEMPT SMP
    ..
    [ 6712.195423] btrfs_update_commit_device_size+0x75/0xf0 [btrfs]
    [ 6712.201424] btrfs_commit_transaction+0x57d/0xa90 [btrfs]
    [ 6712.206999] btrfs_rm_device+0x627/0x850 [btrfs]
    [ 6712.211800] btrfs_ioctl+0x2b03/0x3120 [btrfs]

    Reason for this is that btrfs_shrink_device adds the resized device to
    the fs_devices::resized_devices after it has called the last commit
    transaction.

    So the list fs_devices::resized_devices is not empty when
    btrfs_shrink_device returns. Now the parent function
    btrfs_rm_device calls:

    btrfs_close_bdev(device);
    call_rcu(&device->rcu, free_device_rcu);

    and then does the transactio ncommit. It goes through the
    fs_devices::resized_devices in btrfs_update_commit_device_size and
    leads to use-after-free.

    Fix this by making sure btrfs_shrink_device calls the last needed
    btrfs_commit_transaction before the return. This is consistent with what
    the grow counterpart does and this makes sure the on-disk state is
    persistent when the function returns.

    Reported-by: Lu Fengqi <lufq.fnst@cn.fujitsu.com>
    Tested-by: Lu Fengqi <lufq.fnst@cn.fujitsu.com>
    Signed-off-by: Anand Jain <anand.jain@oracle.com>
    Reviewed-by: David Sterba <dsterba@suse.com>
    [ update changelog ]
    Signed-off-by: David Sterba <dsterba@suse.com>
    Signed-off-by: Sasha Levin <alexander.levin@microsoft.com>
    ---
    fs/btrfs/volumes.c | 7 ++++++-
    1 file changed, 6 insertions(+), 1 deletion(-)

    diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
    index 5304b8d6ceb8..1a22c0ecaf67 100644
    --- a/fs/btrfs/volumes.c
    +++ b/fs/btrfs/volumes.c
    @@ -4584,7 +4584,12 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size)

    /* Now btrfs_update_device() will change the on-disk size. */
    ret = btrfs_update_device(trans, device);
    - btrfs_end_transaction(trans);
    + if (ret < 0) {
    + btrfs_abort_transaction(trans, ret);
    + btrfs_end_transaction(trans);
    + } else {
    + ret = btrfs_commit_transaction(trans);
    + }
    done:
    btrfs_free_path(path);
    if (ret) {
    --
    2.17.1
    \
     
     \ /
      Last update: 2018-09-24 16:49    [W:4.574 / U:0.212 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site