diff options
| author | Mike Rapoport (Microsoft) <rppt@kernel.org> | 2026-03-23 10:48:36 +0300 |
|---|---|---|
| committer | Mike Rapoport (Microsoft) <rppt@kernel.org> | 2026-04-01 11:20:15 +0300 |
| commit | 59bd1d914bb51ab99a33ce32420403ccd035ad29 (patch) | |
| tree | 600acd5d9d3d025edffa1de762f1815a3af7aaaa | |
| parent | 87ce9e83ab8be5daf64351cd481ffa6537778e6b (diff) | |
| download | linux-59bd1d914bb51ab99a33ce32420403ccd035ad29.tar.xz | |
memblock: warn when freeing reserved memory before memory map is initialized
When CONFIG_DEFERRED_STRUCT_PAGE_INIT is enabled, freeing of reserved
memory before the memory map is fully initialized in deferred_init_memmap()
would cause access to uninitialized struct pages and may crash when
accessing spurious list pointers, like was recently discovered during
discussion about memory leaks in x86 EFI code [1].
The trace below is from an attempt to call free_reserved_page() before
page_alloc_init_late():
[ 0.076840] BUG: unable to handle page fault for address: ffffce1a005a0788
[ 0.078226] #PF: supervisor read access in kernel mode
[ 0.078226] #PF: error_code(0x0000) - not-present page
[ 0.078226] PGD 0 P4D 0
[ 0.078226] Oops: Oops: 0000 [#1] PREEMPT SMP NOPTI
[ 0.078226] CPU: 0 UID: 0 PID: 0 Comm: swapper/0 Not tainted 6.12.68-92.123.amzn2023.x86_64 #1
[ 0.078226] Hardware name: Amazon EC2 t3a.nano/, BIOS 1.0 10/16/2017
[ 0.078226] RIP: 0010:__list_del_entry_valid_or_report+0x32/0xb0
...
[ 0.078226] __free_one_page+0x170/0x520
[ 0.078226] free_pcppages_bulk+0x151/0x1e0
[ 0.078226] free_unref_page_commit+0x263/0x320
[ 0.078226] free_unref_page+0x2c8/0x5b0
[ 0.078226] ? srso_return_thunk+0x5/0x5f
[ 0.078226] free_reserved_page+0x1c/0x30
[ 0.078226] memblock_free_late+0x6c/0xc0
Currently there are not many callers of free_reserved_area() and they all
appear to be at the right timings.
Still, in order to protect against problematic code moves or additions of
new callers add a warning that will inform that reserved pages cannot be
freed until the memory map is fully initialized.
[1] https://lore.kernel.org/all/e5d5a1105d90ee1e7fe7eafaed2ed03bbad0c46b.camel@kernel.crashing.org/
Link: https://patch.msgid.link/20260323074836.3653702-10-rppt@kernel.org
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
| -rw-r--r-- | mm/internal.h | 10 | ||||
| -rw-r--r-- | mm/memblock.c | 5 | ||||
| -rw-r--r-- | mm/page_alloc.c | 10 |
3 files changed, 15 insertions, 10 deletions
diff --git a/mm/internal.h b/mm/internal.h index cb0af847d7d9..f60c1edb2e02 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -1233,7 +1233,17 @@ static inline void vunmap_range_noflush(unsigned long start, unsigned long end) #ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT DECLARE_STATIC_KEY_TRUE(deferred_pages); +static inline bool deferred_pages_enabled(void) +{ + return static_branch_unlikely(&deferred_pages); +} + bool __init deferred_grow_zone(struct zone *zone, unsigned int order); +#else +static inline bool deferred_pages_enabled(void) +{ + return false; +} #endif /* CONFIG_DEFERRED_STRUCT_PAGE_INIT */ void init_deferred_page(unsigned long pfn, int nid); diff --git a/mm/memblock.c b/mm/memblock.c index df4e3475fe39..6cf1de7a0dac 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -900,6 +900,11 @@ static unsigned long __free_reserved_area(phys_addr_t start, phys_addr_t end, { unsigned long pages = 0, pfn; + if (deferred_pages_enabled()) { + WARN(1, "Cannot free reserved memory because of deferred initialization of the memory map"); + return 0; + } + for_each_valid_pfn(pfn, PFN_UP(start), PFN_DOWN(end)) { struct page *page = pfn_to_page(pfn); void *direct_map_addr; diff --git a/mm/page_alloc.c b/mm/page_alloc.c index df3d61253001..9ac47bab2ea7 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -331,11 +331,6 @@ int page_group_by_mobility_disabled __read_mostly; */ DEFINE_STATIC_KEY_TRUE(deferred_pages); -static inline bool deferred_pages_enabled(void) -{ - return static_branch_unlikely(&deferred_pages); -} - /* * deferred_grow_zone() is __init, but it is called from * get_page_from_freelist() during early boot until deferred_pages permanently @@ -348,11 +343,6 @@ _deferred_grow_zone(struct zone *zone, unsigned int order) return deferred_grow_zone(zone, order); } #else -static inline bool deferred_pages_enabled(void) -{ - return false; -} - static inline bool _deferred_grow_zone(struct zone *zone, unsigned int order) { return false; |
