]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
mshv: Fix infinite fault loop on permission-denied GPA intercepts
authorStanislav Kinsburskii <skinsburskii@linux.microsoft.com>
Tue, 24 Mar 2026 23:57:40 +0000 (23:57 +0000)
committerWei Liu <wei.liu@kernel.org>
Sat, 4 Apr 2026 05:25:53 +0000 (05:25 +0000)
Prevent infinite fault loops when guests access memory regions without
proper permissions. Currently, mshv_handle_gpa_intercept() attempts to
remap pages for all faults on movable memory regions, regardless of
whether the access type is permitted. When a guest writes to a read-only
region, the remap succeeds but the region remains read-only, causing
immediate re-fault and spinning the vCPU indefinitely.

Validate intercept access type against region permissions before
attempting remaps. Reject writes to non-writable regions and executes to
non-executable regions early, returning false to let the VMM handle the
intercept appropriately.

This also closes a potential DoS vector where malicious guests could
intentionally trigger these fault loops to consume host resources.

Fixes: b9a66cd5ccbb ("mshv: Add support for movable memory regions")
Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
Signed-off-by: Wei Liu <wei.liu@kernel.org>
drivers/hv/mshv_root_main.c
include/hyperv/hvgdk_mini.h
include/hyperv/hvhdk.h

index 6f42423f7faa8c956935abfee35e6217e40756a3..c8e5523a52b6b72150ab5f3eca232db66214c42c 100644 (file)
@@ -630,7 +630,7 @@ static bool mshv_handle_gpa_intercept(struct mshv_vp *vp)
 {
        struct mshv_partition *p = vp->vp_partition;
        struct mshv_mem_region *region;
-       bool ret;
+       bool ret = false;
        u64 gfn;
 #if defined(CONFIG_X86_64)
        struct hv_x64_memory_intercept_message *msg =
@@ -641,6 +641,8 @@ static bool mshv_handle_gpa_intercept(struct mshv_vp *vp)
                (struct hv_arm64_memory_intercept_message *)
                vp->vp_intercept_msg_page->u.payload;
 #endif
+       enum hv_intercept_access_type access_type =
+               msg->header.intercept_access_type;
 
        gfn = HVPFN_DOWN(msg->guest_physical_address);
 
@@ -648,12 +650,19 @@ static bool mshv_handle_gpa_intercept(struct mshv_vp *vp)
        if (!region)
                return false;
 
+       if (access_type == HV_INTERCEPT_ACCESS_WRITE &&
+           !(region->hv_map_flags & HV_MAP_GPA_WRITABLE))
+               goto put_region;
+
+       if (access_type == HV_INTERCEPT_ACCESS_EXECUTE &&
+           !(region->hv_map_flags & HV_MAP_GPA_EXECUTABLE))
+               goto put_region;
+
        /* Only movable memory ranges are supported for GPA intercepts */
        if (region->mreg_type == MSHV_REGION_TYPE_MEM_MOVABLE)
                ret = mshv_region_handle_gfn_fault(region, gfn);
-       else
-               ret = false;
 
+put_region:
        mshv_region_put(region);
 
        return ret;
index 1823a290a7b7a7e89ad04bdbcfaf93d04110985e..f9600f87186ac8828f607a2dd0e6cf9edce2975d 100644 (file)
@@ -1533,4 +1533,10 @@ struct hv_mmio_write_input {
        u8 data[HV_HYPERCALL_MMIO_MAX_DATA_LENGTH];
 } __packed;
 
+enum hv_intercept_access_type {
+       HV_INTERCEPT_ACCESS_READ        = 0,
+       HV_INTERCEPT_ACCESS_WRITE       = 1,
+       HV_INTERCEPT_ACCESS_EXECUTE     = 2
+};
+
 #endif /* _HV_HVGDK_MINI_H */
index 245f3db53bf195480a9ce633b9fdfb666061beeb..5e83d37149662c0bf6c2cfa1f47bd937d9e68fca 100644 (file)
@@ -779,7 +779,7 @@ struct hv_x64_intercept_message_header {
        u32 vp_index;
        u8 instruction_length:4;
        u8 cr8:4; /* Only set for exo partitions */
-       u8 intercept_access_type;
+       u8 intercept_access_type; /* enum hv_intercept_access_type */
        union hv_x64_vp_execution_state execution_state;
        struct hv_x64_segment_register cs_segment;
        u64 rip;
@@ -825,7 +825,7 @@ union hv_arm64_vp_execution_state {
 struct hv_arm64_intercept_message_header {
        u32 vp_index;
        u8 instruction_length;
-       u8 intercept_access_type;
+       u8 intercept_access_type; /* enum hv_intercept_access_type */
        union hv_arm64_vp_execution_state execution_state;
        u64 pc;
        u64 cpsr;