mshv_region_range_fault() unconditionally sets HMM_PFN_REQ_WRITE on
the hmm_range_fault() request. When the region was created without
MSHV_SET_MEM_BIT_WRITABLE (so region->hv_map_flags has no
HV_MAP_GPA_WRITABLE), the request still asks HMM for writable pages.
On read-only mappings this causes hmm_range_fault() to break
copy-on-write — for example, the shared zero page or file-backed
pages — granting the guest a private writable copy of memory that
host policy intended to keep shared.
Gate HMM_PFN_REQ_WRITE on the region's HV_MAP_GPA_WRITABLE bit so
that read-only regions request read-only faults.
Note: this still asks for write on writable regions even if the
backing VMA is read-only. A more thorough check would also consult
each VMA's vm_flags inside the fault loop; that requires iterating
VMAs and is left for a follow-up.
Fixes: b9a66cd5ccbb ("mshv: Add support for movable memory regions")
Signed-off-by: Stanislav Kinsburskii <[email protected]>
---
drivers/hv/mshv_regions.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c
index 81e57f727be35..d9e1fbfefe714 100644
--- a/drivers/hv/mshv_regions.c
+++ b/drivers/hv/mshv_regions.c
@@ -441,7 +441,7 @@ static int mshv_region_range_fault(struct mshv_mem_region
*region,
{
struct hmm_range range = {
.notifier = ®ion->mreg_mni,
- .default_flags = HMM_PFN_REQ_FAULT | HMM_PFN_REQ_WRITE,
+ .default_flags = HMM_PFN_REQ_FAULT,
};
unsigned long *pfns;
int ret;
@@ -455,6 +455,15 @@ static int mshv_region_range_fault(struct mshv_mem_region
*region,
range.start = region->start_uaddr + page_offset * HV_HYP_PAGE_SIZE;
range.end = range.start + page_count * HV_HYP_PAGE_SIZE;
+ /*
+ * Only request writable pages from HMM when the region itself
+ * permits writes. Without this, hmm_range_fault() would
+ * trigger COW on read-only regions, breaking copy-on-write
+ * semantics on shared host pages.
+ */
+ if (region->hv_map_flags & HV_MAP_GPA_WRITABLE)
+ range.default_flags |= HMM_PFN_REQ_WRITE;
+
do {
ret = mshv_region_hmm_fault_and_lock(region, &range);
} while (ret == -EBUSY);