Use-after-free and double-free bugs can be very difficult to track down. The kernel is good at tracking these and preventing bad pages from being used/created through simple checks gated behind "check_pages_enabled". Currently, the only ways to enable this flag is by building with CONFIG_DEBUG_VM, or as a side effect of other checks such as init_on_{alloc, free}, page_poisoning, or debug_pagealloc among others. These solutions are powerful, but may often be too coarse in balancing the performance vs. safety that a user may want, particularly in latency-sensitie production environments. Introduce CONFIG_DEBUG_CHECK_PAGES, which sets is_check_pages_enabled with no other side effects. Setting CONFIG_DEBUG_VM automatically enables this as well as to have backwards compatibility. Developed on top of 7f1dae318f81e508ef59835bc82bdf33e4cb1021 "mm: swap: remove scan_swap_map_slots() references from comments" of mm-new. Signed-off-by: Joshua Hahn --- mm/Kconfig.debug | 12 ++++++++++++ mm/internal.h | 2 +- mm/mm_init.c | 8 ++++---- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/mm/Kconfig.debug b/mm/Kconfig.debug index 32b65073d0cc..366abde25026 100644 --- a/mm/Kconfig.debug +++ b/mm/Kconfig.debug @@ -45,6 +45,18 @@ config DEBUG_PAGEALLOC_ENABLE_DEFAULT Enable debug page memory allocations by default? This value can be overridden by debug_pagealloc=off|on. +config DEBUG_CHECK_PAGES + bool "Debug VM page allocation/free sanity checks" + depends on DEBUG_KERNEL + default y if DEBUG_VM + help + Enable sanity checking of pages after allocations / before freeing. + This adds checks to catch double-frees, use-after-frees, and other + sources of page corruption by inspecting page internals (flags, + mapcount/refcount, memcg_data, etc.). + + This is automatically enabled if CONFIG_DEBUG_VM is set. + config SLUB_DEBUG default y bool "Enable SLUB debugging support" if EXPERT diff --git a/mm/internal.h b/mm/internal.h index 04c307ee33ae..b8decdfc0930 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -562,7 +562,7 @@ pmd_t *mm_find_pmd(struct mm_struct *mm, unsigned long address); extern char * const zone_names[MAX_NR_ZONES]; /* perform sanity checks on struct pages being allocated or freed */ -DECLARE_STATIC_KEY_MAYBE(CONFIG_DEBUG_VM, check_pages_enabled); +DECLARE_STATIC_KEY_MAYBE(CONFIG_DEBUG_CHECK_PAGES, check_pages_enabled); extern int min_free_kbytes; extern int defrag_mode; diff --git a/mm/mm_init.c b/mm/mm_init.c index c6812b4dbb2e..7f47b22864dd 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -2523,7 +2523,7 @@ static int __init early_init_on_free(char *buf) } early_param("init_on_free", early_init_on_free); -DEFINE_STATIC_KEY_MAYBE(CONFIG_DEBUG_VM, check_pages_enabled); +DEFINE_STATIC_KEY_MAYBE(CONFIG_DEBUG_CHECK_PAGES, check_pages_enabled); /* * Enable static keys related to various memory debugging and hardening options. @@ -2588,10 +2588,10 @@ static void __init mem_debugging_and_hardening_init(void) /* * Any page debugging or hardening option also enables sanity checking - * of struct pages being allocated or freed. With CONFIG_DEBUG_VM it's - * enabled already. + * of struct pages being allocated or freed. With CONFIG_DEBUG_VM or + * CONFIG_DEBUG_CHECK_PAGES it's enabled already. */ - if (!IS_ENABLED(CONFIG_DEBUG_VM) && want_check_pages) + if (!IS_ENABLED(CONFIG_DEBUG_CHECK_PAGES) && want_check_pages) static_branch_enable(&check_pages_enabled); } base-commit: 7f1dae318f81e508ef59835bc82bdf33e4cb1021 -- 2.47.3