--- /dev/null
+From e2c27b803bb664748e090d99042ac128b3f88d92 Mon Sep 17 00:00:00 2001
+From: Baokun Li <libaokun1@huawei.com>
+Date: Wed, 13 Dec 2023 14:23:24 +0800
+Subject: mm/filemap: avoid buffered read/write race to read inconsistent data
+
+From: Baokun Li <libaokun1@huawei.com>
+
+commit e2c27b803bb664748e090d99042ac128b3f88d92 upstream.
+
+The following concurrency may cause the data read to be inconsistent with
+the data on disk:
+
+ cpu1 cpu2
+------------------------------|------------------------------
+ // Buffered write 2048 from 0
+ ext4_buffered_write_iter
+ generic_perform_write
+ copy_page_from_iter_atomic
+ ext4_da_write_end
+ ext4_da_do_write_end
+ block_write_end
+ __block_commit_write
+ folio_mark_uptodate
+// Buffered read 4096 from 0 smp_wmb()
+ext4_file_read_iter set_bit(PG_uptodate, folio_flags)
+ generic_file_read_iter i_size_write // 2048
+ filemap_read unlock_page(page)
+ filemap_get_pages
+ filemap_get_read_batch
+ folio_test_uptodate(folio)
+ ret = test_bit(PG_uptodate, folio_flags)
+ if (ret)
+ smp_rmb();
+ // Ensure that the data in page 0-2048 is up-to-date.
+
+ // New buffered write 2048 from 2048
+ ext4_buffered_write_iter
+ generic_perform_write
+ copy_page_from_iter_atomic
+ ext4_da_write_end
+ ext4_da_do_write_end
+ block_write_end
+ __block_commit_write
+ folio_mark_uptodate
+ smp_wmb()
+ set_bit(PG_uptodate, folio_flags)
+ i_size_write // 4096
+ unlock_page(page)
+
+ isize = i_size_read(inode) // 4096
+ // Read the latest isize 4096, but without smp_rmb(), there may be
+ // Load-Load disorder resulting in the data in the 2048-4096 range
+ // in the page is not up-to-date.
+ copy_page_to_iter
+ // copyout 4096
+
+In the concurrency above, we read the updated i_size, but there is no read
+barrier to ensure that the data in the page is the same as the i_size at
+this point, so we may copy the unsynchronized page out. Hence adding the
+missing read memory barrier to fix this.
+
+This is a Load-Load reordering issue, which only occurs on some weak
+mem-ordering architectures (e.g. ARM64, ALPHA), but not on strong
+mem-ordering architectures (e.g. X86). And theoretically the problem
+doesn't only happen on ext4, filesystems that call filemap_read() but
+don't hold inode lock (e.g. btrfs, f2fs, ubifs ...) will have this
+problem, while filesystems with inode lock (e.g. xfs, nfs) won't have
+this problem.
+
+Link: https://lkml.kernel.org/r/20231213062324.739009-1-libaokun1@huawei.com
+Signed-off-by: Baokun Li <libaokun1@huawei.com>
+Reviewed-by: Jan Kara <jack@suse.cz>
+Cc: Andreas Dilger <adilger.kernel@dilger.ca>
+Cc: Christoph Hellwig <hch@infradead.org>
+Cc: Dave Chinner <david@fromorbit.com>
+Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
+Cc: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Cc: Theodore Ts'o <tytso@mit.edu>
+Cc: yangerkun <yangerkun@huawei.com>
+Cc: Yu Kuai <yukuai3@huawei.com>
+Cc: Zhang Yi <yi.zhang@huawei.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ mm/filemap.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+--- a/mm/filemap.c
++++ b/mm/filemap.c
+@@ -2649,6 +2649,15 @@ ssize_t filemap_read(struct kiocb *iocb,
+ end_offset = min_t(loff_t, isize, iocb->ki_pos + iter->count);
+
+ /*
++ * Pairs with a barrier in
++ * block_write_end()->mark_buffer_dirty() or other page
++ * dirtying routines like iomap_write_end() to ensure
++ * changes to page contents are visible before we see
++ * increased inode size.
++ */
++ smp_rmb();
++
++ /*
+ * Once we start copying data, we don't want to be touching any
+ * cachelines that might be contended:
+ */