Skip to content

Commit fda4e2e

Browse files
Andy Honigbonzini
Andy Honig
authored andcommitted
KVM: x86: Convert vapic synchronization to _cached functions (CVE-2013-6368)
In kvm_lapic_sync_from_vapic and kvm_lapic_sync_to_vapic there is the potential to corrupt kernel memory if userspace provides an address that is at the end of a page. This patches concerts those functions to use kvm_write_guest_cached and kvm_read_guest_cached. It also checks the vapic_address specified by userspace during ioctl processing and returns an error to userspace if the address is not a valid GPA. This is generally not guest triggerable, because the required write is done by firmware that runs before the guest. Also, it only affects AMD processors and oldish Intel that do not have the FlexPriority feature (unless you disable FlexPriority, of course; then newer processors are also affected). Fixes: b93463a ('KVM: Accelerated apic support') Reported-by: Andrew Honig <[email protected]> Cc: [email protected] Signed-off-by: Andrew Honig <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent b963a22 commit fda4e2e

File tree

3 files changed

+18
-53
lines changed

3 files changed

+18
-53
lines changed

arch/x86/kvm/lapic.c

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1692,17 +1692,15 @@ static void apic_sync_pv_eoi_from_guest(struct kvm_vcpu *vcpu,
16921692
void kvm_lapic_sync_from_vapic(struct kvm_vcpu *vcpu)
16931693
{
16941694
u32 data;
1695-
void *vapic;
16961695

16971696
if (test_bit(KVM_APIC_PV_EOI_PENDING, &vcpu->arch.apic_attention))
16981697
apic_sync_pv_eoi_from_guest(vcpu, vcpu->arch.apic);
16991698

17001699
if (!test_bit(KVM_APIC_CHECK_VAPIC, &vcpu->arch.apic_attention))
17011700
return;
17021701

1703-
vapic = kmap_atomic(vcpu->arch.apic->vapic_page);
1704-
data = *(u32 *)(vapic + offset_in_page(vcpu->arch.apic->vapic_addr));
1705-
kunmap_atomic(vapic);
1702+
kvm_read_guest_cached(vcpu->kvm, &vcpu->arch.apic->vapic_cache, &data,
1703+
sizeof(u32));
17061704

17071705
apic_set_tpr(vcpu->arch.apic, data & 0xff);
17081706
}
@@ -1738,7 +1736,6 @@ void kvm_lapic_sync_to_vapic(struct kvm_vcpu *vcpu)
17381736
u32 data, tpr;
17391737
int max_irr, max_isr;
17401738
struct kvm_lapic *apic = vcpu->arch.apic;
1741-
void *vapic;
17421739

17431740
apic_sync_pv_eoi_to_guest(vcpu, apic);
17441741

@@ -1754,18 +1751,24 @@ void kvm_lapic_sync_to_vapic(struct kvm_vcpu *vcpu)
17541751
max_isr = 0;
17551752
data = (tpr & 0xff) | ((max_isr & 0xf0) << 8) | (max_irr << 24);
17561753

1757-
vapic = kmap_atomic(vcpu->arch.apic->vapic_page);
1758-
*(u32 *)(vapic + offset_in_page(vcpu->arch.apic->vapic_addr)) = data;
1759-
kunmap_atomic(vapic);
1754+
kvm_write_guest_cached(vcpu->kvm, &vcpu->arch.apic->vapic_cache, &data,
1755+
sizeof(u32));
17601756
}
17611757

1762-
void kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr)
1758+
int kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr)
17631759
{
1764-
vcpu->arch.apic->vapic_addr = vapic_addr;
1765-
if (vapic_addr)
1760+
if (vapic_addr) {
1761+
if (kvm_gfn_to_hva_cache_init(vcpu->kvm,
1762+
&vcpu->arch.apic->vapic_cache,
1763+
vapic_addr, sizeof(u32)))
1764+
return -EINVAL;
17661765
__set_bit(KVM_APIC_CHECK_VAPIC, &vcpu->arch.apic_attention);
1767-
else
1766+
} else {
17681767
__clear_bit(KVM_APIC_CHECK_VAPIC, &vcpu->arch.apic_attention);
1768+
}
1769+
1770+
vcpu->arch.apic->vapic_addr = vapic_addr;
1771+
return 0;
17691772
}
17701773

17711774
int kvm_x2apic_msr_write(struct kvm_vcpu *vcpu, u32 msr, u64 data)

arch/x86/kvm/lapic.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ struct kvm_lapic {
3434
*/
3535
void *regs;
3636
gpa_t vapic_addr;
37-
struct page *vapic_page;
37+
struct gfn_to_hva_cache vapic_cache;
3838
unsigned long pending_events;
3939
unsigned int sipi_vector;
4040
};
@@ -76,7 +76,7 @@ void kvm_set_lapic_tscdeadline_msr(struct kvm_vcpu *vcpu, u64 data);
7676
void kvm_apic_write_nodecode(struct kvm_vcpu *vcpu, u32 offset);
7777
void kvm_apic_set_eoi_accelerated(struct kvm_vcpu *vcpu, int vector);
7878

79-
void kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr);
79+
int kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr);
8080
void kvm_lapic_sync_from_vapic(struct kvm_vcpu *vcpu);
8181
void kvm_lapic_sync_to_vapic(struct kvm_vcpu *vcpu);
8282

arch/x86/kvm/x86.c

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3214,8 +3214,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
32143214
r = -EFAULT;
32153215
if (copy_from_user(&va, argp, sizeof va))
32163216
goto out;
3217-
r = 0;
3218-
kvm_lapic_set_vapic_addr(vcpu, va.vapic_addr);
3217+
r = kvm_lapic_set_vapic_addr(vcpu, va.vapic_addr);
32193218
break;
32203219
}
32213220
case KVM_X86_SETUP_MCE: {
@@ -5739,36 +5738,6 @@ static void post_kvm_run_save(struct kvm_vcpu *vcpu)
57395738
!kvm_event_needs_reinjection(vcpu);
57405739
}
57415740

5742-
static int vapic_enter(struct kvm_vcpu *vcpu)
5743-
{
5744-
struct kvm_lapic *apic = vcpu->arch.apic;
5745-
struct page *page;
5746-
5747-
if (!apic || !apic->vapic_addr)
5748-
return 0;
5749-
5750-
page = gfn_to_page(vcpu->kvm, apic->vapic_addr >> PAGE_SHIFT);
5751-
if (is_error_page(page))
5752-
return -EFAULT;
5753-
5754-
vcpu->arch.apic->vapic_page = page;
5755-
return 0;
5756-
}
5757-
5758-
static void vapic_exit(struct kvm_vcpu *vcpu)
5759-
{
5760-
struct kvm_lapic *apic = vcpu->arch.apic;
5761-
int idx;
5762-
5763-
if (!apic || !apic->vapic_addr)
5764-
return;
5765-
5766-
idx = srcu_read_lock(&vcpu->kvm->srcu);
5767-
kvm_release_page_dirty(apic->vapic_page);
5768-
mark_page_dirty(vcpu->kvm, apic->vapic_addr >> PAGE_SHIFT);
5769-
srcu_read_unlock(&vcpu->kvm->srcu, idx);
5770-
}
5771-
57725741
static void update_cr8_intercept(struct kvm_vcpu *vcpu)
57735742
{
57745743
int max_irr, tpr;
@@ -6069,11 +6038,6 @@ static int __vcpu_run(struct kvm_vcpu *vcpu)
60696038
struct kvm *kvm = vcpu->kvm;
60706039

60716040
vcpu->srcu_idx = srcu_read_lock(&kvm->srcu);
6072-
r = vapic_enter(vcpu);
6073-
if (r) {
6074-
srcu_read_unlock(&kvm->srcu, vcpu->srcu_idx);
6075-
return r;
6076-
}
60776041

60786042
r = 1;
60796043
while (r > 0) {
@@ -6132,8 +6096,6 @@ static int __vcpu_run(struct kvm_vcpu *vcpu)
61326096

61336097
srcu_read_unlock(&kvm->srcu, vcpu->srcu_idx);
61346098

6135-
vapic_exit(vcpu);
6136-
61376099
return r;
61386100
}
61396101

0 commit comments

Comments
 (0)