diff options
| author | gregkh@linuxfoundation.org <gregkh@linuxfoundation.org> | 2017-10-10 21:50:51 +0200 |
|---|---|---|
| committer | Mister Oyster <oysterized@gmail.com> | 2017-12-10 14:34:39 +0100 |
| commit | 156b66c5bad7fa7621024c2537bfeb48e5a21da0 (patch) | |
| tree | 2f6b8ec0fdfd1e1b3c34fdb6204563d29ab530f3 | |
| parent | d3dc597a6c754da47500866c8ebaa46bda2b5e1d (diff) | |
ext4: fix data corruption for mmap writes
3.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jan Kara <jack@suse.cz>
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: Michael Zimmer <michael@swarm64.com>
CC: stable@vger.kernel.org
Fixes: cb20d5188366f04d96d2e07b1240cc92170ade40
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
| -rw-r--r-- | fs/ext4/inode.c | 24 |
1 files changed, 19 insertions, 5 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 2f6689447..61543a761 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1887,15 +1887,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); - if (page->index == size >> PAGE_CACHE_SHIFT) - len = size & ~PAGE_CACHE_MASK; - else - len = PAGE_CACHE_SIZE; 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); if (!err) mpd->wbc->nr_to_write--; |
