Re: [PATCH] mm/migrate_device: avoid out-of-bounds writes for compound folios

Balbir Singh <[email protected]>
Newsgroups gmane.linux.kernel,gmane.linux.kernel.mm
Message-ID <aoLDQifuyIiO-2eA@parvat>
On Mon, Aug 17, 2026 at 03:43:51PM +0800, Hui Su wrote:
> migrate_device_range() and migrate_device_pfns() clear the entries
> following a compound folio so that the PFN arrays retain their
> page-granular representation.
> 
> If a compound folio extends beyond the end of the caller-provided range,
> the loops clear all following folio entries without limiting them to the
> number of slots remaining in the npages-sized array, causing an
> out-of-bounds write.
> 
> Limit the number of entries updated and consumed to the remaining array
> slots while keeping the actual number of pages in the folio unchanged.
> 
> Observed with a KASAN x86 QEMU kernel using the HMM
> migrate_anon_huge_zero selftest. Closing /dev/hmm_dmirror0 after
> migrating an anonymous huge page to device memory exercises:
> 
>   dmirror_fops_release()
>     -> dmirror_device_evict_chunk()
>       -> migrate_device_range()
> 
> Fixes: a30b48bf1b24 ("mm/migrate_device: implement THP migration of zone device pages")
> Signed-off-by: Hui Su <[email protected]>
> ---
>  mm/migrate_device.c | 16 +++++++++-------
>  1 file changed, 9 insertions(+), 7 deletions(-)
> 
> diff --git a/mm/migrate_device.c b/mm/migrate_device.c
> index 908d2d4ec43a..67bfc1af0b29 100644
> --- a/mm/migrate_device.c
> +++ b/mm/migrate_device.c
> @@ -1396,16 +1396,17 @@ int migrate_device_range(unsigned long *src_pfns, unsigned long start,
>  	for (pfn = start, i = 0; i < npages; pfn++, i++) {
>  		struct page *page = pfn_to_page(pfn);
>  		struct folio *folio = page_folio(page);
> -		unsigned int nr = 1;
> +		unsigned long nr, slots;
>  
>  		src_pfns[i] = migrate_device_pfn_lock(pfn);
>  		nr = folio_nr_pages(folio);
> +		slots = min(nr, npages - i);
>  		if (nr > 1) {
>  			src_pfns[i] |= MIGRATE_PFN_COMPOUND;
> -			for (j = 1; j < nr; j++)
> +			for (j = 1; j < slots; j++)
>  				src_pfns[i+j] = 0;
> -			i += j - 1;
> -			pfn += j - 1;
> +			i += slots - 1;
> +			pfn += slots - 1;
>  		}
>  	}
>  
> @@ -1430,15 +1431,16 @@ int migrate_device_pfns(unsigned long *src_pfns, unsigned long npages)
>  	for (i = 0; i < npages; i++) {
>  		struct page *page = pfn_to_page(src_pfns[i]);
>  		struct folio *folio = page_folio(page);
> -		unsigned int nr = 1;
> +		unsigned long nr, slots;
>  
>  		src_pfns[i] = migrate_device_pfn_lock(src_pfns[i]);
>  		nr = folio_nr_pages(folio);
> +		slots = min(nr, npages - i);
>  		if (nr > 1) {
>  			src_pfns[i] |= MIGRATE_PFN_COMPOUND;
> -			for (j = 1; j < nr; j++)
> +			for (j = 1; j < slots; j++)
>  				src_pfns[i+j] = 0;
> -			i += j - 1;
> +			i += slots - 1;
>  		}
>  	}
>  
> -- 
> 2.54.0
> 
>

There is a similar fix sent out by Matthew, The question is, is it OK to
send in partial data upto npages (when nr > npages - i)? We should bail
early and not proceed if that is the case.

Balbir
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.