| From stefan.bader@canonical.com Wed Apr 7 14:42:27 2010 |
| From: Gleb Natapov <gleb@redhat.com> |
| Date: Fri, 19 Mar 2010 15:47:32 +0100 |
| Subject: KVM: x86 emulator: Check IOPL level during io instruction emulation |
| To: stable@kernel.org |
| Cc: Marcelo Tosatti <mtosatti@redhat.com>, Avi Kivity <avi@redhat.com>, Gleb Natapov <gleb@redhat.com> |
| Message-ID: <1269010059-25309-5-git-send-email-stefan.bader@canonical.com> |
| |
| |
| From: Gleb Natapov <gleb@redhat.com> |
| |
| commit f850e2e603bf5a05b0aee7901857cf85715aa694 upstream |
| |
| Make emulator check that vcpu is allowed to execute IN, INS, OUT, |
| OUTS, CLI, STI. |
| |
| Signed-off-by: Gleb Natapov <gleb@redhat.com> |
| Signed-off-by: Avi Kivity <avi@redhat.com> |
| Signed-off-by: Stefan Bader <stefan.bader@canonical.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| --- |
| arch/x86/include/asm/kvm_host.h | 1 |
| arch/x86/kvm/emulate.c | 89 ++++++++++++++++++++++++++++++++++++---- |
| arch/x86/kvm/x86.c | 10 +--- |
| 3 files changed, 87 insertions(+), 13 deletions(-) |
| |
| --- a/arch/x86/include/asm/kvm_host.h |
| +++ b/arch/x86/include/asm/kvm_host.h |
| @@ -663,6 +663,7 @@ void kvm_disable_tdp(void); |
| |
| int load_pdptrs(struct kvm_vcpu *vcpu, unsigned long cr3); |
| int complete_pio(struct kvm_vcpu *vcpu); |
| +bool kvm_check_iopl(struct kvm_vcpu *vcpu); |
| |
| struct kvm_memory_slot *gfn_to_memslot_unaliased(struct kvm *kvm, gfn_t gfn); |
| |
| --- a/arch/x86/kvm/emulate.c |
| +++ b/arch/x86/kvm/emulate.c |
| @@ -1620,6 +1620,57 @@ emulate_sysexit(struct x86_emulate_ctxt |
| return 0; |
| } |
| |
| +static bool emulator_bad_iopl(struct x86_emulate_ctxt *ctxt) |
| +{ |
| + int iopl; |
| + if (ctxt->mode == X86EMUL_MODE_REAL) |
| + return false; |
| + if (ctxt->mode == X86EMUL_MODE_VM86) |
| + return true; |
| + iopl = (ctxt->eflags & X86_EFLAGS_IOPL) >> IOPL_SHIFT; |
| + return kvm_x86_ops->get_cpl(ctxt->vcpu) > iopl; |
| +} |
| + |
| +static bool emulator_io_port_access_allowed(struct x86_emulate_ctxt *ctxt, |
| + struct x86_emulate_ops *ops, |
| + u16 port, u16 len) |
| +{ |
| + struct kvm_segment tr_seg; |
| + int r; |
| + u16 io_bitmap_ptr; |
| + u8 perm, bit_idx = port & 0x7; |
| + unsigned mask = (1 << len) - 1; |
| + |
| + kvm_get_segment(ctxt->vcpu, &tr_seg, VCPU_SREG_TR); |
| + if (tr_seg.unusable) |
| + return false; |
| + if (tr_seg.limit < 103) |
| + return false; |
| + r = ops->read_std(tr_seg.base + 102, &io_bitmap_ptr, 2, ctxt->vcpu, |
| + NULL); |
| + if (r != X86EMUL_CONTINUE) |
| + return false; |
| + if (io_bitmap_ptr + port/8 > tr_seg.limit) |
| + return false; |
| + r = ops->read_std(tr_seg.base + io_bitmap_ptr + port/8, &perm, 1, |
| + ctxt->vcpu, NULL); |
| + if (r != X86EMUL_CONTINUE) |
| + return false; |
| + if ((perm >> bit_idx) & mask) |
| + return false; |
| + return true; |
| +} |
| + |
| +static bool emulator_io_permited(struct x86_emulate_ctxt *ctxt, |
| + struct x86_emulate_ops *ops, |
| + u16 port, u16 len) |
| +{ |
| + if (emulator_bad_iopl(ctxt)) |
| + if (!emulator_io_port_access_allowed(ctxt, ops, port, len)) |
| + return false; |
| + return true; |
| +} |
| + |
| int |
| x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops) |
| { |
| @@ -1779,7 +1830,12 @@ special_insn: |
| break; |
| case 0x6c: /* insb */ |
| case 0x6d: /* insw/insd */ |
| - if (kvm_emulate_pio_string(ctxt->vcpu, NULL, |
| + if (!emulator_io_permited(ctxt, ops, c->regs[VCPU_REGS_RDX], |
| + (c->d & ByteOp) ? 1 : c->op_bytes)) { |
| + kvm_inject_gp(ctxt->vcpu, 0); |
| + goto done; |
| + } |
| + if (kvm_emulate_pio_string(ctxt->vcpu, NULL, |
| 1, |
| (c->d & ByteOp) ? 1 : c->op_bytes, |
| c->rep_prefix ? |
| @@ -1795,6 +1851,11 @@ special_insn: |
| return 0; |
| case 0x6e: /* outsb */ |
| case 0x6f: /* outsw/outsd */ |
| + if (!emulator_io_permited(ctxt, ops, c->regs[VCPU_REGS_RDX], |
| + (c->d & ByteOp) ? 1 : c->op_bytes)) { |
| + kvm_inject_gp(ctxt->vcpu, 0); |
| + goto done; |
| + } |
| if (kvm_emulate_pio_string(ctxt->vcpu, NULL, |
| 0, |
| (c->d & ByteOp) ? 1 : c->op_bytes, |
| @@ -2094,7 +2155,13 @@ special_insn: |
| case 0xef: /* out (e/r)ax,dx */ |
| port = c->regs[VCPU_REGS_RDX]; |
| io_dir_in = 0; |
| - do_io: if (kvm_emulate_pio(ctxt->vcpu, NULL, io_dir_in, |
| + do_io: |
| + if (!emulator_io_permited(ctxt, ops, port, |
| + (c->d & ByteOp) ? 1 : c->op_bytes)) { |
| + kvm_inject_gp(ctxt->vcpu, 0); |
| + goto done; |
| + } |
| + if (kvm_emulate_pio(ctxt->vcpu, NULL, io_dir_in, |
| (c->d & ByteOp) ? 1 : c->op_bytes, |
| port) != 0) { |
| c->eip = saved_eip; |
| @@ -2119,13 +2186,21 @@ special_insn: |
| c->dst.type = OP_NONE; /* Disable writeback. */ |
| break; |
| case 0xfa: /* cli */ |
| - ctxt->eflags &= ~X86_EFLAGS_IF; |
| - c->dst.type = OP_NONE; /* Disable writeback. */ |
| + if (emulator_bad_iopl(ctxt)) |
| + kvm_inject_gp(ctxt->vcpu, 0); |
| + else { |
| + ctxt->eflags &= ~X86_EFLAGS_IF; |
| + c->dst.type = OP_NONE; /* Disable writeback. */ |
| + } |
| break; |
| case 0xfb: /* sti */ |
| - toggle_interruptibility(ctxt, X86_SHADOW_INT_STI); |
| - ctxt->eflags |= X86_EFLAGS_IF; |
| - c->dst.type = OP_NONE; /* Disable writeback. */ |
| + if (emulator_bad_iopl(ctxt)) |
| + kvm_inject_gp(ctxt->vcpu, 0); |
| + else { |
| + toggle_interruptibility(ctxt, X86_SHADOW_INT_STI); |
| + ctxt->eflags |= X86_EFLAGS_IF; |
| + c->dst.type = OP_NONE; /* Disable writeback. */ |
| + } |
| break; |
| case 0xfc: /* cld */ |
| ctxt->eflags &= ~EFLG_DF; |
| --- a/arch/x86/kvm/x86.c |
| +++ b/arch/x86/kvm/x86.c |
| @@ -3079,6 +3079,8 @@ int kvm_emulate_pio(struct kvm_vcpu *vcp |
| { |
| unsigned long val; |
| |
| + trace_kvm_pio(!in, port, size, 1); |
| + |
| vcpu->run->exit_reason = KVM_EXIT_IO; |
| vcpu->run->io.direction = in ? KVM_EXIT_IO_IN : KVM_EXIT_IO_OUT; |
| vcpu->run->io.size = vcpu->arch.pio.size = size; |
| @@ -3090,9 +3092,6 @@ int kvm_emulate_pio(struct kvm_vcpu *vcp |
| vcpu->arch.pio.down = 0; |
| vcpu->arch.pio.rep = 0; |
| |
| - trace_kvm_pio(vcpu->run->io.direction == KVM_EXIT_IO_OUT, port, |
| - size, 1); |
| - |
| val = kvm_register_read(vcpu, VCPU_REGS_RAX); |
| memcpy(vcpu->arch.pio_data, &val, 4); |
| |
| @@ -3111,6 +3110,8 @@ int kvm_emulate_pio_string(struct kvm_vc |
| unsigned now, in_page; |
| int ret = 0; |
| |
| + trace_kvm_pio(!in, port, size, count); |
| + |
| vcpu->run->exit_reason = KVM_EXIT_IO; |
| vcpu->run->io.direction = in ? KVM_EXIT_IO_IN : KVM_EXIT_IO_OUT; |
| vcpu->run->io.size = vcpu->arch.pio.size = size; |
| @@ -3122,9 +3123,6 @@ int kvm_emulate_pio_string(struct kvm_vc |
| vcpu->arch.pio.down = down; |
| vcpu->arch.pio.rep = rep; |
| |
| - trace_kvm_pio(vcpu->run->io.direction == KVM_EXIT_IO_OUT, port, |
| - size, count); |
| - |
| if (!count) { |
| kvm_x86_ops->skip_emulated_instruction(vcpu); |
| return 1; |