• Qu Wenruo's avatar
    btrfs: Wait for in-flight bios before freeing target device for raid56 · ae6529c3
    Qu Wenruo authored
    When raid56 dev-replace is cancelled by running scrub, we will free
    target device without waiting for in-flight bios, causing the following
    NULL pointer deference or general protection failure.
    
     BUG: unable to handle kernel NULL pointer dereference at 00000000000005e0
     IP: generic_make_request_checks+0x4d/0x610
     CPU: 1 PID: 11676 Comm: kworker/u4:14 Tainted: G  O    4.11.0-rc2 #72
     Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.10.2-20170228_101828-anatol 04/01/2014
     Workqueue: btrfs-endio-raid56 btrfs_endio_raid56_helper [btrfs]
     task: ffff88002875b4c0 task.stack: ffffc90001334000
     RIP: 0010:generic_make_request_checks+0x4d/0x610
     Call Trace:
      ? generic_make_request+0xc7/0x360
      generic_make_request+0x24/0x360
      ? generic_make_request+0xc7/0x360
      submit_bio+0x64/0x120
      ? page_in_rbio+0x4d/0x80 [btrfs]
      ? rbio_orig_end_io+0x80/0x80 [btrfs]
      finish_rmw+0x3f4/0x540 [btrfs]
      validate_rbio_for_rmw+0x36/0x40 [btrfs]
      raid_rmw_end_io+0x7a/0x90 [btrfs]
      bio_endio+0x56/0x60
      end_workqueue_fn+0x3c/0x40 [btrfs]
      btrfs_scrubparity_helper+0xef/0x620 [btrfs]
      btrfs_endio_raid56_helper+0xe/0x10 [btrfs]
      process_one_work+0x2af/0x720
      ? process_one_work+0x22b/0x720
      worker_thread+0x4b/0x4f0
      kthread+0x10f/0x150
      ? process_one_work+0x720/0x720
      ? kthread_create_on_node+0x40/0x40
      ret_from_fork+0x2e/0x40
     RIP: generic_make_request_checks+0x4d/0x610 RSP: ffffc90001337bb8
    
    In btrfs_dev_replace_finishing(), we will call
    btrfs_rm_dev_replace_blocked() to wait bios before destroying the target
    device when scrub is finished normally.
    
    However when dev-replace is aborted, either due to error or cancelled by
    scrub, we didn't wait for bios, this can lead to use-after-free if there
    are bios holding the target device.
    
    Furthermore, for raid56 scrub, at least 2 places are calling
    btrfs_map_sblock() without protection of bio_counter, leading to the
    problem.
    
    This patch fixes the problem:
    1) Wait for bio_counter before freeing target device when canceling
       replace
    2) When calling btrfs_map_sblock() for raid56, use bio_counter to
       protect the call.
    
    Cc: Liu Bo <bo.li.liu@oracle.com>
    Signed-off-by: default avatarQu Wenruo <quwenruo@cn.fujitsu.com>
    Reviewed-by: default avatarLiu Bo <bo.li.liu@oracle.com>
    Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
    ae6529c3
scrub.c 116 KB