KVM: x86 emulator: Fix task switch privilege checks

Currently, all task switches check privileges against the DPL of the
TSS. This is only correct for jmp/call to a TSS. If a task gate is used,
the DPL of this take gate is used for the check instead. Exceptions,
external interrupts and iret shouldn't perform any check.

[avi: kill kvm-kmod remnants]

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Avi Kivity <avi@redhat.com>
This commit is contained in:
Kevin Wolf 2012-02-08 14:34:38 +01:00 committed by Avi Kivity
parent 9cc815e469
commit 7f3d35fddd
6 changed files with 61 additions and 17 deletions

View File

@ -388,7 +388,7 @@ bool x86_page_table_writing_insn(struct x86_emulate_ctxt *ctxt);
#define EMULATION_INTERCEPTED 2 #define EMULATION_INTERCEPTED 2
int x86_emulate_insn(struct x86_emulate_ctxt *ctxt); int x86_emulate_insn(struct x86_emulate_ctxt *ctxt);
int emulator_task_switch(struct x86_emulate_ctxt *ctxt, int emulator_task_switch(struct x86_emulate_ctxt *ctxt,
u16 tss_selector, int reason, u16 tss_selector, int idt_index, int reason,
bool has_error_code, u32 error_code); bool has_error_code, u32 error_code);
int emulate_int_real(struct x86_emulate_ctxt *ctxt, int irq); int emulate_int_real(struct x86_emulate_ctxt *ctxt, int irq);
#endif /* _ASM_X86_KVM_X86_EMULATE_H */ #endif /* _ASM_X86_KVM_X86_EMULATE_H */

View File

@ -768,8 +768,8 @@ int kvm_emulate_wbinvd(struct kvm_vcpu *vcpu);
void kvm_get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg); void kvm_get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg);
int kvm_load_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector, int seg); int kvm_load_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector, int seg);
int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int reason, int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int idt_index,
bool has_error_code, u32 error_code); int reason, bool has_error_code, u32 error_code);
int kvm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0); int kvm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0);
int kvm_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3); int kvm_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3);

View File

@ -1152,6 +1152,22 @@ static int pio_in_emulated(struct x86_emulate_ctxt *ctxt,
return 1; return 1;
} }
static int read_interrupt_descriptor(struct x86_emulate_ctxt *ctxt,
u16 index, struct desc_struct *desc)
{
struct desc_ptr dt;
ulong addr;
ctxt->ops->get_idt(ctxt, &dt);
if (dt.size < index * 8 + 7)
return emulate_gp(ctxt, index << 3 | 0x2);
addr = dt.address + index * 8;
return ctxt->ops->read_std(ctxt, addr, desc, sizeof *desc,
&ctxt->exception);
}
static void get_descriptor_table_ptr(struct x86_emulate_ctxt *ctxt, static void get_descriptor_table_ptr(struct x86_emulate_ctxt *ctxt,
u16 selector, struct desc_ptr *dt) u16 selector, struct desc_ptr *dt)
{ {
@ -2421,7 +2437,7 @@ static int task_switch_32(struct x86_emulate_ctxt *ctxt,
} }
static int emulator_do_task_switch(struct x86_emulate_ctxt *ctxt, static int emulator_do_task_switch(struct x86_emulate_ctxt *ctxt,
u16 tss_selector, int reason, u16 tss_selector, int idt_index, int reason,
bool has_error_code, u32 error_code) bool has_error_code, u32 error_code)
{ {
struct x86_emulate_ops *ops = ctxt->ops; struct x86_emulate_ops *ops = ctxt->ops;
@ -2443,12 +2459,35 @@ static int emulator_do_task_switch(struct x86_emulate_ctxt *ctxt,
/* FIXME: check that next_tss_desc is tss */ /* FIXME: check that next_tss_desc is tss */
if (reason != TASK_SWITCH_IRET) { /*
if ((tss_selector & 3) > next_tss_desc.dpl || * Check privileges. The three cases are task switch caused by...
ops->cpl(ctxt) > next_tss_desc.dpl) *
return emulate_gp(ctxt, 0); * 1. jmp/call/int to task gate: Check against DPL of the task gate
* 2. Exception/IRQ/iret: No check is performed
* 3. jmp/call to TSS: Check agains DPL of the TSS
*/
if (reason == TASK_SWITCH_GATE) {
if (idt_index != -1) {
/* Software interrupts */
struct desc_struct task_gate_desc;
int dpl;
ret = read_interrupt_descriptor(ctxt, idt_index,
&task_gate_desc);
if (ret != X86EMUL_CONTINUE)
return ret;
dpl = task_gate_desc.dpl;
if ((tss_selector & 3) > dpl || ops->cpl(ctxt) > dpl)
return emulate_gp(ctxt, (idt_index << 3) | 0x2);
}
} else if (reason != TASK_SWITCH_IRET) {
int dpl = next_tss_desc.dpl;
if ((tss_selector & 3) > dpl || ops->cpl(ctxt) > dpl)
return emulate_gp(ctxt, tss_selector);
} }
desc_limit = desc_limit_scaled(&next_tss_desc); desc_limit = desc_limit_scaled(&next_tss_desc);
if (!next_tss_desc.p || if (!next_tss_desc.p ||
((desc_limit < 0x67 && (next_tss_desc.type & 8)) || ((desc_limit < 0x67 && (next_tss_desc.type & 8)) ||
@ -2501,7 +2540,7 @@ static int emulator_do_task_switch(struct x86_emulate_ctxt *ctxt,
} }
int emulator_task_switch(struct x86_emulate_ctxt *ctxt, int emulator_task_switch(struct x86_emulate_ctxt *ctxt,
u16 tss_selector, int reason, u16 tss_selector, int idt_index, int reason,
bool has_error_code, u32 error_code) bool has_error_code, u32 error_code)
{ {
int rc; int rc;
@ -2509,7 +2548,7 @@ int emulator_task_switch(struct x86_emulate_ctxt *ctxt,
ctxt->_eip = ctxt->eip; ctxt->_eip = ctxt->eip;
ctxt->dst.type = OP_NONE; ctxt->dst.type = OP_NONE;
rc = emulator_do_task_switch(ctxt, tss_selector, reason, rc = emulator_do_task_switch(ctxt, tss_selector, idt_index, reason,
has_error_code, error_code); has_error_code, error_code);
if (rc == X86EMUL_CONTINUE) if (rc == X86EMUL_CONTINUE)

View File

@ -2799,7 +2799,10 @@ static int task_switch_interception(struct vcpu_svm *svm)
(int_vec == OF_VECTOR || int_vec == BP_VECTOR))) (int_vec == OF_VECTOR || int_vec == BP_VECTOR)))
skip_emulated_instruction(&svm->vcpu); skip_emulated_instruction(&svm->vcpu);
if (kvm_task_switch(&svm->vcpu, tss_selector, reason, if (int_type != SVM_EXITINTINFO_TYPE_SOFT)
int_vec = -1;
if (kvm_task_switch(&svm->vcpu, tss_selector, int_vec, reason,
has_error_code, error_code) == EMULATE_FAIL) { has_error_code, error_code) == EMULATE_FAIL) {
svm->vcpu.run->exit_reason = KVM_EXIT_INTERNAL_ERROR; svm->vcpu.run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
svm->vcpu.run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION; svm->vcpu.run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;

View File

@ -4658,9 +4658,10 @@ static int handle_task_switch(struct kvm_vcpu *vcpu)
bool has_error_code = false; bool has_error_code = false;
u32 error_code = 0; u32 error_code = 0;
u16 tss_selector; u16 tss_selector;
int reason, type, idt_v; int reason, type, idt_v, idt_index;
idt_v = (vmx->idt_vectoring_info & VECTORING_INFO_VALID_MASK); idt_v = (vmx->idt_vectoring_info & VECTORING_INFO_VALID_MASK);
idt_index = (vmx->idt_vectoring_info & VECTORING_INFO_VECTOR_MASK);
type = (vmx->idt_vectoring_info & VECTORING_INFO_TYPE_MASK); type = (vmx->idt_vectoring_info & VECTORING_INFO_TYPE_MASK);
exit_qualification = vmcs_readl(EXIT_QUALIFICATION); exit_qualification = vmcs_readl(EXIT_QUALIFICATION);
@ -4698,8 +4699,9 @@ static int handle_task_switch(struct kvm_vcpu *vcpu)
type != INTR_TYPE_NMI_INTR)) type != INTR_TYPE_NMI_INTR))
skip_emulated_instruction(vcpu); skip_emulated_instruction(vcpu);
if (kvm_task_switch(vcpu, tss_selector, reason, if (kvm_task_switch(vcpu, tss_selector,
has_error_code, error_code) == EMULATE_FAIL) { type == INTR_TYPE_SOFT_INTR ? idt_index : -1, reason,
has_error_code, error_code) == EMULATE_FAIL) {
vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION; vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
vcpu->run->internal.ndata = 0; vcpu->run->internal.ndata = 0;

View File

@ -5655,15 +5655,15 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
return 0; return 0;
} }
int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int reason, int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int idt_index,
bool has_error_code, u32 error_code) int reason, bool has_error_code, u32 error_code)
{ {
struct x86_emulate_ctxt *ctxt = &vcpu->arch.emulate_ctxt; struct x86_emulate_ctxt *ctxt = &vcpu->arch.emulate_ctxt;
int ret; int ret;
init_emulate_ctxt(vcpu); init_emulate_ctxt(vcpu);
ret = emulator_task_switch(ctxt, tss_selector, reason, ret = emulator_task_switch(ctxt, tss_selector, idt_index, reason,
has_error_code, error_code); has_error_code, error_code);
if (ret) if (ret)