• Josef Bacik's avatar
    btrfs: do not double free backref nodes on error · 49ecc679
    Josef Bacik authored
    Zygo reported the following KASAN splat:
    
      BUG: KASAN: use-after-free in btrfs_backref_cleanup_node+0x18a/0x420
      Read of size 8 at addr ffff888112402950 by task btrfs/28836
    
      CPU: 0 PID: 28836 Comm: btrfs Tainted: G        W         5.10.0-e35f27394290-for-next+ #23
      Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014
      Call Trace:
       dump_stack+0xbc/0xf9
       ? btrfs_backref_cleanup_node+0x18a/0x420
       print_address_description.constprop.8+0x21/0x210
       ? record_print_text.cold.34+0x11/0x11
       ? btrfs_backref_cleanup_node+0x18a/0x420
       ? btrfs_backref_cleanup_node+0x18a/0x420
       kasan_report.cold.10+0x20/0x37
       ? btrfs_backref_cleanup_node+0x18a/0x420
       __asan_load8+0x69/0x90
       btrfs_backref_cleanup_node+0x18a/0x420
       btrfs_backref_release_cache+0x83/0x1b0
       relocate_block_group+0x394/0x780
       ? merge_reloc_roots+0x4a0/0x4a0
       btrfs_relocate_block_group+0x26e/0x4c0
       btrfs_relocate_chunk+0x52/0x120
       btrfs_balance+0xe2e/0x1900
       ? check_flags.part.50+0x6c/0x1e0
       ? btrfs_relocate_chunk+0x120/0x120
       ? kmem_cache_alloc_trace+0xa06/0xcb0
       ? _copy_from_user+0x83/0xc0
       btrfs_ioctl_balance+0x3a7/0x460
       btrfs_ioctl+0x24c8/0x4360
       ? __kasan_check_read+0x11/0x20
       ? check_chain_key+0x1f4/0x2f0
       ? __asan_loadN+0xf/0x20
       ? btrfs_ioctl_get_supported_features+0x30/0x30
       ? kvm_sched_clock_read+0x18/0x30
       ? check_chain_key+0x1f4/0x2f0
       ? lock_downgrade+0x3f0/0x3f0
       ? handle_mm_fault+0xad6/0x2150
       ? do_vfs_ioctl+0xfc/0x9d0
       ? ioctl_file_clone+0xe0/0xe0
       ? check_flags.part.50+0x6c/0x1e0
       ? check_flags.part.50+0x6c/0x1e0
       ? check_flags+0x26/0x30
       ? lock_is_held_type+0xc3/0xf0
       ? syscall_enter_from_user_mode+0x1b/0x60
       ? do_syscall_64+0x13/0x80
       ? rcu_read_lock_sched_held+0xa1/0xd0
       ? __kasan_check_read+0x11/0x20
       ? __fget_light+0xae/0x110
       __x64_sys_ioctl+0xc3/0x100
       do_syscall_64+0x37/0x80
       entry_SYSCALL_64_after_hwframe+0x44/0xa9
      RIP: 0033:0x7f4c4bdfe427
    
      Allocated by task 28836:
       kasan_save_stack+0x21/0x50
       __kasan_kmalloc.constprop.18+0xbe/0xd0
       kasan_kmalloc+0x9/0x10
       kmem_cache_alloc_trace+0x410/0xcb0
       btrfs_backref_alloc_node+0x46/0xf0
       btrfs_backref_add_tree_node+0x60d/0x11d0
       build_backref_tree+0xc5/0x700
       relocate_tree_blocks+0x2be/0xb90
       relocate_block_group+0x2eb/0x780
       btrfs_relocate_block_group+0x26e/0x4c0
       btrfs_relocate_chunk+0x52/0x120
       btrfs_balance+0xe2e/0x1900
       btrfs_ioctl_balance+0x3a7/0x460
       btrfs_ioctl+0x24c8/0x4360
       __x64_sys_ioctl+0xc3/0x100
       do_syscall_64+0x37/0x80
       entry_SYSCALL_64_after_hwframe+0x44/0xa9
    
      Freed by task 28836:
       kasan_save_stack+0x21/0x50
       kasan_set_track+0x20/0x30
       kasan_set_free_info+0x1f/0x30
       __kasan_slab_free+0xf3/0x140
       kasan_slab_free+0xe/0x10
       kfree+0xde/0x200
       btrfs_backref_error_cleanup+0x452/0x530
       build_backref_tree+0x1a5/0x700
       relocate_tree_blocks+0x2be/0xb90
       relocate_block_group+0x2eb/0x780
       btrfs_relocate_block_group+0x26e/0x4c0
       btrfs_relocate_chunk+0x52/0x120
       btrfs_balance+0xe2e/0x1900
       btrfs_ioctl_balance+0x3a7/0x460
       btrfs_ioctl+0x24c8/0x4360
       __x64_sys_ioctl+0xc3/0x100
       do_syscall_64+0x37/0x80
       entry_SYSCALL_64_after_hwframe+0x44/0xa9
    
    This occurred because we freed our backref node in
    btrfs_backref_error_cleanup(), but then tried to free it again in
    btrfs_backref_release_cache().  This is because
    btrfs_backref_release_cache() will cycle through all of the
    cache->leaves nodes and free them up.  However
    btrfs_backref_error_cleanup() freed the backref node with
    btrfs_backref_free_node(), which simply kfree()d the backref node
    without unlinking it from the cache.  Change this to a
    btrfs_backref_drop_node(), which does the appropriate cleanup and
    removes the node from the cache->leaves list, so when we go to free the
    remaining cache we don't trip over items we've already dropped.
    
    Fixes: 75bfb9af ("Btrfs: cleanup error handling in build_backref_tree")
    CC: stable@vger.kernel.org # 4.4+
    Signed-off-by: default avatarJosef Bacik <josef@toxicpanda.com>
    Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
    Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
    49ecc679
backref.c 82.6 KB