7.3-stable review patch. If anyone has any objections, please let me know. ------------------ From: Namjae Jeon [ Upstream commit 748ad4ba13cd14163ed7262c6ac304c380804167 ] Buffered overwrite that ends before valid_size in the same filesystem block reads the entire block without IOMAP_F_ZERO_TAIL. If an existing cluster contains stale bytes beyond valid_size, the synchronous read loads them into the page cache and marks the block uptodate. Subsequent buffered reads can return these bytes while i_size exceeds valid_size. For example, with 512-byte blocks, valid_size of 1000 and i_size of 2048, a cold-cache overwrite of [900, 950) exposes stale data in [1000, 1024). Setting ZERO_TAIL for this write would also erase valid data in [950, 1000). Use iomap_truncate_page() to zero only the invalid tail at valid_size before preparing such buffered overwrites. Call it after dropping s_lock and before iomap locks the write folio. The inode lock held by buffered writes serializes valid_size against truncation and page_mkwrite. Skip direct I/O, fault mappings and new allocations. Writes that reach valid_size continue to use IOMAP_F_ZERO_TAIL. Fixes: 82a81a7352bc ("exfat: add iomap buffered I/O support") Cc: stable@vger.kernel.org Signed-off-by: Namjae Jeon Signed-off-by: Sasha Levin --- fs/exfat/iomap.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/fs/exfat/iomap.c b/fs/exfat/iomap.c index faa9b116e5528..bcb7a9214f92b 100644 --- a/fs/exfat/iomap.c +++ b/fs/exfat/iomap.c @@ -149,7 +149,27 @@ static int exfat_iomap_begin(struct inode *inode, loff_t offset, loff_t length, static int exfat_write_iomap_begin(struct inode *inode, loff_t offset, loff_t length, unsigned int flags, struct iomap *iomap, struct iomap *srcmap) { - return __exfat_iomap_begin(inode, offset, length, flags, iomap, true); + struct exfat_inode_info *ei = EXFAT_I(inode); + loff_t valid_size = exfat_get_valid_size(ei); + loff_t end; + int err; + + err = __exfat_iomap_begin(inode, offset, length, flags, iomap, true); + if (err || flags & (IOMAP_DIRECT | IOMAP_FAULT) || + iomap->flags & IOMAP_F_NEW) + return err; + + end = iomap->offset + iomap->length; + if (end >= valid_size || + round_up(end, i_blocksize(inode)) <= valid_size) + return 0; + + /* + * Zero the invalid bytes from valid_size to the end of the block + * to prevent stale data from being exposed through the page cache. + */ + return iomap_truncate_page(inode, valid_size, NULL, &exfat_iomap_ops, + NULL, NULL); } static DEFINE_IOMAP_ITER_NEXT(exfat_iomap_next, exfat_iomap_begin); -- 2.53.0