From: Stanislav Kinsburskii <[email protected]> Sent: Thursday, 
May 7, 2026 8:44 AM
> 
> 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 89207aad7cf1f..5d509299f14d7 100644
> --- a/drivers/hv/mshv_synic.c
> +++ b/drivers/hv/mshv_synic.c
> @@ -190,7 +190,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;
> @@ -230,6 +232,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;
> +             }

Instead of counting the banks while going through the loop, and checking
against the max on each loop iteration, consider doing the following up
front before entering the while loop:

                if (hweight64(vpset->valid_bank_mask) > max_banks) {
                        pr_debug("valid_bank_mask exceeds buffer capacity\n");
                        return;
                }

Of course, such an approach would not process *any* bits in the message,
but that's probably OK since the message Linux got is malformed.

Michael

> +
>               while (true) {
>                       struct mshv_vp *vp;
> 
> @@ -258,6 +265,7 @@ handle_bitset_message(const struct 
> hv_vp_signal_bitset_scheduler_message *msg)
>               }
> 
>               bank_contents++;
> +             banks_used++;
>       }
> 
>  unlock_out:
> @@ -274,10 +282,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