From: Greg Kroah-Hartman Date: Fri, 24 Jul 2020 09:47:05 +0000 (+0200) Subject: 5.4-stable patches X-Git-Tag: v4.14.190~41 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=64098af1d6f4ccb33d07d34f919087b498579c97;p=thirdparty%2Fkernel%2Fstable-queue.git 5.4-stable patches added patches: btrfs-reloc-clear-dead_reloc_tree-bit-for-orphan-roots-to-prevent-runaway-balance.patch btrfs-reloc-fix-reloc-root-leak-and-null-pointer-dereference.patch --- diff --git a/queue-5.4/btrfs-reloc-clear-dead_reloc_tree-bit-for-orphan-roots-to-prevent-runaway-balance.patch b/queue-5.4/btrfs-reloc-clear-dead_reloc_tree-bit-for-orphan-roots-to-prevent-runaway-balance.patch new file mode 100644 index 00000000000..57b867a359d --- /dev/null +++ b/queue-5.4/btrfs-reloc-clear-dead_reloc_tree-bit-for-orphan-roots-to-prevent-runaway-balance.patch @@ -0,0 +1,56 @@ +From 1dae7e0e58b484eaa43d530f211098fdeeb0f404 Mon Sep 17 00:00:00 2001 +From: Qu Wenruo +Date: Wed, 20 May 2020 14:58:51 +0800 +Subject: btrfs: reloc: clear DEAD_RELOC_TREE bit for orphan roots to prevent runaway balance + +From: Qu Wenruo + +commit 1dae7e0e58b484eaa43d530f211098fdeeb0f404 upstream. + +[BUG] +There are several reported runaway balance, that balance is flooding the +log with "found X extents" where the X never changes. + +[CAUSE] +Commit d2311e698578 ("btrfs: relocation: Delay reloc tree deletion after +merge_reloc_roots") introduced BTRFS_ROOT_DEAD_RELOC_TREE bit to +indicate that one subvolume has finished its tree blocks swap with its +reloc tree. + +However if balance is canceled or hits ENOSPC halfway, we didn't clear +the BTRFS_ROOT_DEAD_RELOC_TREE bit, leaving that bit hanging forever +until unmount. + +Any subvolume root with that bit, would cause backref cache to skip this +tree block, as it has finished its tree block swap. This would cause +all tree blocks of that root be ignored by balance, leading to runaway +balance. + +[FIX] +Fix the problem by also clearing the BTRFS_ROOT_DEAD_RELOC_TREE bit for +the original subvolume of orphan reloc root. + +Add an umount check for the stale bit still set. + +Fixes: d2311e698578 ("btrfs: relocation: Delay reloc tree deletion after merge_reloc_roots") +Signed-off-by: Qu Wenruo +Signed-off-by: David Sterba +[Manually solve the conflicts due to no btrfs root refs rework] +Signed-off-by: David Sterba +Signed-off-by: Greg Kroah-Hartman + +--- + fs/btrfs/relocation.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/fs/btrfs/relocation.c ++++ b/fs/btrfs/relocation.c +@@ -2540,6 +2540,8 @@ again: + if (!IS_ERR(root)) { + if (root->reloc_root == reloc_root) + root->reloc_root = NULL; ++ clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, ++ &root->state); + } + + list_del_init(&reloc_root->root_list); diff --git a/queue-5.4/btrfs-reloc-fix-reloc-root-leak-and-null-pointer-dereference.patch b/queue-5.4/btrfs-reloc-fix-reloc-root-leak-and-null-pointer-dereference.patch new file mode 100644 index 00000000000..f0d35b35741 --- /dev/null +++ b/queue-5.4/btrfs-reloc-fix-reloc-root-leak-and-null-pointer-dereference.patch @@ -0,0 +1,133 @@ +From 51415b6c1b117e223bc083e30af675cb5c5498f3 Mon Sep 17 00:00:00 2001 +From: Qu Wenruo +Date: Tue, 19 May 2020 10:13:20 +0800 +Subject: btrfs: reloc: fix reloc root leak and NULL pointer dereference + +From: Qu Wenruo + +commit 51415b6c1b117e223bc083e30af675cb5c5498f3 upstream. + +[BUG] +When balance is canceled, there is a pretty high chance that unmounting +the fs can lead to lead the NULL pointer dereference: + + BTRFS warning (device dm-3): page private not zero on page 223158272 + ... + BTRFS warning (device dm-3): page private not zero on page 223162368 + BTRFS error (device dm-3): leaked root 18446744073709551608-304 refcount 1 + BUG: kernel NULL pointer dereference, address: 0000000000000168 + #PF: supervisor read access in kernel mode + #PF: error_code(0x0000) - not-present page + PGD 0 P4D 0 + Oops: 0000 [#1] PREEMPT SMP NOPTI + CPU: 2 PID: 5793 Comm: umount Tainted: G O 5.7.0-rc5-custom+ #53 + Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 + RIP: 0010:__lock_acquire+0x5dc/0x24c0 + Call Trace: + lock_acquire+0xab/0x390 + _raw_spin_lock+0x39/0x80 + btrfs_release_extent_buffer_pages+0xd7/0x200 [btrfs] + release_extent_buffer+0xb2/0x170 [btrfs] + free_extent_buffer+0x66/0xb0 [btrfs] + btrfs_put_root+0x8e/0x130 [btrfs] + btrfs_check_leaked_roots.cold+0x5/0x5d [btrfs] + btrfs_free_fs_info+0xe5/0x120 [btrfs] + btrfs_kill_super+0x1f/0x30 [btrfs] + deactivate_locked_super+0x3b/0x80 + deactivate_super+0x3e/0x50 + cleanup_mnt+0x109/0x160 + __cleanup_mnt+0x12/0x20 + task_work_run+0x67/0xa0 + exit_to_usermode_loop+0xc5/0xd0 + syscall_return_slowpath+0x205/0x360 + do_syscall_64+0x6e/0xb0 + entry_SYSCALL_64_after_hwframe+0x49/0xb3 + RIP: 0033:0x7fd028ef740b + +[CAUSE] +When balance is canceled, all reloc roots are marked as orphan, and +orphan reloc roots are going to be cleaned up. + +However for orphan reloc roots and merged reloc roots, their lifespan +are quite different: + + Merged reloc roots | Orphan reloc roots by cancel +-------------------------------------------------------------------- +create_reloc_root() | create_reloc_root() +|- refs == 1 | |- refs == 1 + | +btrfs_grab_root(reloc_root); | btrfs_grab_root(reloc_root); +|- refs == 2 | |- refs == 2 + | +root->reloc_root = reloc_root; | root->reloc_root = reloc_root; + >>> No difference so far <<< + | +prepare_to_merge() | prepare_to_merge() +|- btrfs_set_root_refs(item, 1);| |- if (!err) (err == -EINTR) + | +merge_reloc_roots() | merge_reloc_roots() +|- merge_reloc_root() | |- Doing nothing to put reloc root + |- insert_dirty_subvol() | |- refs == 2 + |- __del_reloc_root() | + |- btrfs_put_root() | + |- refs == 1 | + >>> Now orphan reloc roots still have refs 2 <<< + | +clean_dirty_subvols() | clean_dirty_subvols() +|- btrfs_drop_snapshot() | |- btrfS_drop_snapshot() + |- reloc_root get freed | |- reloc_root still has refs 2 + | related ebs get freed, but + | reloc_root still recorded in + | allocated_roots +btrfs_check_leaked_roots() | btrfs_check_leaked_roots() +|- No leaked roots | |- Leaked reloc_roots detected + | |- btrfs_put_root() + | |- free_extent_buffer(root->node); + | |- eb already freed, caused NULL + | pointer dereference + +[FIX] +The fix is to clear fs_root->reloc_root and put it at +merge_reloc_roots() time, so that we won't leak reloc roots. + +Fixes: d2311e698578 ("btrfs: relocation: Delay reloc tree deletion after merge_reloc_roots") +CC: stable@vger.kernel.org # 5.1+ +Tested-by: Johannes Thumshirn +Signed-off-by: Qu Wenruo +Signed-off-by: David Sterba +[Manually solve the conflicts due to no btrfs root refs rework] +Signed-off-by: David Sterba +Signed-off-by: Greg Kroah-Hartman + +--- + fs/btrfs/relocation.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +--- a/fs/btrfs/relocation.c ++++ b/fs/btrfs/relocation.c +@@ -2525,12 +2525,10 @@ again: + reloc_root = list_entry(reloc_roots.next, + struct btrfs_root, root_list); + ++ root = read_fs_root(fs_info, reloc_root->root_key.offset); + if (btrfs_root_refs(&reloc_root->root_item) > 0) { +- root = read_fs_root(fs_info, +- reloc_root->root_key.offset); + BUG_ON(IS_ERR(root)); + BUG_ON(root->reloc_root != reloc_root); +- + ret = merge_reloc_root(rc, root); + if (ret) { + if (list_empty(&reloc_root->root_list)) +@@ -2539,6 +2537,11 @@ again: + goto out; + } + } else { ++ if (!IS_ERR(root)) { ++ if (root->reloc_root == reloc_root) ++ root->reloc_root = NULL; ++ } ++ + list_del_init(&reloc_root->root_list); + /* Don't forget to queue this reloc root for cleanup */ + list_add_tail(&reloc_root->reloc_dirty_list, diff --git a/queue-5.4/series b/queue-5.4/series index 056aa040d3e..e29016904d2 100644 --- a/queue-5.4/series +++ b/queue-5.4/series @@ -24,3 +24,5 @@ drm-amd-display-check-dmcu-exists-before-loading.patch dm-mpath-pass-io-start-time-to-path-selector.patch dm-do-not-use-waitqueue-for-request-based-dm.patch sunrpc-reverting-d03727b248d0-nfsv4-fix-close-not-waiting-for-direct-io-compeletion.patch +btrfs-reloc-fix-reloc-root-leak-and-null-pointer-dereference.patch +btrfs-reloc-clear-dead_reloc_tree-bit-for-orphan-roots-to-prevent-runaway-balance.patch