| From 24d4accb8414e41bf2cc16ef8bc1415040da79aa Mon Sep 17 00:00:00 2001 |
| From: Minchan Kim <minchan@kernel.org> |
| Date: Fri, 24 Feb 2017 14:59:59 -0800 |
| Subject: [PATCH] mm: do not access page->mapping directly on page_endio |
| |
| commit dd8416c47715cf324c9a16f13273f9fda87acfed upstream. |
| |
| With rw_page, page_endio is used for completing IO on a page and it |
| propagates write error to the address space if the IO fails. The |
| problem is it accesses page->mapping directly which might be okay for |
| file-backed pages but it shouldn't for anonymous page. Otherwise, it |
| can corrupt one of field from anon_vma under us and system goes panic |
| randomly. |
| |
| swap_writepage |
| bdev_writepage |
| ops->rw_page |
| |
| I encountered the BUG during developing new zram feature and it was |
| really hard to figure it out because it made random crash, somtime |
| mmap_sem lockdep, sometime other places where places never related to |
| zram/zsmalloc, and not reproducible with some configuration. |
| |
| When I consider how that bug is subtle and people do fast-swap test with |
| brd, it's worth to add stable mark, I think. |
| |
| Fixes: dd6bd0d9c7db ("swap: use bdev_read_page() / bdev_write_page()") |
| Signed-off-by: Minchan Kim <minchan@kernel.org> |
| Acked-by: Michal Hocko <mhocko@suse.com> |
| Cc: Matthew Wilcox <willy@infradead.org> |
| Cc: <stable@vger.kernel.org> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/mm/filemap.c b/mm/filemap.c |
| index 97c5671ee082..d95439feca0d 100644 |
| --- a/mm/filemap.c |
| +++ b/mm/filemap.c |
| @@ -912,9 +912,12 @@ void page_endio(struct page *page, bool is_write, int err) |
| unlock_page(page); |
| } else { |
| if (err) { |
| + struct address_space *mapping; |
| + |
| SetPageError(page); |
| - if (page->mapping) |
| - mapping_set_error(page->mapping, err); |
| + mapping = page_mapping(page); |
| + if (mapping) |
| + mapping_set_error(mapping, err); |
| } |
| end_page_writeback(page); |
| } |
| -- |
| 2.12.0 |
| |