Skip to content

Commit b8de957

Browse files
committed
memblock: make free_reserved_area() more robust
There are two potential problems in free_reserved_area(): * it may free a page with not-existent buddy page * it may be passed a virtual address from an alias mapping that won't be properly translated by virt_to_page(), for example a symbol on arm64 While first issue is quite theoretical and the second one does not manifest itself because all the callers do the right thing, it is easy to make free_reserved_area() robust enough to avoid these potential issues. Replace the loop by virtual address with a loop by pfn that uses for_each_valid_pfn() and use __pa() or __pa_symbol() depending on the virtual mapping alias to correctly determine the loop boundaries. Link: https://patch.msgid.link/20260323074836.3653702-6-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
1 parent 0510bda commit b8de957

1 file changed

Lines changed: 23 additions & 11 deletions

File tree

mm/memblock.c

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -896,21 +896,32 @@ int __init_memblock memblock_remove(phys_addr_t base, phys_addr_t size)
896896

897897
unsigned long free_reserved_area(void *start, void *end, int poison, const char *s)
898898
{
899-
void *pos;
900-
unsigned long pages = 0;
899+
phys_addr_t start_pa, end_pa;
900+
unsigned long pages = 0, pfn;
901901

902-
start = (void *)PAGE_ALIGN((unsigned long)start);
903-
end = (void *)((unsigned long)end & PAGE_MASK);
904-
for (pos = start; pos < end; pos += PAGE_SIZE, pages++) {
905-
struct page *page = virt_to_page(pos);
902+
/*
903+
* end is the first address past the region and it may be beyond what
904+
* __pa() or __pa_symbol() can handle.
905+
* Use the address included in the range for the conversion and add
906+
* back 1 afterwards.
907+
*/
908+
if (__is_kernel((unsigned long)start)) {
909+
start_pa = __pa_symbol(start);
910+
end_pa = __pa_symbol(end - 1) + 1;
911+
} else {
912+
start_pa = __pa(start);
913+
end_pa = __pa(end - 1) + 1;
914+
}
915+
916+
for_each_valid_pfn(pfn, PFN_UP(start_pa), PFN_DOWN(end_pa)) {
917+
struct page *page = pfn_to_page(pfn);
906918
void *direct_map_addr;
907919

908920
/*
909-
* 'direct_map_addr' might be different from 'pos'
910-
* because some architectures' virt_to_page()
911-
* work with aliases. Getting the direct map
912-
* address ensures that we get a _writeable_
913-
* alias for the memset().
921+
* 'direct_map_addr' might be different from the kernel virtual
922+
* address because some architectures use aliases.
923+
* Going via physical address, pfn_to_page() and page_address()
924+
* ensures that we get a _writeable_ alias for the memset().
914925
*/
915926
direct_map_addr = page_address(page);
916927
/*
@@ -922,6 +933,7 @@ unsigned long free_reserved_area(void *start, void *end, int poison, const char
922933
memset(direct_map_addr, poison, PAGE_SIZE);
923934

924935
free_reserved_page(page);
936+
pages++;
925937
}
926938

927939
if (pages && s)

0 commit comments

Comments
 (0)