From patchwork Wed Jun 23 00:44:33 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matthew Ruffell X-Patchwork-Id: 1495885 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.ubuntu.com (client-ip=91.189.94.19; helo=huckleberry.canonical.com; envelope-from=kernel-team-bounces@lists.ubuntu.com; receiver=) Received: from huckleberry.canonical.com (huckleberry.canonical.com [91.189.94.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4G8l0R4THrz9sWw; Wed, 23 Jun 2021 10:44:55 +1000 (AEST) Received: from localhost ([127.0.0.1] helo=huckleberry.canonical.com) by huckleberry.canonical.com with esmtp (Exim 4.86_2) (envelope-from ) id 1lvr0c-0002Ne-Az; Wed, 23 Jun 2021 00:44:50 +0000 Received: from youngberry.canonical.com ([91.189.89.112]) by huckleberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1lvr0Z-0002Mv-8c for kernel-team@lists.ubuntu.com; Wed, 23 Jun 2021 00:44:47 +0000 Received: from mail-pl1-f198.google.com ([209.85.214.198]) by youngberry.canonical.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.93) (envelope-from ) id 1lvr0Z-0003jw-0M for kernel-team@lists.ubuntu.com; Wed, 23 Jun 2021 00:44:47 +0000 Received: by mail-pl1-f198.google.com with SMTP id q12-20020a170902a3ccb0290124d72c3413so101609plb.3 for ; Tue, 22 Jun 2021 17:44:46 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=8l5BAnn3N3CRdOFVaV0gf16xfP4E/b3NgZ68zNFfCx4=; b=XgUHP4syLWw7v36Biuuhzb/9LzPqdyPMbKHcdgmhOwCCn3dNDgpqYjVnvDJdYTB9zh Jz36t+63gchGXo3cq5x+7MPX0sT6v9sbvmIUArC2FLenQPyJnM7yHRrNHUlAyGD4/5AA 0km6ublWX5t4x2If+IslLvO9Z22NWEMW6NB8ux//RQ6Yu5ujZtL5BL4ZaZGb5Woqb0Fr Dovd+RZT8VttRv4851LKJtVVLFYTqfxIXlOPXku28kz02vsqMyNSZezY1/C3JopcPndm T6gKREFK9uVxjBw8XuwQVpjUNkmvEc6qPb8QCGcHwYSPgseKkzvZkQ0f5tg+0WS9Jch+ sOMw== X-Gm-Message-State: AOAM530Dyc4NLxl2hE6qiU8pGp4cOOl0RGHJU2mhNoJ1p5Et67eHShW9 Y7M6tgx0UAao4eSQXtsuoZpV4Zx3XfsTXIFf7G4JCak5tBFE/HhQUUrUcCmSpWnzIzRTS+1Nv3Q aXMF7aelXIHqaMgUserwyjFoullBtpC4xC3HSww5fmg== X-Received: by 2002:a62:3606:0:b029:302:3c87:4f37 with SMTP id d6-20020a6236060000b02903023c874f37mr6225711pfa.53.1624409085729; Tue, 22 Jun 2021 17:44:45 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyP9yPlWcVBbwX/OaI1eVz1mSrRxeYhggQU0OgHf+o/3C9P1XBxAVYoEYB9s6kaI9ZfkJReBA== X-Received: by 2002:a62:3606:0:b029:302:3c87:4f37 with SMTP id d6-20020a6236060000b02903023c874f37mr6225691pfa.53.1624409085335; Tue, 22 Jun 2021 17:44:45 -0700 (PDT) Received: from desktop.. (125-237-197-94-fibre.sparkbb.co.nz. [125.237.197.94]) by smtp.gmail.com with ESMTPSA id n23sm443798pff.93.2021.06.22.17.44.43 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 22 Jun 2021 17:44:45 -0700 (PDT) From: Matthew Ruffell To: kernel-team@lists.ubuntu.com Subject: [SRU][B][PATCH 2/2] btrfs: reloc: fix reloc root leak and NULL pointer dereference Date: Wed, 23 Jun 2021 12:44:33 +1200 Message-Id: <20210623004433.22819-3-matthew.ruffell@canonical.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210623004433.22819-1-matthew.ruffell@canonical.com> References: <20210623004433.22819-1-matthew.ruffell@canonical.com> MIME-Version: 1.0 X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.20 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: kernel-team-bounces@lists.ubuntu.com Sender: "kernel-team" From: Qu Wenruo BugLink: https://bugs.launchpad.net/bugs/1933172 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 (cherry picked from commit 044ca910276b2e68cf40f5584e85b0727d919a43 5.4.y) CVE-2019-19036 Signed-off-by: Matthew Ruffell --- fs/btrfs/relocation.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 95bc0ee66c03..aaa33186648b 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -2444,12 +2444,10 @@ void merge_reloc_roots(struct reloc_control *rc) 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)) @@ -2458,6 +2456,11 @@ void merge_reloc_roots(struct reloc_control *rc) goto out; } } else { + if (!IS_ERR(root)) { + if (root->reloc_root == reloc_root) + root->reloc_root = NULL; + } + list_del_init(&reloc_root->root_list); }