Qu Wenruo e13003
From e0467866198f7f536806f39e5d0d91ae8018de08 Mon Sep 17 00:00:00 2001
Qu Wenruo e13003
From: Qu Wenruo <wqu@suse.com>
Qu Wenruo e13003
Date: Mon, 26 Jul 2021 14:35:02 +0800
Qu Wenruo e13003
Patch-mainline: v5.15-rc1
Qu Wenruo e13003
References: jsc#SLE-17681
Qu Wenruo e13003
Git-commit: e0467866198f7f536806f39e5d0d91ae8018de08
Qu Wenruo e13003
Subject: [PATCH 13/18] btrfs: subpage: fix race between prepare_pages() and
Qu Wenruo e13003
 btrfs_releasepage()
Qu Wenruo e13003
Qu Wenruo e13003
[BUG]
Qu Wenruo e13003
When running generic/095, there is a high chance to crash with subpage
Qu Wenruo e13003
data RW support:
Qu Wenruo e13003
Qu Wenruo e13003
 assertion failed: PagePrivate(page) && page->private
Qu Wenruo e13003
 ------------[ cut here ]------------
Qu Wenruo e13003
 kernel BUG at fs/btrfs/ctree.h:3403!
Qu Wenruo e13003
 Internal error: Oops - BUG: 0 [#1] SMP
Qu Wenruo e13003
 CPU: 1 PID: 3567 Comm: fio Tainted: 5.12.0-rc7-custom+ #17
Qu Wenruo e13003
 Hardware name: Khadas VIM3 (DT)
Qu Wenruo e13003
 Call trace:
Qu Wenruo e13003
  assertfail.constprop.0+0x28/0x2c [btrfs]
Qu Wenruo e13003
  btrfs_subpage_assert+0x80/0xa0 [btrfs]
Qu Wenruo e13003
  btrfs_subpage_set_uptodate+0x34/0xec [btrfs]
Qu Wenruo e13003
  btrfs_page_clamp_set_uptodate+0x74/0xa4 [btrfs]
Qu Wenruo e13003
  btrfs_dirty_pages+0x160/0x270 [btrfs]
Qu Wenruo e13003
  btrfs_buffered_write+0x444/0x630 [btrfs]
Qu Wenruo e13003
  btrfs_direct_write+0x1cc/0x2d0 [btrfs]
Qu Wenruo e13003
  btrfs_file_write_iter+0xc0/0x160 [btrfs]
Qu Wenruo e13003
  new_sync_write+0xe8/0x180
Qu Wenruo e13003
  vfs_write+0x1b4/0x210
Qu Wenruo e13003
  ksys_pwrite64+0x7c/0xc0
Qu Wenruo e13003
  __arm64_sys_pwrite64+0x24/0x30
Qu Wenruo e13003
  el0_svc_common.constprop.0+0x70/0x140
Qu Wenruo e13003
  do_el0_svc+0x28/0x90
Qu Wenruo e13003
  el0_svc+0x2c/0x54
Qu Wenruo e13003
  el0_sync_handler+0x1a8/0x1ac
Qu Wenruo e13003
  el0_sync+0x170/0x180
Qu Wenruo e13003
 Code: f0000160 913be042 913c4000 955444bc (d4210000)
Qu Wenruo e13003
 ---[ end trace 3fdd39f4cccedd68 ]---
Qu Wenruo e13003
Qu Wenruo e13003
[CAUSE]
Qu Wenruo e13003
Although prepare_pages() calls find_or_create_page(), which returns the
Qu Wenruo e13003
page locked, but in later prepare_uptodate_page() calls, we may call
Qu Wenruo e13003
btrfs_readpage() which will unlock the page before it returns.
Qu Wenruo e13003
Qu Wenruo e13003
This leaves a window where btrfs_releasepage() can sneak in and release
Qu Wenruo e13003
the page, clearing page->private and causing above ASSERT().
Qu Wenruo e13003
Qu Wenruo e13003
[FIX]
Qu Wenruo e13003
In prepare_uptodate_page(), we should not only check page->mapping, but
Qu Wenruo e13003
also PagePrivate() to ensure we are still holding the correct page which
Qu Wenruo e13003
has proper fs context setup.
Qu Wenruo e13003
Qu Wenruo e13003
Reported-by: Ritesh Harjani <riteshh@linux.ibm.com>
Qu Wenruo e13003
Tested-by: Ritesh Harjani <riteshh@linux.ibm.com>
Qu Wenruo e13003
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Qu Wenruo e13003
Signed-off-by: Qu Wenruo <wqu@suse.com>
Qu Wenruo e13003
Signed-off-by: David Sterba <dsterba@suse.com>
Qu Wenruo e13003
---
Qu Wenruo e13003
 fs/btrfs/file.c | 13 ++++++++++++-
Qu Wenruo e13003
 1 file changed, 12 insertions(+), 1 deletion(-)
Qu Wenruo e13003
Qu Wenruo e13003
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
Qu Wenruo e13003
index ee34497500e1..8c57af3702fa 100644
Qu Wenruo e13003
--- a/fs/btrfs/file.c
Qu Wenruo e13003
+++ b/fs/btrfs/file.c
Qu Wenruo e13003
@@ -1340,7 +1340,18 @@ static int prepare_uptodate_page(struct inode *inode,
Qu Wenruo e13003
 			unlock_page(page);
Qu Wenruo e13003
 			return -EIO;
Qu Wenruo e13003
 		}
Qu Wenruo e13003
-		if (page->mapping != inode->i_mapping) {
Qu Wenruo e13003
+
Qu Wenruo e13003
+		/*
Qu Wenruo e13003
+		 * Since btrfs_readpage() will unlock the page before it
Qu Wenruo e13003
+		 * returns, there is a window where btrfs_releasepage() can
Qu Wenruo e13003
+		 * be called to release the page.
Qu Wenruo e13003
+		 * Here we check both inode mapping and PagePrivate() to
Qu Wenruo e13003
+		 * make sure the page was not released.
Qu Wenruo e13003
+		 *
Qu Wenruo e13003
+		 * The private flag check is essential for subpage as we need
Qu Wenruo e13003
+		 * to store extra bitmap using page->private.
Qu Wenruo e13003
+		 */
Qu Wenruo e13003
+		if (page->mapping != inode->i_mapping || !PagePrivate(page)) {
Qu Wenruo e13003
 			unlock_page(page);
Qu Wenruo e13003
 			return -EAGAIN;
Qu Wenruo e13003
 		}
Qu Wenruo e13003
-- 
Qu Wenruo e13003
2.33.0
Qu Wenruo e13003