handle_pair_message() iterates up to msg->vp_count without verifying it
against HV_MESSAGE_MAX_PARTITION_VP_PAIR_COUNT. Since vp_count is read
from untrusted hypervisor data, a malformed message with a large value
would cause out-of-bounds reads from the partition_ids and vp_indexes
arrays.

handle_bitset_message() iterates over set bits in valid_bank_mask (up to
64) and advances bank_contents for each one. However, the payload buffer
only has space for 16 bank entries. A valid_bank_mask with more than 16
bits set causes bank_contents to read beyond the message buffer.

Fix both by adding bounds validation:
- Clamp vp_count to HV_MESSAGE_MAX_PARTITION_VP_PAIR_COUNT
- Track banks consumed and stop before exceeding buffer capacity

Fixes: 621191d709b1 ("Drivers: hv: Introduce mshv_root module to expose 
/dev/mshv to VMMs")
Signed-off-by: Stanislav Kinsburskii <[email protected]>
---
 drivers/hv/mshv_synic.c |   20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/drivers/hv/mshv_synic.c b/drivers/hv/mshv_synic.c
index 5333b3889a408..3a71682ffe048 100644
--- a/drivers/hv/mshv_synic.c
+++ b/drivers/hv/mshv_synic.c
@@ -187,7 +187,9 @@ static void kick_vp(struct mshv_vp *vp)
 static void
 handle_bitset_message(const struct hv_vp_signal_bitset_scheduler_message *msg)
 {
-       int bank_idx, vps_signaled = 0, bank_mask_size;
+       int bank_idx, vps_signaled = 0, bank_mask_size, banks_used = 0;
+       const int max_banks = sizeof(msg->vp_bitset.bitset_buffer) /
+                             sizeof(u64) - 2; /* subtract format + mask */
        struct mshv_partition *partition;
        const struct hv_vpset *vpset;
        const u64 *bank_contents;
@@ -227,6 +229,11 @@ handle_bitset_message(const struct 
hv_vp_signal_bitset_scheduler_message *msg)
                if (bank_idx == bank_mask_size)
                        break;
 
+               if (unlikely(banks_used >= max_banks)) {
+                       pr_debug("valid_bank_mask exceeds buffer capacity\n");
+                       goto unlock_out;
+               }
+
                while (true) {
                        struct mshv_vp *vp;
 
@@ -255,6 +262,7 @@ handle_bitset_message(const struct 
hv_vp_signal_bitset_scheduler_message *msg)
                }
 
                bank_contents++;
+               banks_used++;
        }
 
 unlock_out:
@@ -271,10 +279,18 @@ handle_pair_message(const struct 
hv_vp_signal_pair_scheduler_message *msg)
        struct mshv_partition *partition = NULL;
        struct mshv_vp *vp;
        int idx;
+       u8 vp_count = msg->vp_count;
+
+       if (unlikely(vp_count > HV_MESSAGE_MAX_PARTITION_VP_PAIR_COUNT)) {
+               pr_debug("pair message vp_count %u exceeds max %lu\n",
+                        vp_count,
+                        (unsigned long)HV_MESSAGE_MAX_PARTITION_VP_PAIR_COUNT);
+               return;
+       }
 
        rcu_read_lock();
 
-       for (idx = 0; idx < msg->vp_count; idx++) {
+       for (idx = 0; idx < vp_count; idx++) {
                u64 partition_id = msg->partition_ids[idx];
                u32 vp_index = msg->vp_indexes[idx];
 



Reply via email to