Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

+23 -11
+23 -11
mm/memblock.c
··· 896 896 897 897 unsigned long free_reserved_area(void *start, void *end, int poison, const char *s) 898 898 { 899 - void *pos; 900 - unsigned long pages = 0; 899 + phys_addr_t start_pa, end_pa; 900 + unsigned long pages = 0, pfn; 901 901 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); 906 918 void *direct_map_addr; 907 919 908 920 /* 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(). 914 925 */ 915 926 direct_map_addr = page_address(page); 916 927 /* ··· 933 922 memset(direct_map_addr, poison, PAGE_SIZE); 934 923 935 924 free_reserved_page(page); 925 + pages++; 936 926 } 937 927 938 928 if (pages && s)