| From: Wanpeng Li <wanpeng.li@hotmail.com> |
| Date: Wed, 13 Dec 2017 10:46:40 +0100 |
| Subject: KVM: x86: fix escape of guest dr6 to the host |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| commit efdab992813fb2ed825745625b83c05032e9cda2 upstream. |
| |
| syzkaller reported: |
| |
| WARNING: CPU: 0 PID: 12927 at arch/x86/kernel/traps.c:780 do_debug+0x222/0x250 |
| CPU: 0 PID: 12927 Comm: syz-executor Tainted: G OE 4.15.0-rc2+ #16 |
| RIP: 0010:do_debug+0x222/0x250 |
| Call Trace: |
| <#DB> |
| debug+0x3e/0x70 |
| RIP: 0010:copy_user_enhanced_fast_string+0x10/0x20 |
| </#DB> |
| _copy_from_user+0x5b/0x90 |
| SyS_timer_create+0x33/0x80 |
| entry_SYSCALL_64_fastpath+0x23/0x9a |
| |
| The testcase sets a watchpoint (with perf_event_open) on a buffer that is |
| passed to timer_create() as the struct sigevent argument. In timer_create(), |
| copy_from_user()'s rep movsb triggers the BP. The testcase also sets |
| the debug registers for the guest. |
| |
| However, KVM only restores host debug registers when the host has active |
| watchpoints, which triggers a race condition when running the testcase with |
| multiple threads. The guest's DR6.BS bit can escape to the host before |
| another thread invokes timer_create(), and do_debug() complains. |
| |
| The fix is to respect do_debug()'s dr6 invariant when leaving KVM. |
| |
| Reported-by: Dmitry Vyukov <dvyukov@google.com> |
| Cc: Paolo Bonzini <pbonzini@redhat.com> |
| Cc: Radim Krčmář <rkrcmar@redhat.com> |
| Cc: David Hildenbrand <david@redhat.com> |
| Cc: Dmitry Vyukov <dvyukov@google.com> |
| Reviewed-by: David Hildenbrand <david@redhat.com> |
| Signed-off-by: Wanpeng Li <wanpeng.li@hotmail.com> |
| Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> |
| Signed-off-by: Radim Krčmář <rkrcmar@redhat.com> |
| [bwh: Backported to 3.16: adjust context] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| arch/x86/kvm/x86.c | 6 ++++++ |
| 1 file changed, 6 insertions(+) |
| |
| --- a/arch/x86/kvm/x86.c |
| +++ b/arch/x86/kvm/x86.c |
| @@ -2892,6 +2892,12 @@ void kvm_arch_vcpu_put(struct kvm_vcpu * |
| kvm_x86_ops->vcpu_put(vcpu); |
| kvm_put_guest_fpu(vcpu); |
| vcpu->arch.last_host_tsc = native_read_tsc(); |
| + /* |
| + * If userspace has set any breakpoints or watchpoints, dr6 is restored |
| + * on every vmexit, but if not, we might have a stale dr6 from the |
| + * guest. do_debug expects dr6 to be cleared after it runs, do the same. |
| + */ |
| + set_debugreg(0, 6); |
| } |
| |
| static int kvm_vcpu_ioctl_get_lapic(struct kvm_vcpu *vcpu, |