Skip to content

Commit 4462b32

Browse files
tehcastertorvalds
authored andcommitted
mm, page_alloc: more extensive free page checking with debug_pagealloc
The page allocator checks struct pages for expected state (mapcount, flags etc) as pages are being allocated (check_new_page()) and freed (free_pages_check()) to provide some defense against errors in page allocator users. Prior commits 479f854 ("mm, page_alloc: defer debugging checks of pages allocated from the PCP") and 4db7548 ("mm, page_alloc: defer debugging checks of freed pages until a PCP drain") this has happened for order-0 pages as they were allocated from or freed to the per-cpu caches (pcplists). Since those are fast paths, the checks are now performed only when pages are moved between pcplists and global free lists. This however lowers the chances of catching errors soon enough. In order to increase the chances of the checks to catch errors, the kernel has to be rebuilt with CONFIG_DEBUG_VM, which also enables multiple other internal debug checks (VM_BUG_ON() etc), which is suboptimal when the goal is to catch errors in mm users, not in mm code itself. To catch some wrong users of the page allocator we have CONFIG_DEBUG_PAGEALLOC, which is designed to have virtually no overhead unless enabled at boot time. Memory corruptions when writing to freed pages have often the same underlying errors (use-after-free, double free) as corrupting the corresponding struct pages, so this existing debugging functionality is a good fit to extend by also perform struct page checks at least as often as if CONFIG_DEBUG_VM was enabled. Specifically, after this patch, when debug_pagealloc is enabled on boot, and CONFIG_DEBUG_VM disabled, pages are checked when allocated from or freed to the pcplists *in addition* to being moved between pcplists and free lists. When both debug_pagealloc and CONFIG_DEBUG_VM are enabled, pages are checked when being moved between pcplists and free lists *in addition* to when allocated from or freed to the pcplists. When debug_pagealloc is not enabled on boot, the overhead in fast paths should be virtually none thanks to the use of static key. Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Vlastimil Babka <[email protected]> Reviewed-by: Andrew Morton <[email protected]> Cc: Mel Gorman <[email protected]> Cc: Joonsoo Kim <[email protected]> Cc: "Kirill A. Shutemov" <[email protected]> Cc: Matthew Wilcox <[email protected]> Cc: Michal Hocko <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 96a2b03 commit 4462b32

File tree

2 files changed

+52
-14
lines changed

2 files changed

+52
-14
lines changed

mm/Kconfig.debug

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,17 @@ config DEBUG_PAGEALLOC
1919
Depending on runtime enablement, this results in a small or large
2020
slowdown, but helps to find certain types of memory corruption.
2121

22+
Also, the state of page tracking structures is checked more often as
23+
pages are being allocated and freed, as unexpected state changes
24+
often happen for same reasons as memory corruption (e.g. double free,
25+
use-after-free).
26+
2227
For architectures which don't enable ARCH_SUPPORTS_DEBUG_PAGEALLOC,
2328
fill the pages with poison patterns after free_pages() and verify
24-
the patterns before alloc_pages(). Additionally,
25-
this option cannot be enabled in combination with hibernation as
26-
that would result in incorrect warnings of memory corruption after
27-
a resume because free pages are not saved to the suspend image.
29+
the patterns before alloc_pages(). Additionally, this option cannot
30+
be enabled in combination with hibernation as that would result in
31+
incorrect warnings of memory corruption after a resume because free
32+
pages are not saved to the suspend image.
2833

2934
By default this option will have a small overhead, e.g. by not
3035
allowing the kernel mapping to be backed by large pages on some

mm/page_alloc.c

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,19 +1160,36 @@ static __always_inline bool free_pages_prepare(struct page *page,
11601160
}
11611161

11621162
#ifdef CONFIG_DEBUG_VM
1163-
static inline bool free_pcp_prepare(struct page *page)
1163+
/*
1164+
* With DEBUG_VM enabled, order-0 pages are checked immediately when being freed
1165+
* to pcp lists. With debug_pagealloc also enabled, they are also rechecked when
1166+
* moved from pcp lists to free lists.
1167+
*/
1168+
static bool free_pcp_prepare(struct page *page)
11641169
{
11651170
return free_pages_prepare(page, 0, true);
11661171
}
11671172

1168-
static inline bool bulkfree_pcp_prepare(struct page *page)
1173+
static bool bulkfree_pcp_prepare(struct page *page)
11691174
{
1170-
return false;
1175+
if (debug_pagealloc_enabled())
1176+
return free_pages_check(page);
1177+
else
1178+
return false;
11711179
}
11721180
#else
1181+
/*
1182+
* With DEBUG_VM disabled, order-0 pages being freed are checked only when
1183+
* moving from pcp lists to free list in order to reduce overhead. With
1184+
* debug_pagealloc enabled, they are checked also immediately when being freed
1185+
* to the pcp lists.
1186+
*/
11731187
static bool free_pcp_prepare(struct page *page)
11741188
{
1175-
return free_pages_prepare(page, 0, false);
1189+
if (debug_pagealloc_enabled())
1190+
return free_pages_prepare(page, 0, true);
1191+
else
1192+
return free_pages_prepare(page, 0, false);
11761193
}
11771194

11781195
static bool bulkfree_pcp_prepare(struct page *page)
@@ -2035,23 +2052,39 @@ static inline bool free_pages_prezeroed(void)
20352052
}
20362053

20372054
#ifdef CONFIG_DEBUG_VM
2038-
static bool check_pcp_refill(struct page *page)
2055+
/*
2056+
* With DEBUG_VM enabled, order-0 pages are checked for expected state when
2057+
* being allocated from pcp lists. With debug_pagealloc also enabled, they are
2058+
* also checked when pcp lists are refilled from the free lists.
2059+
*/
2060+
static inline bool check_pcp_refill(struct page *page)
20392061
{
2040-
return false;
2062+
if (debug_pagealloc_enabled())
2063+
return check_new_page(page);
2064+
else
2065+
return false;
20412066
}
20422067

2043-
static bool check_new_pcp(struct page *page)
2068+
static inline bool check_new_pcp(struct page *page)
20442069
{
20452070
return check_new_page(page);
20462071
}
20472072
#else
2048-
static bool check_pcp_refill(struct page *page)
2073+
/*
2074+
* With DEBUG_VM disabled, free order-0 pages are checked for expected state
2075+
* when pcp lists are being refilled from the free lists. With debug_pagealloc
2076+
* enabled, they are also checked when being allocated from the pcp lists.
2077+
*/
2078+
static inline bool check_pcp_refill(struct page *page)
20492079
{
20502080
return check_new_page(page);
20512081
}
2052-
static bool check_new_pcp(struct page *page)
2082+
static inline bool check_new_pcp(struct page *page)
20532083
{
2054-
return false;
2084+
if (debug_pagealloc_enabled())
2085+
return check_new_page(page);
2086+
else
2087+
return false;
20552088
}
20562089
#endif /* CONFIG_DEBUG_VM */
20572090

0 commit comments

Comments
 (0)