KVM: X86: Fix array_index_nospec protection in __pv_send_ipi

The __pv_send_ipi() function iterates over up to BITS_PER_LONG vCPUs
starting from the APIC ID specified in its 'min' argument, which is
provided by the guest.

Commit c87bd4dd43 used array_index_nospec() to clamp the value of 'min'
but then the for_each_set_bit() loop dereferences higher indices without
further protection. Theoretically, a guest can trigger speculative access
to up to BITS_PER_LONG elements off the end of the phys_map[] array.

(In practice it would probably need aggressive loop unrolling by the
compiler to go more than one element off the end, and even that seems
unlikely, but the theoretical possibility exists.)

Move the array_index_nospec() inside the loop to protect the [map + i]
index which is actually being used each time.

Fixes: c87bd4dd43 ("KVM: x86: use array_index_nospec with indices that come from guest")
Fixes: bdf7ffc899 ("KVM: LAPIC: Fix pv ipis out-of-bounds access")
Fixes: 4180bf1b65 ("KVM: X86: Implement "send IPI" hypercall")

Signed-off-by: Anel Orazgaliyeva <anelkz@amazon.de>
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Reviewed-by: Jim Mattson <jmattson@google.com>
Link: https://patch.msgid.link/9d50fc3ca9e8e58f551d015f95d51a3c29ce6ccc.camel@infradead.org
Signed-off-by: Sean Christopherson <seanjc@google.com>
This commit is contained in:
Anel Orazgaliyeva 2026-03-06 08:59:52 +01:00 committed by Sean Christopherson
parent 26c9bfc0fa
commit 00d572d4cd

View File

@ -840,16 +840,16 @@ static int __pv_send_ipi(unsigned long *ipi_bitmap, struct kvm_apic_map *map,
{
int i, count = 0;
struct kvm_vcpu *vcpu;
size_t map_index;
if (min > map->max_apic_id)
return 0;
min = array_index_nospec(min, map->max_apic_id + 1);
for_each_set_bit(i, ipi_bitmap,
min((u32)BITS_PER_LONG, (map->max_apic_id - min + 1))) {
if (map->phys_map[min + i]) {
vcpu = map->phys_map[min + i]->vcpu;
min((u32)BITS_PER_LONG, (map->max_apic_id - min + 1))) {
map_index = array_index_nospec(min + i, map->max_apic_id + 1);
if (map->phys_map[map_index]) {
vcpu = map->phys_map[map_index]->vcpu;
count += kvm_apic_set_irq(vcpu, irq, NULL);
}
}