From: yangerkun yangerkun@huawei.com
hulk inclusion category: bugfix bugzilla: 27600 CVE: NA ---------------------------
dio_bio_complete will set page dirty without consider is there still buffer_head valid with this page. This will trigger some problem while ext4 try to writeback this page. For ext4, we fix it by skip writeback the page without buffer_head.
[1] https://lwn.net/Articles/774411/ : "DMA and get_user_pages()" [2] https://lwn.net/Articles/753027/ : "The Trouble with get_user_pages()" [3] https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=...
Signed-off-by: yangerkun yangerkun@huawei.com Reviewed-by: zhangyi (F) yi.zhang@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- fs/ext4/inode.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 5abbb915f4e3..be8e89720735 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -2075,6 +2075,20 @@ static int __ext4_journalled_writepage(struct page *page, return ret; }
+static void cancel_page_dirty_status(struct page *page) +{ + struct address_space *mapping = page_mapping(page); + unsigned long flags; + + cancel_dirty_page(page); + xa_lock_irqsave(&mapping->i_pages, flags); + radix_tree_tag_clear(&mapping->i_pages, page_index(page), + PAGECACHE_TAG_DIRTY); + radix_tree_tag_clear(&mapping->i_pages, page_index(page), + PAGECACHE_TAG_TOWRITE); + xa_unlock_irqrestore(&mapping->i_pages, flags); +} + /* * Note that we don't need to start a transaction unless we're journaling data * because we should have holes filled from ext4_page_mkwrite(). We even don't @@ -2133,6 +2147,12 @@ static int ext4_writepage(struct page *page, return -EIO; }
+ if (WARN_ON(!page_has_buffers(page))) { + cancel_page_dirty_status(page); + unlock_page(page); + return 0; + } + trace_ext4_writepage(page); size = i_size_read(inode); if (page->index == size >> PAGE_SHIFT) @@ -2686,6 +2706,12 @@ static int mpage_prepare_extent_to_map(struct mpage_da_data *mpd) continue; }
+ if (WARN_ON(!page_has_buffers(page))) { + cancel_page_dirty_status(page); + unlock_page(page); + continue; + } + wait_on_page_writeback(page); BUG_ON(PageWriteback(page));