mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-01 10:43:43 +00:00
ext4: fix data corruption for mmap writes
mpage_submit_page() can race with another process growing i_size and
writing data via mmap to the written-back page. As mpage_submit_page()
samples i_size too early, it may happen that ext4_bio_write_page()
zeroes out too large tail of the page and thus corrupts user data.
Fix the problem by sampling i_size only after the page has been
write-protected in page tables by clear_page_dirty_for_io() call.
Reported-by: Michael Zimmer <michael@swarm64.com>
CC: stable@vger.kernel.org
Fixes: cb20d51883
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
This commit is contained in:
parent
4f8caa60a5
commit
a056bdaae7
@ -2124,15 +2124,29 @@ static int ext4_writepage(struct page *page,
|
||||
static int mpage_submit_page(struct mpage_da_data *mpd, struct page *page)
|
||||
{
|
||||
int len;
|
||||
loff_t size = i_size_read(mpd->inode);
|
||||
loff_t size;
|
||||
int err;
|
||||
|
||||
BUG_ON(page->index != mpd->first_page);
|
||||
clear_page_dirty_for_io(page);
|
||||
/*
|
||||
* We have to be very careful here! Nothing protects writeback path
|
||||
* against i_size changes and the page can be writeably mapped into
|
||||
* page tables. So an application can be growing i_size and writing
|
||||
* data through mmap while writeback runs. clear_page_dirty_for_io()
|
||||
* write-protects our page in page tables and the page cannot get
|
||||
* written to again until we release page lock. So only after
|
||||
* clear_page_dirty_for_io() we are safe to sample i_size for
|
||||
* ext4_bio_write_page() to zero-out tail of the written page. We rely
|
||||
* on the barrier provided by TestClearPageDirty in
|
||||
* clear_page_dirty_for_io() to make sure i_size is really sampled only
|
||||
* after page tables are updated.
|
||||
*/
|
||||
size = i_size_read(mpd->inode);
|
||||
if (page->index == size >> PAGE_SHIFT)
|
||||
len = size & ~PAGE_MASK;
|
||||
else
|
||||
len = PAGE_SIZE;
|
||||
clear_page_dirty_for_io(page);
|
||||
err = ext4_bio_write_page(&mpd->io_submit, page, len, mpd->wbc, false);
|
||||
if (!err)
|
||||
mpd->wbc->nr_to_write--;
|
||||
|
Loading…
Reference in New Issue
Block a user