Re: [PATCH] mm/page_alloc: free allocated PFNs if the range does not match

From: Zi Yan

Date: Mon Jun 29 2026 - 22:24:28 EST


On Mon Jun 29, 2026 at 10:06 PM EDT, Andrew Morton wrote:
> On Mon, 29 Jun 2026 21:35:33 -0400 Zi Yan <ziy@xxxxxxxxxx> wrote:
>
>> When using __GFP_COMP in alloc_contig_frozen_range(), if the allocated
>> range does not match the requested one, the code errors out with EINVAL
>> without freeing the allocated PFNs and causes free page leaks. Fix it by
>> calling release_free_list() in the error path.
>>
>> The issue is reported by Sashiko[1].
>>
>> --- a/mm/compaction.c
>> +++ b/mm/compaction.c
>> @@ -88,7 +88,7 @@ static struct page *mark_allocated_noprof(struct page *page, unsigned int order,
>> }
>> #define mark_allocated(...) alloc_hooks(mark_allocated_noprof(__VA_ARGS__))
>>
>> -static unsigned long release_free_list(struct list_head *freepages)
>> +unsigned long release_free_list(struct list_head *freepages)
>> {
>> int order;
>> unsigned long high_pfn = 0;
>>
>> ...
>>
>> --- a/mm/page_alloc.c
>> +++ b/mm/page_alloc.c
>> @@ -7235,9 +7235,11 @@ int alloc_contig_frozen_range_noprof(unsigned long start, unsigned long end,
>> check_new_pages(head, order);
>> prep_new_page(head, order, gfp_mask, 0);
>> } else {
>> + release_free_list(cc.freepages);
>
> I wonder if there's a Kconfig combination which results in this being
> undefined.
>
> I couldn't immediately find such a combination. No doubt we'll be told
> if there is one ;)

I asked Codex about this and think it is OK, since release_free_list()
is defined in CONGIF_COMPACTION || CONFIG_CMA and
alloc_contig_frozen_range() is compiled inside CONFIG_CONTIG_ALLOC,
which is def_bool (MEMORY_ISOLATION && COMPACTION) || CMA.

In addition, isolate_freepages_range() above is defined in the same
Kconfig condition as release_free_list() and there is no issue, so the
use of release_free_list() should be fine here.

Hmm, I think the fixup below places release_free_list() delcaration in a
better location, sitting next to isolate_freepages_range().

I will wait for feedbacks and send v2 with the fixup later.

diff --git a/mm/internal.h b/mm/internal.h
index 6f9e5c2a6065..764fdc0d7cbf 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -821,7 +821,6 @@ static inline void clear_zone_contiguous(struct zone *zone)
}

extern int __isolate_free_page(struct page *page, unsigned int order);
-extern unsigned long release_free_list(struct list_head *freepages);
extern void __putback_isolated_page(struct page *page, unsigned int order,
int mt);
extern void memblock_free_pages(unsigned long pfn, unsigned int order);
@@ -1067,6 +1066,7 @@ struct capture_control {
unsigned long
isolate_freepages_range(struct compact_control *cc,
unsigned long start_pfn, unsigned long end_pfn);
+unsigned long release_free_list(struct list_head *freepages);
int
isolate_migratepages_range(struct compact_control *cc,
unsigned long low_pfn, unsigned long end_pfn);
--
Best Regards,
Yan, Zi