Skip to content

Commit 8974558

Browse files
tehcastertorvalds
authored andcommitted
mm, page_owner, debug_pagealloc: save and dump freeing stack trace
The debug_pagealloc functionality is useful to catch buggy page allocator users that cause e.g. use after free or double free. When page inconsistency is detected, debugging is often simpler by knowing the call stack of process that last allocated and freed the page. When page_owner is also enabled, we record the allocation stack trace, but not freeing. This patch therefore adds recording of freeing process stack trace to page owner info, if both page_owner and debug_pagealloc are configured and enabled. With only page_owner enabled, this info is not useful for the memory leak debugging use case. dump_page() is adjusted to print the info. An example result of calling __free_pages() twice may look like this (note the page last free stack trace): BUG: Bad page state in process bash pfn:13d8f8 page:ffffc31984f63e00 refcount:-1 mapcount:0 mapping:0000000000000000 index:0x0 flags: 0x1affff800000000() raw: 01affff800000000 dead000000000100 dead000000000122 0000000000000000 raw: 0000000000000000 0000000000000000 ffffffffffffffff 0000000000000000 page dumped because: nonzero _refcount page_owner tracks the page as freed page last allocated via order 0, migratetype Unmovable, gfp_mask 0xcc0(GFP_KERNEL) prep_new_page+0x143/0x150 get_page_from_freelist+0x289/0x380 __alloc_pages_nodemask+0x13c/0x2d0 khugepaged+0x6e/0xc10 kthread+0xf9/0x130 ret_from_fork+0x3a/0x50 page last free stack trace: free_pcp_prepare+0x134/0x1e0 free_unref_page+0x18/0x90 khugepaged+0x7b/0xc10 kthread+0xf9/0x130 ret_from_fork+0x3a/0x50 Modules linked in: CPU: 3 PID: 271 Comm: bash Not tainted 5.3.0-rc4-2.g07a1a73-default+ raspberrypi#57 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.1-0-ga5cab58-prebuilt.qemu.org 04/01/2014 Call Trace: dump_stack+0x85/0xc0 bad_page.cold+0xba/0xbf rmqueue_pcplist.isra.0+0x6c5/0x6d0 rmqueue+0x2d/0x810 get_page_from_freelist+0x191/0x380 __alloc_pages_nodemask+0x13c/0x2d0 __get_free_pages+0xd/0x30 __pud_alloc+0x2c/0x110 copy_page_range+0x4f9/0x630 dup_mmap+0x362/0x480 dup_mm+0x68/0x110 copy_process+0x19e1/0x1b40 _do_fork+0x73/0x310 __x64_sys_clone+0x75/0x80 do_syscall_64+0x6e/0x1e0 entry_SYSCALL_64_after_hwframe+0x49/0xbe RIP: 0033:0x7f10af854a10 ... Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Vlastimil Babka <[email protected]> Cc: Kirill A. Shutemov <[email protected]> Cc: Matthew Wilcox <[email protected]> Cc: Mel Gorman <[email protected]> Cc: Michal Hocko <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 3738916 commit 8974558

File tree

3 files changed

+45
-14
lines changed

3 files changed

+45
-14
lines changed

Documentation/admin-guide/kernel-parameters.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,8 @@
809809
enables the feature at boot time. By default, it is
810810
disabled and the system will work mostly the same as a
811811
kernel built without CONFIG_DEBUG_PAGEALLOC.
812+
Note: to get most of debug_pagealloc error reports, it's
813+
useful to also enable the page_owner functionality.
812814
on: enable the feature
813815

814816
debugpat [X86] Enable PAT debugging

mm/Kconfig.debug

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ config DEBUG_PAGEALLOC
2121
Also, the state of page tracking structures is checked more often as
2222
pages are being allocated and freed, as unexpected state changes
2323
often happen for same reasons as memory corruption (e.g. double free,
24-
use-after-free).
24+
use-after-free). The error reports for these checks can be augmented
25+
with stack traces of last allocation and freeing of the page, when
26+
PAGE_OWNER is also selected and enabled on boot.
2527

2628
For architectures which don't enable ARCH_SUPPORTS_DEBUG_PAGEALLOC,
2729
fill the pages with poison patterns after free_pages() and verify

mm/page_owner.c

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ struct page_owner {
2424
short last_migrate_reason;
2525
gfp_t gfp_mask;
2626
depot_stack_handle_t handle;
27+
#ifdef CONFIG_DEBUG_PAGEALLOC
28+
depot_stack_handle_t free_handle;
29+
#endif
2730
};
2831

2932
static bool page_owner_disabled = true;
@@ -102,19 +105,6 @@ static inline struct page_owner *get_page_owner(struct page_ext *page_ext)
102105
return (void *)page_ext + page_owner_ops.offset;
103106
}
104107

105-
void __reset_page_owner(struct page *page, unsigned int order)
106-
{
107-
int i;
108-
struct page_ext *page_ext;
109-
110-
for (i = 0; i < (1 << order); i++) {
111-
page_ext = lookup_page_ext(page + i);
112-
if (unlikely(!page_ext))
113-
continue;
114-
__clear_bit(PAGE_EXT_OWNER_ACTIVE, &page_ext->flags);
115-
}
116-
}
117-
118108
static inline bool check_recursive_alloc(unsigned long *entries,
119109
unsigned int nr_entries,
120110
unsigned long ip)
@@ -154,6 +144,32 @@ static noinline depot_stack_handle_t save_stack(gfp_t flags)
154144
return handle;
155145
}
156146

147+
void __reset_page_owner(struct page *page, unsigned int order)
148+
{
149+
int i;
150+
struct page_ext *page_ext;
151+
#ifdef CONFIG_DEBUG_PAGEALLOC
152+
depot_stack_handle_t handle = 0;
153+
struct page_owner *page_owner;
154+
155+
if (debug_pagealloc_enabled())
156+
handle = save_stack(GFP_NOWAIT | __GFP_NOWARN);
157+
#endif
158+
159+
for (i = 0; i < (1 << order); i++) {
160+
page_ext = lookup_page_ext(page + i);
161+
if (unlikely(!page_ext))
162+
continue;
163+
__clear_bit(PAGE_EXT_OWNER_ACTIVE, &page_ext->flags);
164+
#ifdef CONFIG_DEBUG_PAGEALLOC
165+
if (debug_pagealloc_enabled()) {
166+
page_owner = get_page_owner(page_ext);
167+
page_owner->free_handle = handle;
168+
}
169+
#endif
170+
}
171+
}
172+
157173
static inline void __set_page_owner_handle(struct page *page,
158174
struct page_ext *page_ext, depot_stack_handle_t handle,
159175
unsigned int order, gfp_t gfp_mask)
@@ -435,6 +451,17 @@ void __dump_page_owner(struct page *page)
435451
stack_trace_print(entries, nr_entries, 0);
436452
}
437453

454+
#ifdef CONFIG_DEBUG_PAGEALLOC
455+
handle = READ_ONCE(page_owner->free_handle);
456+
if (!handle) {
457+
pr_alert("page_owner free stack trace missing\n");
458+
} else {
459+
nr_entries = stack_depot_fetch(handle, &entries);
460+
pr_alert("page last free stack trace:\n");
461+
stack_trace_print(entries, nr_entries, 0);
462+
}
463+
#endif
464+
438465
if (page_owner->last_migrate_reason != -1)
439466
pr_alert("page has been migrated, last migrate reason: %s\n",
440467
migrate_reason_names[page_owner->last_migrate_reason]);

0 commit comments

Comments
 (0)