| From: Matthew Wilcox <mawilcox@microsoft.com> |
| Date: Fri, 20 Apr 2018 14:56:20 -0700 |
| Subject: mm/filemap.c: fix NULL pointer in page_cache_tree_insert() |
| |
| commit abc1be13fd113ddef5e2d807a466286b864caed3 upstream. |
| |
| f2fs specifies the __GFP_ZERO flag for allocating some of its pages. |
| Unfortunately, the page cache also uses the mapping's GFP flags for |
| allocating radix tree nodes. It always masked off the __GFP_HIGHMEM |
| flag, and masks off __GFP_ZERO in some paths, but not all. That causes |
| radix tree nodes to be allocated with a NULL list_head, which causes |
| backtraces like: |
| |
| __list_del_entry+0x30/0xd0 |
| list_lru_del+0xac/0x1ac |
| page_cache_tree_insert+0xd8/0x110 |
| |
| The __GFP_DMA and __GFP_DMA32 flags would also be able to sneak through |
| if they are ever used. Fix them all by using GFP_RECLAIM_MASK at the |
| innermost location, and remove it from earlier in the callchain. |
| |
| Link: http://lkml.kernel.org/r/20180411060320.14458-2-willy@infradead.org |
| Fixes: 449dd6984d0e ("mm: keep page cache radix tree nodes in check") |
| Signed-off-by: Matthew Wilcox <mawilcox@microsoft.com> |
| Reported-by: Chris Fries <cfries@google.com> |
| Debugged-by: Minchan Kim <minchan@kernel.org> |
| Acked-by: Johannes Weiner <hannes@cmpxchg.org> |
| Acked-by: Michal Hocko <mhocko@suse.com> |
| Reviewed-by: Jan Kara <jack@suse.cz> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| [bwh: Backported to 3.16: |
| - Drop change in page_cache_read(), which always passes GFP_KERNEL |
| - Adjust context] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| --- a/mm/filemap.c |
| +++ b/mm/filemap.c |
| @@ -529,7 +529,7 @@ int replace_page_cache_page(struct page |
| VM_BUG_ON_PAGE(!PageLocked(new), new); |
| VM_BUG_ON_PAGE(new->mapping, new); |
| |
| - error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM); |
| + error = radix_tree_preload(gfp_mask & GFP_RECLAIM_MASK); |
| if (!error) { |
| struct address_space *mapping = old->mapping; |
| void (*freepage)(struct page *); |
| @@ -576,7 +576,7 @@ static int __add_to_page_cache_locked(st |
| if (error) |
| return error; |
| |
| - error = radix_tree_maybe_preload(gfp_mask & ~__GFP_HIGHMEM); |
| + error = radix_tree_maybe_preload(gfp_mask & GFP_RECLAIM_MASK); |
| if (error) { |
| mem_cgroup_uncharge_cache_page(page); |
| return error; |
| @@ -1111,8 +1111,7 @@ no_page: |
| if (fgp_flags & FGP_ACCESSED) |
| init_page_accessed(page); |
| |
| - err = add_to_page_cache_lru(page, mapping, offset, |
| - gfp_mask & GFP_RECLAIM_MASK); |
| + err = add_to_page_cache_lru(page, mapping, offset, gfp_mask); |
| if (unlikely(err)) { |
| page_cache_release(page); |
| page = NULL; |