1 From a19a70a73c7a94e78709d3e5b0d1e075528f1955 Mon Sep 17 00:00:00 2001
2 From: Sasha Levin <sashal@kernel.org>
3 Date: Thu, 29 Oct 2020 14:30:48 -0700
4 Subject: xfs: flush new eof page on truncate to avoid post-eof corruption
6 From: Brian Foster <bfoster@redhat.com>
8 [ Upstream commit 869ae85dae64b5540e4362d7fe4cd520e10ec05c ]
10 It is possible to expose non-zeroed post-EOF data in XFS if the new
11 EOF page is dirty, backed by an unwritten block and the truncate
12 happens to race with writeback. iomap_truncate_page() will not zero
13 the post-EOF portion of the page if the underlying block is
14 unwritten. The subsequent call to truncate_setsize() will, but
15 doesn't dirty the page. Therefore, if writeback happens to complete
16 after iomap_truncate_page() (so it still sees the unwritten block)
17 but before truncate_setsize(), the cached page becomes inconsistent
18 with the on-disk block. A mapped read after the associated page is
19 reclaimed or invalidated exposes non-zero post-EOF data.
21 For example, consider the following sequence when run on a kernel
22 modified to explicitly flush the new EOF page within the race
25 $ xfs_io -fc "falloc 0 4k" -c fsync /mnt/file
26 $ xfs_io -c "pwrite 0 4k" -c "truncate 1k" /mnt/file
28 $ xfs_io -c "mmap 0 4k" -c "mread -v 1k 8" /mnt/file
29 00000400: 00 00 00 00 00 00 00 00 ........
30 $ umount /mnt/; mount <dev> /mnt/
31 $ xfs_io -c "mmap 0 4k" -c "mread -v 1k 8" /mnt/file
32 00000400: cd cd cd cd cd cd cd cd ........
34 Update xfs_setattr_size() to explicitly flush the new EOF page prior
35 to the page truncate to ensure iomap has the latest state of the
38 Fixes: 68a9f5e7007c ("xfs: implement iomap based buffered write path")
39 Signed-off-by: Brian Foster <bfoster@redhat.com>
40 Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
41 Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
42 Signed-off-by: Sasha Levin <sashal@kernel.org>
44 fs/xfs/xfs_iops.c | 10 ++++++++++
45 1 file changed, 10 insertions(+)
47 diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
48 index e427ad097e2ee..948ac1290121b 100644
49 --- a/fs/xfs/xfs_iops.c
50 +++ b/fs/xfs/xfs_iops.c
51 @@ -895,6 +895,16 @@ xfs_setattr_size(
52 error = iomap_zero_range(inode, oldsize, newsize - oldsize,
53 &did_zeroing, &xfs_iomap_ops);
56 + * iomap won't detect a dirty page over an unwritten block (or a
57 + * cow block over a hole) and subsequently skips zeroing the
58 + * newly post-EOF portion of the page. Flush the new EOF to
59 + * convert the block before the pagecache truncate.
61 + error = filemap_write_and_wait_range(inode->i_mapping, newsize,
65 error = iomap_truncate_page(inode, newsize, &did_zeroing,