diff options
author | Kevin Wolf <kwolf@redhat.com> | 2012-02-08 08:34:38 -0500 |
---|---|---|
committer | Avi Kivity <avi@redhat.com> | 2012-03-08 07:10:26 -0500 |
commit | 7f3d35fddd173e52886d03bc34b5b5d6f5bea343 (patch) | |
tree | 9561913495a92c398b9b8e372d4e9a5c1d55c7f4 | |
parent | 9cc815e46911486f52bec60517d0f7b40d323bbc (diff) |
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>
-rw-r--r-- | arch/x86/include/asm/kvm_emulate.h | 2 | ||||
-rw-r--r-- | arch/x86/include/asm/kvm_host.h | 4 | ||||
-rw-r--r-- | arch/x86/kvm/emulate.c | 53 | ||||
-rw-r--r-- | arch/x86/kvm/svm.c | 5 | ||||
-rw-r--r-- | arch/x86/kvm/vmx.c | 8 | ||||
-rw-r--r-- | arch/x86/kvm/x86.c | 6 |
6 files changed, 61 insertions, 17 deletions
diff --git a/arch/x86/include/asm/kvm_emulate.h b/arch/x86/include/asm/kvm_emulate.h index 7b9cfc4878af..df437b68f42b 100644 --- a/arch/x86/include/asm/kvm_emulate.h +++ b/arch/x86/include/asm/kvm_emulate.h | |||
@@ -388,7 +388,7 @@ bool x86_page_table_writing_insn(struct x86_emulate_ctxt *ctxt); | |||
388 | #define EMULATION_INTERCEPTED 2 | 388 | #define EMULATION_INTERCEPTED 2 |
389 | int x86_emulate_insn(struct x86_emulate_ctxt *ctxt); | 389 | int x86_emulate_insn(struct x86_emulate_ctxt *ctxt); |
390 | int emulator_task_switch(struct x86_emulate_ctxt *ctxt, | 390 | int emulator_task_switch(struct x86_emulate_ctxt *ctxt, |
391 | u16 tss_selector, int reason, | 391 | u16 tss_selector, int idt_index, int reason, |
392 | bool has_error_code, u32 error_code); | 392 | bool has_error_code, u32 error_code); |
393 | int emulate_int_real(struct x86_emulate_ctxt *ctxt, int irq); | 393 | int emulate_int_real(struct x86_emulate_ctxt *ctxt, int irq); |
394 | #endif /* _ASM_X86_KVM_X86_EMULATE_H */ | 394 | #endif /* _ASM_X86_KVM_X86_EMULATE_H */ |
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 74c9edf2bb18..e216ba066e79 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h | |||
@@ -768,8 +768,8 @@ int kvm_emulate_wbinvd(struct kvm_vcpu *vcpu); | |||
768 | void kvm_get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg); | 768 | void kvm_get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg); |
769 | int kvm_load_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector, int seg); | 769 | int kvm_load_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector, int seg); |
770 | 770 | ||
771 | int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int reason, | 771 | int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int idt_index, |
772 | bool has_error_code, u32 error_code); | 772 | int reason, bool has_error_code, u32 error_code); |
773 | 773 | ||
774 | int kvm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0); | 774 | int kvm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0); |
775 | int kvm_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3); | 775 | int kvm_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3); |
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 71450aca3b86..fa310a48591c 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c | |||
@@ -1152,6 +1152,22 @@ static int pio_in_emulated(struct x86_emulate_ctxt *ctxt, | |||
1152 | return 1; | 1152 | return 1; |
1153 | } | 1153 | } |
1154 | 1154 | ||
1155 | static int read_interrupt_descriptor(struct x86_emulate_ctxt *ctxt, | ||
1156 | u16 index, struct desc_struct *desc) | ||
1157 | { | ||
1158 | struct desc_ptr dt; | ||
1159 | ulong addr; | ||
1160 | |||
1161 | ctxt->ops->get_idt(ctxt, &dt); | ||
1162 | |||
1163 | if (dt.size < index * 8 + 7) | ||
1164 | return emulate_gp(ctxt, index << 3 | 0x2); | ||
1165 | |||
1166 | addr = dt.address + index * 8; | ||
1167 | return ctxt->ops->read_std(ctxt, addr, desc, sizeof *desc, | ||
1168 | &ctxt->exception); | ||
1169 | } | ||
1170 | |||
1155 | static void get_descriptor_table_ptr(struct x86_emulate_ctxt *ctxt, | 1171 | static void get_descriptor_table_ptr(struct x86_emulate_ctxt *ctxt, |
1156 | u16 selector, struct desc_ptr *dt) | 1172 | u16 selector, struct desc_ptr *dt) |
1157 | { | 1173 | { |
@@ -2421,7 +2437,7 @@ static int task_switch_32(struct x86_emulate_ctxt *ctxt, | |||
2421 | } | 2437 | } |
2422 | 2438 | ||
2423 | static int emulator_do_task_switch(struct x86_emulate_ctxt *ctxt, | 2439 | static int emulator_do_task_switch(struct x86_emulate_ctxt *ctxt, |
2424 | u16 tss_selector, int reason, | 2440 | u16 tss_selector, int idt_index, int reason, |
2425 | bool has_error_code, u32 error_code) | 2441 | bool has_error_code, u32 error_code) |
2426 | { | 2442 | { |
2427 | struct x86_emulate_ops *ops = ctxt->ops; | 2443 | struct x86_emulate_ops *ops = ctxt->ops; |
@@ -2443,12 +2459,35 @@ static int emulator_do_task_switch(struct x86_emulate_ctxt *ctxt, | |||
2443 | 2459 | ||
2444 | /* FIXME: check that next_tss_desc is tss */ | 2460 | /* FIXME: check that next_tss_desc is tss */ |
2445 | 2461 | ||
2446 | if (reason != TASK_SWITCH_IRET) { | 2462 | /* |
2447 | if ((tss_selector & 3) > next_tss_desc.dpl || | 2463 | * Check privileges. The three cases are task switch caused by... |
2448 | ops->cpl(ctxt) > next_tss_desc.dpl) | 2464 | * |
2449 | return emulate_gp(ctxt, 0); | 2465 | * 1. jmp/call/int to task gate: Check against DPL of the task gate |
2466 | * 2. Exception/IRQ/iret: No check is performed | ||
2467 | * 3. jmp/call to TSS: Check agains DPL of the TSS | ||
2468 | */ | ||
2469 | if (reason == TASK_SWITCH_GATE) { | ||
2470 | if (idt_index != -1) { | ||
2471 | /* Software interrupts */ | ||
2472 | struct desc_struct task_gate_desc; | ||
2473 | int dpl; | ||
2474 | |||
2475 | ret = read_interrupt_descriptor(ctxt, idt_index, | ||
2476 | &task_gate_desc); | ||
2477 | if (ret != X86EMUL_CONTINUE) | ||
2478 | return ret; | ||
2479 | |||
2480 | dpl = task_gate_desc.dpl; | ||
2481 | if ((tss_selector & 3) > dpl || ops->cpl(ctxt) > dpl) | ||
2482 | return emulate_gp(ctxt, (idt_index << 3) | 0x2); | ||
2483 | } | ||
2484 | } else if (reason != TASK_SWITCH_IRET) { | ||
2485 | int dpl = next_tss_desc.dpl; | ||
2486 | if ((tss_selector & 3) > dpl || ops->cpl(ctxt) > dpl) | ||
2487 | return emulate_gp(ctxt, tss_selector); | ||
2450 | } | 2488 | } |
2451 | 2489 | ||
2490 | |||
2452 | desc_limit = desc_limit_scaled(&next_tss_desc); | 2491 | desc_limit = desc_limit_scaled(&next_tss_desc); |
2453 | if (!next_tss_desc.p || | 2492 | if (!next_tss_desc.p || |
2454 | ((desc_limit < 0x67 && (next_tss_desc.type & 8)) || | 2493 | ((desc_limit < 0x67 && (next_tss_desc.type & 8)) || |
@@ -2501,7 +2540,7 @@ static int emulator_do_task_switch(struct x86_emulate_ctxt *ctxt, | |||
2501 | } | 2540 | } |
2502 | 2541 | ||
2503 | int emulator_task_switch(struct x86_emulate_ctxt *ctxt, | 2542 | int emulator_task_switch(struct x86_emulate_ctxt *ctxt, |
2504 | u16 tss_selector, int reason, | 2543 | u16 tss_selector, int idt_index, int reason, |
2505 | bool has_error_code, u32 error_code) | 2544 | bool has_error_code, u32 error_code) |
2506 | { | 2545 | { |
2507 | int rc; | 2546 | int rc; |
@@ -2509,7 +2548,7 @@ int emulator_task_switch(struct x86_emulate_ctxt *ctxt, | |||
2509 | ctxt->_eip = ctxt->eip; | 2548 | ctxt->_eip = ctxt->eip; |
2510 | ctxt->dst.type = OP_NONE; | 2549 | ctxt->dst.type = OP_NONE; |
2511 | 2550 | ||
2512 | rc = emulator_do_task_switch(ctxt, tss_selector, reason, | 2551 | rc = emulator_do_task_switch(ctxt, tss_selector, idt_index, reason, |
2513 | has_error_code, error_code); | 2552 | has_error_code, error_code); |
2514 | 2553 | ||
2515 | if (rc == X86EMUL_CONTINUE) | 2554 | if (rc == X86EMUL_CONTINUE) |
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index 0b7690ee20bd..95cdeaf9c718 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c | |||
@@ -2799,7 +2799,10 @@ static int task_switch_interception(struct vcpu_svm *svm) | |||
2799 | (int_vec == OF_VECTOR || int_vec == BP_VECTOR))) | 2799 | (int_vec == OF_VECTOR || int_vec == BP_VECTOR))) |
2800 | skip_emulated_instruction(&svm->vcpu); | 2800 | skip_emulated_instruction(&svm->vcpu); |
2801 | 2801 | ||
2802 | if (kvm_task_switch(&svm->vcpu, tss_selector, reason, | 2802 | if (int_type != SVM_EXITINTINFO_TYPE_SOFT) |
2803 | int_vec = -1; | ||
2804 | |||
2805 | if (kvm_task_switch(&svm->vcpu, tss_selector, int_vec, reason, | ||
2803 | has_error_code, error_code) == EMULATE_FAIL) { | 2806 | has_error_code, error_code) == EMULATE_FAIL) { |
2804 | svm->vcpu.run->exit_reason = KVM_EXIT_INTERNAL_ERROR; | 2807 | svm->vcpu.run->exit_reason = KVM_EXIT_INTERNAL_ERROR; |
2805 | svm->vcpu.run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION; | 2808 | svm->vcpu.run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION; |
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index d2bd719925a6..124a0952a040 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c | |||
@@ -4658,9 +4658,10 @@ static int handle_task_switch(struct kvm_vcpu *vcpu) | |||
4658 | bool has_error_code = false; | 4658 | bool has_error_code = false; |
4659 | u32 error_code = 0; | 4659 | u32 error_code = 0; |
4660 | u16 tss_selector; | 4660 | u16 tss_selector; |
4661 | int reason, type, idt_v; | 4661 | int reason, type, idt_v, idt_index; |
4662 | 4662 | ||
4663 | idt_v = (vmx->idt_vectoring_info & VECTORING_INFO_VALID_MASK); | 4663 | idt_v = (vmx->idt_vectoring_info & VECTORING_INFO_VALID_MASK); |
4664 | idt_index = (vmx->idt_vectoring_info & VECTORING_INFO_VECTOR_MASK); | ||
4664 | type = (vmx->idt_vectoring_info & VECTORING_INFO_TYPE_MASK); | 4665 | type = (vmx->idt_vectoring_info & VECTORING_INFO_TYPE_MASK); |
4665 | 4666 | ||
4666 | exit_qualification = vmcs_readl(EXIT_QUALIFICATION); | 4667 | exit_qualification = vmcs_readl(EXIT_QUALIFICATION); |
@@ -4698,8 +4699,9 @@ static int handle_task_switch(struct kvm_vcpu *vcpu) | |||
4698 | type != INTR_TYPE_NMI_INTR)) | 4699 | type != INTR_TYPE_NMI_INTR)) |
4699 | skip_emulated_instruction(vcpu); | 4700 | skip_emulated_instruction(vcpu); |
4700 | 4701 | ||
4701 | if (kvm_task_switch(vcpu, tss_selector, reason, | 4702 | if (kvm_task_switch(vcpu, tss_selector, |
4702 | has_error_code, error_code) == EMULATE_FAIL) { | 4703 | type == INTR_TYPE_SOFT_INTR ? idt_index : -1, reason, |
4704 | has_error_code, error_code) == EMULATE_FAIL) { | ||
4703 | vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; | 4705 | vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; |
4704 | vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION; | 4706 | vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION; |
4705 | vcpu->run->internal.ndata = 0; | 4707 | vcpu->run->internal.ndata = 0; |
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index ca74c1dadf3a..490a1b1a255f 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c | |||
@@ -5655,15 +5655,15 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, | |||
5655 | return 0; | 5655 | return 0; |
5656 | } | 5656 | } |
5657 | 5657 | ||
5658 | int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int reason, | 5658 | int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int idt_index, |
5659 | bool has_error_code, u32 error_code) | 5659 | int reason, bool has_error_code, u32 error_code) |
5660 | { | 5660 | { |
5661 | struct x86_emulate_ctxt *ctxt = &vcpu->arch.emulate_ctxt; | 5661 | struct x86_emulate_ctxt *ctxt = &vcpu->arch.emulate_ctxt; |
5662 | int ret; | 5662 | int ret; |
5663 | 5663 | ||
5664 | init_emulate_ctxt(vcpu); | 5664 | init_emulate_ctxt(vcpu); |
5665 | 5665 | ||
5666 | ret = emulator_task_switch(ctxt, tss_selector, reason, | 5666 | ret = emulator_task_switch(ctxt, tss_selector, idt_index, reason, |
5667 | has_error_code, error_code); | 5667 | has_error_code, error_code); |
5668 | 5668 | ||
5669 | if (ret) | 5669 | if (ret) |