If a compound page is being split while dump_page() is being run on that page, we can end up calling compound_mapcount() on a page that is no longer compound. This leads to a crash (already seen at least once in the field), due to the VM_BUG_ON_PAGE() assertion inside compound_mapcount(). (The above is from Matthew Wilcox's analysis of Qian Cai's bug report.) In order to avoid this kind of crash, make dump_page() slightly more robust, by providing a version of compound_page() that doesn't assert, but just warns the first time that such a thing happens. And the first time is usually enough. For debug tools, we don't want to go *too* far in this direction, but this is a simple small fix, and the crash has already been seen, so it's a good trade-off. Reported-by: Qian Cai <cai@xxxxxx> Cc: Matthew Wilcox <willy@xxxxxxxxxxxxx> Cc: Vlastimil Babka <vbabka@xxxxxxx> Cc: Kirill A. Shutemov <kirill.shutemov@xxxxxxxxxxxxxxx> Signed-off-by: John Hubbard <jhubbard@xxxxxxxxxx> --- include/linux/mm.h | 17 +++++++++++++++-- mm/debug.c | 4 ++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index dc7b87310c10..e3991fbb42c0 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -779,6 +779,13 @@ static inline void *kvcalloc(size_t n, size_t size, gfp_t flags) extern void kvfree(const void *addr); extern void kvfree_sensitive(const void *addr, size_t len); + +static inline int __compound_mapcount(struct page *page) +{ + page = compound_head(page); + return atomic_read(compound_mapcount_ptr(page)) + 1; +} + /* * Mapcount of compound page as a whole, does not include mapped sub-pages. * @@ -787,8 +794,14 @@ extern void kvfree_sensitive(const void *addr, size_t len); static inline int compound_mapcount(struct page *page) { VM_BUG_ON_PAGE(!PageCompound(page), page); - page = compound_head(page); - return atomic_read(compound_mapcount_ptr(page)) + 1; + return __compound_mapcount(page); +} + +static inline int dump_page_compound_mapcount(struct page *page) +{ + if (WARN_ON_ONCE(!PageCompound(page))) + return 0; + return __compound_mapcount(page); } /* diff --git a/mm/debug.c b/mm/debug.c index 4f376514744d..eab4244aabd8 100644 --- a/mm/debug.c +++ b/mm/debug.c @@ -91,7 +91,7 @@ void __dump_page(struct page *page, const char *reason) "compound_mapcount:%d compound_pincount:%d\n", page, page_ref_count(head), mapcount, mapping, page_to_pgoff(page), head, - compound_order(head), compound_mapcount(page), + compound_order(head), dump_page_compound_mapcount(page), compound_pincount(page)); } else { pr_warn("page:%px refcount:%d mapcount:%d mapping:%p " @@ -99,7 +99,7 @@ void __dump_page(struct page *page, const char *reason) "compound_mapcount:%d\n", page, page_ref_count(head), mapcount, mapping, page_to_pgoff(page), head, - compound_order(head), compound_mapcount(page)); + compound_order(head), dump_page_compound_mapcount(page)); } else pr_warn("page:%px refcount:%d mapcount:%d mapping:%p index:%#lx\n", -- 2.28.0