From: Ankit Agrawal Date: Tue, 17 Feb 2026 15:30:08 +0000 (+0000) Subject: hw/vfio: sort and validate sparse mmap regions by offset X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=da02b21cc70ef04a9ad15198f33551f17c94dff5;p=thirdparty%2Fqemu.git hw/vfio: sort and validate sparse mmap regions by offset Sort sparse mmap regions by offset during region setup to ensure predictable mapping order, avoid overlaps and a proper handling of the gaps between sub-regions. Add validation to detect overlapping sparse regions early during setup before any mapping operations begin. The sorting is performed on the subregions ranges during vfio_setup_region_sparse_mmaps(). This also ensures that subsequent mapping code can rely on subregions being in ascending offset order. This is preparatory work for alignment adjustments needed to support hugepfnmap on systems where device memory (e.g., Grace-based systems) may have non-power-of-2 sizes. cc: Alex Williamson Reviewed-by: Alex Williamson Reviewed-by: Shameer Kolothum Signed-off-by: Ankit Agrawal Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260217153010.408739-2-ankita@nvidia.com Signed-off-by: Cédric Le Goater --- diff --git a/hw/vfio/region.c b/hw/vfio/region.c index ab39d77574..8fbc98918f 100644 --- a/hw/vfio/region.c +++ b/hw/vfio/region.c @@ -149,6 +149,19 @@ static const MemoryRegionOps vfio_region_ops = { }, }; +static int vfio_mmap_compare_offset(const void *a, const void *b) +{ + const VFIOMmap *mmap_a = a; + const VFIOMmap *mmap_b = b; + + if (mmap_a->offset < mmap_b->offset) { + return -1; + } else if (mmap_a->offset > mmap_b->offset) { + return 1; + } + return 0; +} + static int vfio_setup_region_sparse_mmaps(VFIORegion *region, struct vfio_region_info *info) { @@ -182,6 +195,35 @@ static int vfio_setup_region_sparse_mmaps(VFIORegion *region, region->nr_mmaps = j; region->mmaps = g_realloc(region->mmaps, j * sizeof(VFIOMmap)); + /* + * Sort sparse mmaps by offset to ensure proper handling of gaps + * and predictable mapping order in vfio_region_mmap(). + */ + if (region->nr_mmaps > 1) { + qsort(region->mmaps, region->nr_mmaps, sizeof(VFIOMmap), + vfio_mmap_compare_offset); + + /* + * Validate that sparse regions don't overlap after sorting. + */ + for (i = 1; i < region->nr_mmaps; i++) { + off_t prev_end = region->mmaps[i - 1].offset + + region->mmaps[i - 1].size; + if (prev_end > region->mmaps[i].offset) { + error_report("%s: overlapping sparse mmap regions detected " + "in region %d: [0x%"PRIx64"-0x%"PRIx64"] overlaps " + "with [0x%"PRIx64"-0x%"PRIx64"]", + __func__, region->nr, region->mmaps[i - 1].offset, + prev_end - 1, region->mmaps[i].offset, + region->mmaps[i].offset + region->mmaps[i].size - 1); + g_free(region->mmaps); + region->mmaps = NULL; + region->nr_mmaps = 0; + return -EINVAL; + } + } + } + return 0; } @@ -213,11 +255,13 @@ int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region, ret = vfio_setup_region_sparse_mmaps(region, info); - if (ret) { + if (ret == -ENODEV) { region->nr_mmaps = 1; region->mmaps = g_new0(VFIOMmap, region->nr_mmaps); region->mmaps[0].offset = 0; region->mmaps[0].size = region->size; + } else if (ret) { + return ret; } } }