Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 94fe45da authored by Jan Kiszka's avatar Jan Kiszka Committed by Avi Kivity
Browse files

KVM: x86: Fix guest single-stepping while interruptible



Commit 705c5323 opened the doors of hell by unconditionally injecting
single-step flags as long as guest_debug signaled this. This doesn't
work when the guest branches into some interrupt or exception handler
and triggers a vmexit with flag reloading.

Fix it by saving cs:rip when user space requests single-stepping and
restricting the trace flag injection to this guest code position.

Signed-off-by: default avatarJan Kiszka <jan.kiszka@siemens.com>
Signed-off-by: default avatarMarcelo Tosatti <mtosatti@redhat.com>
parent ffde22ac
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -371,6 +371,10 @@ struct kvm_vcpu_arch {
	u64 mcg_status;
	u64 mcg_ctl;
	u64 *mce_banks;

	/* used for guest single stepping over the given code position */
	u16 singlestep_cs;
	unsigned long singlestep_rip;
};

struct kvm_mem_alias {
+28 −19
Original line number Diff line number Diff line
@@ -235,25 +235,6 @@ bool kvm_require_cpl(struct kvm_vcpu *vcpu, int required_cpl)
}
EXPORT_SYMBOL_GPL(kvm_require_cpl);

unsigned long kvm_get_rflags(struct kvm_vcpu *vcpu)
{
	unsigned long rflags;

	rflags = kvm_x86_ops->get_rflags(vcpu);
	if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)
		rflags &= ~(unsigned long)(X86_EFLAGS_TF | X86_EFLAGS_RF);
	return rflags;
}
EXPORT_SYMBOL_GPL(kvm_get_rflags);

void kvm_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags)
{
	if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)
		rflags |= X86_EFLAGS_TF | X86_EFLAGS_RF;
	kvm_x86_ops->set_rflags(vcpu, rflags);
}
EXPORT_SYMBOL_GPL(kvm_set_rflags);

/*
 * Load the pae pdptrs.  Return true is they are all valid.
 */
@@ -4565,6 +4546,12 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
		vcpu->arch.switch_db_regs = (vcpu->arch.dr7 & DR7_BP_EN_MASK);
	}

	if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
		vcpu->arch.singlestep_cs =
			get_segment_selector(vcpu, VCPU_SREG_CS);
		vcpu->arch.singlestep_rip = kvm_rip_read(vcpu);
	}

	/*
	 * Trigger an rflags update that will inject or remove the trace
	 * flags.
@@ -5031,6 +5018,28 @@ int kvm_arch_interrupt_allowed(struct kvm_vcpu *vcpu)
	return kvm_x86_ops->interrupt_allowed(vcpu);
}

unsigned long kvm_get_rflags(struct kvm_vcpu *vcpu)
{
	unsigned long rflags;

	rflags = kvm_x86_ops->get_rflags(vcpu);
	if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)
		rflags &= ~(unsigned long)(X86_EFLAGS_TF | X86_EFLAGS_RF);
	return rflags;
}
EXPORT_SYMBOL_GPL(kvm_get_rflags);

void kvm_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags)
{
	if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP &&
	    vcpu->arch.singlestep_cs ==
			get_segment_selector(vcpu, VCPU_SREG_CS) &&
	    vcpu->arch.singlestep_rip == kvm_rip_read(vcpu))
		rflags |= X86_EFLAGS_TF | X86_EFLAGS_RF;
	kvm_x86_ops->set_rflags(vcpu, rflags);
}
EXPORT_SYMBOL_GPL(kvm_set_rflags);

EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_exit);
EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_inj_virq);
EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_page_fault);