From 7372acade2bdbca0782e7fd5040b913b3724bfbb Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Tue, 19 May 2026 13:36:20 +0200 Subject: [PATCH] memory: overlapping XENMAPSPACE_gmfn_range requests Overlapping requests may need processing backwards, or else the intended effect wouldn't be achieved (and instead some pages would be moved more than once). With some adjustment there this also covers XEN_DMOP_relocate_memory, where the potential issue was first noticed. Fixes: a04811a315e0 ("mm: New XENMEM space, XENMAPSPACE_gmfn_range") Reported-by: Andrew Cooper Signed-off-by: Jan Beulich Acked-by: Andrew Cooper Tested-by: Andrew Cooper master commit: 7acc7dd36b9e30aa6c5939d9d7531b3c39ad5c03 master date: 2026-05-13 09:59:19 +0200 --- xen/arch/x86/hvm/dm.c | 8 ++++++-- xen/common/memory.c | 47 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/xen/arch/x86/hvm/dm.c b/xen/arch/x86/hvm/dm.c index 3b53471af0..066498e07e 100644 --- a/xen/arch/x86/hvm/dm.c +++ b/xen/arch/x86/hvm/dm.c @@ -569,12 +569,16 @@ int dm_op(const struct dmop_args *op_args) rc = xenmem_add_to_physmap(d, &xatp, 0); if ( rc == 0 && data->size != xatp.size ) + { rc = xatp.size; + xatp.idx += rc; + xatp.gpfn += rc; + } if ( rc > 0 ) { data->size -= rc; - data->src_gfn += rc; - data->dst_gfn += rc; + data->src_gfn = xatp.idx; + data->dst_gfn = xatp.gpfn; const_op = false; rc = -ERESTART; } diff --git a/xen/common/memory.c b/xen/common/memory.c index 10becf7c1f..3412c7fb4a 100644 --- a/xen/common/memory.c +++ b/xen/common/memory.c @@ -850,7 +850,7 @@ int xenmem_add_to_physmap(struct domain *d, struct xen_add_to_physmap *xatp, unsigned int start) { unsigned int done = 0; - long rc = 0; + long rc = 0, adjust = 1; union add_to_physmap_extra extra = {}; struct page_info *pages[16]; @@ -885,8 +885,25 @@ int xenmem_add_to_physmap(struct domain *d, struct xen_add_to_physmap *xatp, return -EOVERFLOW; } - xatp->idx += start; - xatp->gpfn += start; + /* + * Overlapping ranges need processing backwards when destination is above + * source. + */ + if ( xatp->gpfn > xatp->idx && + unlikely(xatp->gpfn < xatp->idx + xatp->size) ) + { + adjust = -1; + + /* Both fields store "next item to process". */ + xatp->idx += xatp->size - start - 1; + xatp->gpfn += xatp->size - start - 1; + } + else + { + xatp->idx += start; + xatp->gpfn += start; + } + xatp->size -= start; #ifdef CONFIG_HAS_PASSTHROUGH @@ -904,8 +921,8 @@ int xenmem_add_to_physmap(struct domain *d, struct xen_add_to_physmap *xatp, if ( rc < 0 ) break; - xatp->idx++; - xatp->gpfn++; + xatp->idx += adjust; + xatp->gpfn += adjust; if ( extra.ppage ) ++extra.ppage; @@ -928,7 +945,10 @@ int xenmem_add_to_physmap(struct domain *d, struct xen_add_to_physmap *xatp, this_cpu(iommu_dont_flush_iotlb) = 0; - ret = iommu_iotlb_flush(d, _dfn(xatp->idx - done), done, + if ( likely(adjust > 0) ) + adjust = done; + + ret = iommu_iotlb_flush(d, _dfn(xatp->idx - adjust), done, IOMMU_FLUSHF_modified); if ( unlikely(ret) && rc >= 0 ) rc = ret; @@ -942,13 +962,26 @@ int xenmem_add_to_physmap(struct domain *d, struct xen_add_to_physmap *xatp, for ( i = 0; i < done; ++i ) put_page(pages[i]); - ret = iommu_iotlb_flush(d, _dfn(xatp->gpfn - done), done, + ret = iommu_iotlb_flush(d, _dfn(xatp->gpfn - adjust), done, IOMMU_FLUSHF_added | IOMMU_FLUSHF_modified); if ( unlikely(ret) && rc >= 0 ) rc = ret; } #endif + /* + * For internal callers (e.g. XEN_DMOP_relocate_memory handling) leave + * the GFNs from where to resume in *xatp (they're correct already when + * we worked forwards). These are the values not biased for a possible + * non-zero "start" that a subsequent invocation might use, so also do + * this updating only when incoming "start" was 0. + */ + if ( rc > 0 && !start && unlikely(adjust < 0) ) + { + xatp->idx -= xatp->size - rc - 1; + xatp->gpfn -= xatp->size - rc - 1; + } + return rc; }