mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
x86/vm_event: fix race between __context_switch() and vm_event_resume()
The introspection agent can reply to a vm_event faster than vmx_vmexit_handler() can complete in some cases, where it is then not safe for vm_event_set_registers() to modify v->arch.user_regs. In the test scenario, we were stepping over an INT3 breakpoint by setting RIP += 1. The quick reply tended to complete before the VCPU triggering the introspection event had properly paused and been descheduled. If the reply occurs before __context_switch() happens, __context_switch() clobbers the reply by overwriting v->arch.user_regs from the stack. If we don't pass through __context_switch() (due to switching to the idle vCPU), reply data wouldn't be picked up when switching back straight to the original vCPU. This patch ensures that vm_event_resume() code only sets per-VCPU data to be used for the actual setting of registers later in hvm_do_resume() (similar to the model used to control setting of CRs and MSRs). The patch additionally removes the sync_vcpu_execstate(v) call from vm_event_resume(), which is no longer necessary, which removes the associated broadcast TLB flush (read: performance improvement). Signed-off-by: Razvan Cojocaru <rcojocaru@bitdefender.com> Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com> Reviewed-by: Jan Beulich <jbeulich@suse.com> Acked-by: Tamas K Lengyel <tamas@tklengyel.com> Release-acked-by: Julien Grall <julien.grall@arm.com>
This commit is contained in:
1 parent
1366a0e76d
commit
461f048203
4 files changed
+49
-27
No files matched your search
@@ -25,12 +25,47 @@
|
||||
#include <asm/hvm/support.h>
|
||||
#include <asm/vm_event.h>
|
||||
|
||||
static void hvm_vm_event_set_registers(const struct vcpu *v)
|
||||
{
|
||||
ASSERT(v == current);
|
||||
|
||||
if ( unlikely(v->arch.vm_event->set_gprs) )
|
||||
{
|
||||
struct cpu_user_regs *regs = guest_cpu_user_regs();
|
||||
|
||||
regs->rax = v->arch.vm_event->gprs.rax;
|
||||
regs->rbx = v->arch.vm_event->gprs.rbx;
|
||||
regs->rcx = v->arch.vm_event->gprs.rcx;
|
||||
regs->rdx = v->arch.vm_event->gprs.rdx;
|
||||
regs->rsp = v->arch.vm_event->gprs.rsp;
|
||||
regs->rbp = v->arch.vm_event->gprs.rbp;
|
||||
regs->rsi = v->arch.vm_event->gprs.rsi;
|
||||
regs->rdi = v->arch.vm_event->gprs.rdi;
|
||||
|
||||
regs->r8 = v->arch.vm_event->gprs.r8;
|
||||
regs->r9 = v->arch.vm_event->gprs.r9;
|
||||
regs->r10 = v->arch.vm_event->gprs.r10;
|
||||
regs->r11 = v->arch.vm_event->gprs.r11;
|
||||
regs->r12 = v->arch.vm_event->gprs.r12;
|
||||
regs->r13 = v->arch.vm_event->gprs.r13;
|
||||
regs->r14 = v->arch.vm_event->gprs.r14;
|
||||
regs->r15 = v->arch.vm_event->gprs.r15;
|
||||
|
||||
regs->rflags = v->arch.vm_event->gprs.rflags;
|
||||
regs->rip = v->arch.vm_event->gprs.rip;
|
||||
|
||||
v->arch.vm_event->set_gprs = false;
|
||||
}
|
||||
}
|
||||
|
||||
void hvm_vm_event_do_resume(struct vcpu *v)
|
||||
{
|
||||
struct monitor_write_data *w;
|
||||
|
||||
ASSERT(v->arch.vm_event);
|
||||
|
||||
hvm_vm_event_set_registers(v);
|
||||
|
||||
w = &v->arch.vm_event->write_data;
|
||||
|
||||
if ( unlikely(v->arch.vm_event->emulate_flags) )
|
||||
|
||||
+2
-20
@@ -113,26 +113,8 @@ void vm_event_set_registers(struct vcpu *v, vm_event_response_t *rsp)
|
||||
{
|
||||
ASSERT(atomic_read(&v->vm_event_pause_count));
|
||||
|
||||
v->arch.user_regs.rax = rsp->data.regs.x86.rax;
|
||||
v->arch.user_regs.rbx = rsp->data.regs.x86.rbx;
|
||||
v->arch.user_regs.rcx = rsp->data.regs.x86.rcx;
|
||||
v->arch.user_regs.rdx = rsp->data.regs.x86.rdx;
|
||||
v->arch.user_regs.rsp = rsp->data.regs.x86.rsp;
|
||||
v->arch.user_regs.rbp = rsp->data.regs.x86.rbp;
|
||||
v->arch.user_regs.rsi = rsp->data.regs.x86.rsi;
|
||||
v->arch.user_regs.rdi = rsp->data.regs.x86.rdi;
|
||||
|
||||
v->arch.user_regs.r8 = rsp->data.regs.x86.r8;
|
||||
v->arch.user_regs.r9 = rsp->data.regs.x86.r9;
|
||||
v->arch.user_regs.r10 = rsp->data.regs.x86.r10;
|
||||
v->arch.user_regs.r11 = rsp->data.regs.x86.r11;
|
||||
v->arch.user_regs.r12 = rsp->data.regs.x86.r12;
|
||||
v->arch.user_regs.r13 = rsp->data.regs.x86.r13;
|
||||
v->arch.user_regs.r14 = rsp->data.regs.x86.r14;
|
||||
v->arch.user_regs.r15 = rsp->data.regs.x86.r15;
|
||||
|
||||
v->arch.user_regs.rflags = rsp->data.regs.x86.rflags;
|
||||
v->arch.user_regs.rip = rsp->data.regs.x86.rip;
|
||||
v->arch.vm_event->gprs = rsp->data.regs.x86;
|
||||
v->arch.vm_event->set_gprs = true;
|
||||
}
|
||||
|
||||
void vm_event_monitor_next_interrupt(struct vcpu *v)
|
||||
|
||||
+10
-7
@@ -357,6 +357,16 @@ void vm_event_resume(struct domain *d, struct vm_event_domain *ved)
|
||||
{
|
||||
vm_event_response_t rsp;
|
||||
|
||||
/*
|
||||
* vm_event_resume() runs in either XEN_DOMCTL_VM_EVENT_OP_*, or
|
||||
* EVTCHN_send context from the introspection consumer. Both contexts
|
||||
* are guaranteed not to be the subject of vm_event responses.
|
||||
* While we could ASSERT(v != current) for each VCPU in d in the loop
|
||||
* below, this covers the case where we would need to iterate over all
|
||||
* of them more succintly.
|
||||
*/
|
||||
ASSERT(d != current->domain);
|
||||
|
||||
/* Pull all responses off the ring. */
|
||||
while ( vm_event_get_response(d, ved, &rsp) )
|
||||
{
|
||||
@@ -374,13 +384,6 @@ void vm_event_resume(struct domain *d, struct vm_event_domain *ved)
|
||||
|
||||
v = d->vcpu[rsp.vcpu_id];
|
||||
|
||||
/*
|
||||
* Make sure the vCPU state has been synchronized for the custom
|
||||
* handlers.
|
||||
*/
|
||||
if ( atomic_read(&v->vm_event_pause_count) )
|
||||
sync_vcpu_execstate(v);
|
||||
|
||||
/*
|
||||
* In some cases the response type needs extra handling, so here
|
||||
* we call the appropriate handlers.
|
||||
|
||||
@@ -32,6 +32,8 @@ struct arch_vm_event {
|
||||
struct vm_event_emul_insn_data insn;
|
||||
} emul;
|
||||
struct monitor_write_data write_data;
|
||||
struct vm_event_regs_x86 gprs;
|
||||
bool set_gprs;
|
||||
};
|
||||
|
||||
int vm_event_init_domain(struct domain *d);
|
||||
|
||||