49 lines
2 KiB
Diff
49 lines
2 KiB
Diff
Bugzilla: 1071837
|
|
Upstream-status: 3.14
|
|
|
|
From a08d3b3b99efd509133946056531cdf8f3a0c09b Mon Sep 17 00:00:00 2001
|
|
From: Andrew Honig <ahonig@google.com>
|
|
Date: Thu, 27 Feb 2014 18:35:14 +0000
|
|
Subject: kvm: x86: fix emulator buffer overflow (CVE-2014-0049)
|
|
|
|
The problem occurs when the guest performs a pusha with the stack
|
|
address pointing to an mmio address (or an invalid guest physical
|
|
address) to start with, but then extending into an ordinary guest
|
|
physical address. When doing repeated emulated pushes
|
|
emulator_read_write sets mmio_needed to 1 on the first one. On a
|
|
later push when the stack points to regular memory,
|
|
mmio_nr_fragments is set to 0, but mmio_is_needed is not set to 0.
|
|
|
|
As a result, KVM exits to userspace, and then returns to
|
|
complete_emulated_mmio. In complete_emulated_mmio
|
|
vcpu->mmio_cur_fragment is incremented. The termination condition of
|
|
vcpu->mmio_cur_fragment == vcpu->mmio_nr_fragments is never achieved.
|
|
The code bounces back and fourth to userspace incrementing
|
|
mmio_cur_fragment past it's buffer. If the guest does nothing else it
|
|
eventually leads to a a crash on a memcpy from invalid memory address.
|
|
|
|
However if a guest code can cause the vm to be destroyed in another
|
|
vcpu with excellent timing, then kvm_clear_async_pf_completion_queue
|
|
can be used by the guest to control the data that's pointed to by the
|
|
call to cancel_work_item, which can be used to gain execution.
|
|
|
|
Fixes: f78146b0f9230765c6315b2e14f56112513389ad
|
|
Signed-off-by: Andrew Honig <ahonig@google.com>
|
|
Cc: stable@vger.kernel.org (3.5+)
|
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
|
---
|
|
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
|
|
index 39c28f09..2b85784 100644
|
|
--- a/arch/x86/kvm/x86.c
|
|
+++ b/arch/x86/kvm/x86.c
|
|
@@ -6186,7 +6186,7 @@ static int complete_emulated_mmio(struct kvm_vcpu *vcpu)
|
|
frag->len -= len;
|
|
}
|
|
|
|
- if (vcpu->mmio_cur_fragment == vcpu->mmio_nr_fragments) {
|
|
+ if (vcpu->mmio_cur_fragment >= vcpu->mmio_nr_fragments) {
|
|
vcpu->mmio_needed = 0;
|
|
|
|
/* FIXME: return into emulator if single-stepping. */
|
|
--
|
|
cgit v0.9.2
|