The current check is incorrect; it only checks if the beginning or end
of a region is within an existing region. This doesn't account for
userspace specifying a region that begins before and ends after an
existing region.

Change the logic to a range intersection check against gfns and uaddrs
for each region.

Remove mshv_partition_region_by_uaddr() as it is no longer used.

Fixes: 621191d709b1 ("Drivers: hv: Introduce mshv_root module to expose 
/dev/mshv to VMMs")
Reported-by: Michael Kelley <[email protected]>
Closes: 
https://lore.kernel.org/linux-hyperv/sn6pr02mb41575be0406d3ab22e1d7db5d4...@sn6pr02mb4157.namprd02.prod.outlook.com/
Signed-off-by: Nuno Das Neves <[email protected]>
---
 drivers/hv/mshv_root_main.c | 31 +++++++++++--------------------
 1 file changed, 11 insertions(+), 20 deletions(-)

diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index 814465a0912d..25a68912a78d 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -1206,21 +1206,6 @@ mshv_partition_region_by_gfn(struct mshv_partition 
*partition, u64 gfn)
        return NULL;
 }
 
-static struct mshv_mem_region *
-mshv_partition_region_by_uaddr(struct mshv_partition *partition, u64 uaddr)
-{
-       struct mshv_mem_region *region;
-
-       hlist_for_each_entry(region, &partition->pt_mem_regions, hnode) {
-               if (uaddr >= region->start_uaddr &&
-                   uaddr < region->start_uaddr +
-                           (region->nr_pages << HV_HYP_PAGE_SHIFT))
-                       return region;
-       }
-
-       return NULL;
-}
-
 /*
  * NB: caller checks and makes sure mem->size is page aligned
  * Returns: 0 with regionpp updated on success, or -errno
@@ -1230,15 +1215,21 @@ static int mshv_partition_create_region(struct 
mshv_partition *partition,
                                        struct mshv_mem_region **regionpp,
                                        bool is_mmio)
 {
-       struct mshv_mem_region *region;
+       struct mshv_mem_region *region, *rg;
        u64 nr_pages = HVPFN_DOWN(mem->size);
 
        /* Reject overlapping regions */
-       if (mshv_partition_region_by_gfn(partition, mem->guest_pfn) ||
-           mshv_partition_region_by_gfn(partition, mem->guest_pfn + nr_pages - 
1) ||
-           mshv_partition_region_by_uaddr(partition, mem->userspace_addr) ||
-           mshv_partition_region_by_uaddr(partition, mem->userspace_addr + 
mem->size - 1))
+       hlist_for_each_entry(rg, &partition->pt_mem_regions, hnode) {
+               u64 rg_size = rg->nr_pages << HV_HYP_PAGE_SHIFT;
+
+               if ((mem->guest_pfn + nr_pages <= rg->start_gfn ||
+                    rg->start_gfn + rg->nr_pages <= mem->guest_pfn) &&
+                   (mem->userspace_addr + mem->size <= rg->start_uaddr ||
+                    rg->start_uaddr + rg_size <= mem->userspace_addr))
+                       continue;
+
                return -EEXIST;
+       }
 
        region = vzalloc(sizeof(*region) + sizeof(struct page *) * nr_pages);
        if (!region)
-- 
2.34.1


Reply via email to