diff options
author | Jan Kara | 2017-05-26 16:45:45 -0500 |
---|---|---|
committer | Greg Kroah-Hartman | 2017-10-12 04:27:35 -0500 |
commit | 40c00e5fac3abbd5e6fa08e93fa99b3e632ece16 (patch) | |
tree | 82575b3f77809c7c87e42444d2fb95e6d6622a21 | |
parent | 90fd6738731b6d105fc8f04832ae17a9ac82c05c (diff) | |
download | ti-linux-kernel-40c00e5fac3abbd5e6fa08e93fa99b3e632ece16.tar.gz ti-linux-kernel-40c00e5fac3abbd5e6fa08e93fa99b3e632ece16.tar.xz ti-linux-kernel-40c00e5fac3abbd5e6fa08e93fa99b3e632ece16.zip |
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: 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 1796d1bd9a1d..194a6baa4283 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -1946,15 +1946,29 @@ static int ext4_writepage(struct page *page, | |||
1946 | static int mpage_submit_page(struct mpage_da_data *mpd, struct page *page) | 1946 | static int mpage_submit_page(struct mpage_da_data *mpd, struct page *page) |
1947 | { | 1947 | { |
1948 | int len; | 1948 | int len; |
1949 | loff_t size = i_size_read(mpd->inode); | 1949 | loff_t size; |
1950 | int err; | 1950 | int err; |
1951 | 1951 | ||
1952 | BUG_ON(page->index != mpd->first_page); | 1952 | BUG_ON(page->index != mpd->first_page); |
1953 | if (page->index == size >> PAGE_CACHE_SHIFT) | ||
1954 | len = size & ~PAGE_CACHE_MASK; | ||
1955 | else | ||
1956 | len = PAGE_CACHE_SIZE; | ||
1957 | clear_page_dirty_for_io(page); | 1953 | clear_page_dirty_for_io(page); |
1954 | /* | ||
1955 | * We have to be very careful here! Nothing protects writeback path | ||
1956 | * against i_size changes and the page can be writeably mapped into | ||
1957 | * page tables. So an application can be growing i_size and writing | ||
1958 | * data through mmap while writeback runs. clear_page_dirty_for_io() | ||
1959 | * write-protects our page in page tables and the page cannot get | ||
1960 | * written to again until we release page lock. So only after | ||
1961 | * clear_page_dirty_for_io() we are safe to sample i_size for | ||
1962 | * ext4_bio_write_page() to zero-out tail of the written page. We rely | ||
1963 | * on the barrier provided by TestClearPageDirty in | ||
1964 | * clear_page_dirty_for_io() to make sure i_size is really sampled only | ||
1965 | * after page tables are updated. | ||
1966 | */ | ||
1967 | size = i_size_read(mpd->inode); | ||
1968 | if (page->index == size >> PAGE_SHIFT) | ||
1969 | len = size & ~PAGE_MASK; | ||
1970 | else | ||
1971 | len = PAGE_SIZE; | ||
1958 | err = ext4_bio_write_page(&mpd->io_submit, page, len, mpd->wbc, false); | 1972 | err = ext4_bio_write_page(&mpd->io_submit, page, len, mpd->wbc, false); |
1959 | if (!err) | 1973 | if (!err) |
1960 | mpd->wbc->nr_to_write--; | 1974 | mpd->wbc->nr_to_write--; |