| From 5aab62639a7eb76ec6ca15d18f9df017ba535eee Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 3 Mar 2022 09:38:47 -0500 |
| Subject: ext4: don't BUG if someone dirty pages without asking ext4 first |
| |
| From: Theodore Ts'o <tytso@mit.edu> |
| |
| [ Upstream commit cc5095747edfb054ca2068d01af20be3fcc3634f ] |
| |
| [un]pin_user_pages_remote is dirtying pages without properly warning |
| the file system in advance. A related race was noted by Jan Kara in |
| 2018[1]; however, more recently instead of it being a very hard-to-hit |
| race, it could be reliably triggered by process_vm_writev(2) which was |
| discovered by Syzbot[2]. |
| |
| This is technically a bug in mm/gup.c, but arguably ext4 is fragile in |
| that if some other kernel subsystem dirty pages without properly |
| notifying the file system using page_mkwrite(), ext4 will BUG, while |
| other file systems will not BUG (although data will still be lost). |
| |
| So instead of crashing with a BUG, issue a warning (since there may be |
| potential data loss) and just mark the page as clean to avoid |
| unprivileged denial of service attacks until the problem can be |
| properly fixed. More discussion and background can be found in the |
| thread starting at [2]. |
| |
| [1] https://lore.kernel.org/linux-mm/20180103100430.GE4911@quack2.suse.cz |
| [2] https://lore.kernel.org/r/Yg0m6IjcNmfaSokM@google.com |
| |
| Reported-by: syzbot+d59332e2db681cf18f0318a06e994ebbb529a8db@syzkaller.appspotmail.com |
| Reported-by: Lee Jones <lee.jones@linaro.org> |
| Signed-off-by: Theodore Ts'o <tytso@mit.edu> |
| Link: https://lore.kernel.org/r/YiDS9wVfq4mM2jGK@mit.edu |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| fs/ext4/inode.c | 25 +++++++++++++++++++++++++ |
| 1 file changed, 25 insertions(+) |
| |
| diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c |
| index 2f5686dfa30d..a61d1e4e1026 100644 |
| --- a/fs/ext4/inode.c |
| +++ b/fs/ext4/inode.c |
| @@ -1992,6 +1992,15 @@ static int ext4_writepage(struct page *page, |
| else |
| len = PAGE_SIZE; |
| |
| + /* Should never happen but for bugs in other kernel subsystems */ |
| + if (!page_has_buffers(page)) { |
| + ext4_warning_inode(inode, |
| + "page %lu does not have buffers attached", page->index); |
| + ClearPageDirty(page); |
| + unlock_page(page); |
| + return 0; |
| + } |
| + |
| page_bufs = page_buffers(page); |
| /* |
| * We cannot do block allocation or other extent handling in this |
| @@ -2595,6 +2604,22 @@ static int mpage_prepare_extent_to_map(struct mpage_da_data *mpd) |
| wait_on_page_writeback(page); |
| BUG_ON(PageWriteback(page)); |
| |
| + /* |
| + * Should never happen but for buggy code in |
| + * other subsystems that call |
| + * set_page_dirty() without properly warning |
| + * the file system first. See [1] for more |
| + * information. |
| + * |
| + * [1] https://lore.kernel.org/linux-mm/20180103100430.GE4911@quack2.suse.cz |
| + */ |
| + if (!page_has_buffers(page)) { |
| + ext4_warning_inode(mpd->inode, "page %lu does not have buffers attached", page->index); |
| + ClearPageDirty(page); |
| + unlock_page(page); |
| + continue; |
| + } |
| + |
| if (mpd->map.m_len == 0) |
| mpd->first_page = page->index; |
| mpd->next_page = page->index + 1; |
| -- |
| 2.34.1 |
| |