Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 40c00e5f authored by Jan Kara's avatar Jan Kara Committed by Greg Kroah-Hartman
Browse files

ext4: fix data corruption for mmap writes



commit a056bdaae7a181f7dcc876cfab2f94538e508709 upstream.

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: default avatarMichael Zimmer <michael@swarm64.com>
CC: stable@vger.kernel.org
Fixes: cb20d518
Signed-off-by: default avatarJan Kara <jack@suse.cz>
Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 90fd6738
Loading
Loading
Loading
Loading
+19 −5
Original line number Original line Diff line number Diff line
@@ -1946,15 +1946,29 @@ static int ext4_writepage(struct page *page,
static int mpage_submit_page(struct mpage_da_data *mpd, struct page *page)
static int mpage_submit_page(struct mpage_da_data *mpd, struct page *page)
{
{
	int len;
	int len;
	loff_t size = i_size_read(mpd->inode);
	loff_t size;
	int err;
	int err;


	BUG_ON(page->index != mpd->first_page);
	BUG_ON(page->index != mpd->first_page);
	if (page->index == size >> PAGE_CACHE_SHIFT)
		len = size & ~PAGE_CACHE_MASK;
	else
		len = PAGE_CACHE_SIZE;
	clear_page_dirty_for_io(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;
	err = ext4_bio_write_page(&mpd->io_submit, page, len, mpd->wbc, false);
	err = ext4_bio_write_page(&mpd->io_submit, page, len, mpd->wbc, false);
	if (!err)
	if (!err)
		mpd->wbc->nr_to_write--;
		mpd->wbc->nr_to_write--;