Commit ba9eb9b8 authored by Nuno Das Neves's avatar Nuno Das Neves Committed by Wei Liu
Browse files

mshv: Fix create memory region overlap check



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: 621191d7 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs")
Reported-by: default avatarMichael Kelley <mhklinux@outlook.com>
Closes: https://lore.kernel.org/linux-hyperv/SN6PR02MB41575BE0406D3AB22E1D7DB5D4C2A@SN6PR02MB4157.namprd02.prod.outlook.com/


Signed-off-by: default avatarNuno Das Neves <nunodasneves@linux.microsoft.com>
Reviewed-by: default avatarMichael Kelley <mhklinux@outlook.com>
Signed-off-by: default avatarWei Liu <wei.liu@kernel.org>
parent 8ec6070f
Loading
Loading
Loading
Loading
+11 −20
Original line number Diff line number Diff line
@@ -1174,21 +1174,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
@@ -1198,15 +1183,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)