Skip to content

Commit c5c810b

Browse files
Brian Fosterbrauner
authored andcommitted
iomap: fix handling of dirty folios over unwritten extents
The iomap zero range implementation doesn't properly handle dirty pagecache over unwritten mappings. It skips such mappings as if they were pre-zeroed. If some part of an unwritten mapping is dirty in pagecache from a previous write, the data in cache should be zeroed as well. Instead, the data is left in cache and creates a stale data exposure problem if writeback occurs sometime after the zero range. Most callers are unaffected by this because the higher level filesystem contexts that call zero range typically perform a filemap flush of the target range for other reasons. A couple contexts that don't otherwise need to flush are write file size extension and truncate in XFS. The former path is currently susceptible to the stale data exposure problem and the latter performs a flush specifically to work around it. This is clearly inconsistent and incomplete. As a first step toward correcting behavior, lift the XFS workaround to iomap_zero_range() and unconditionally flush the range before the zero range operation proceeds. While this appears to be a bit of a big hammer, most all users already do this from calling context save for the couple of exceptions noted above. Future patches will optimize or elide this flush while maintaining functional correctness. Fixes: ae259a9 ("fs: introduce iomap infrastructure") Signed-off-by: Brian Foster <[email protected]> Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Darrick J. Wong <[email protected]> Reviewed-by: Josef Bacik <[email protected]> Signed-off-by: Christian Brauner <[email protected]>
1 parent 6f634eb commit c5c810b

File tree

2 files changed

+10
-10
lines changed

2 files changed

+10
-10
lines changed

fs/iomap/buffered-io.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1452,6 +1452,16 @@ iomap_zero_range(struct inode *inode, loff_t pos, loff_t len, bool *did_zero,
14521452
};
14531453
int ret;
14541454

1455+
/*
1456+
* Zero range wants to skip pre-zeroed (i.e. unwritten) mappings, but
1457+
* pagecache must be flushed to ensure stale data from previous
1458+
* buffered writes is not exposed.
1459+
*/
1460+
ret = filemap_write_and_wait_range(inode->i_mapping,
1461+
pos, pos + len - 1);
1462+
if (ret)
1463+
return ret;
1464+
14551465
while ((ret = iomap_iter(&iter, ops)) > 0)
14561466
iter.processed = iomap_zero_iter(&iter, did_zero);
14571467
return ret;

fs/xfs/xfs_iops.c

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -870,16 +870,6 @@ xfs_setattr_size(
870870
error = xfs_zero_range(ip, oldsize, newsize - oldsize,
871871
&did_zeroing);
872872
} else {
873-
/*
874-
* iomap won't detect a dirty page over an unwritten block (or a
875-
* cow block over a hole) and subsequently skips zeroing the
876-
* newly post-EOF portion of the page. Flush the new EOF to
877-
* convert the block before the pagecache truncate.
878-
*/
879-
error = filemap_write_and_wait_range(inode->i_mapping, newsize,
880-
newsize);
881-
if (error)
882-
return error;
883873
error = xfs_truncate_page(ip, newsize, &did_zeroing);
884874
}
885875

0 commit comments

Comments
 (0)