From 1064ac7eb51924205a7affd99c06f1f5d60c1475 Mon Sep 17 00:00:00 2001 From: Gleb Natapov <gleb@redhat.com> Date: Mon, 1 Feb 2010 17:14:56 +0200 Subject: [PATCH 5/7] Check IOPL level during io instruction emulation. Make emulator check that vcpu is allowed to execute IN, INS, OUT, OUTS, CLI, STI. Message-Id: <1265037298-12612-6-git-send-email-gleb@redhat.com> CVE: CVE-2010-0306 Bugzilla: 560698 Acked-by: Marcelo Tosatti <mtosatti@redhat.com> Acked-by: Juan Quintela <quintela@redhat.com> Acked-by: Chris Wright <chrisw@redhat.com> Signed-off-by: Gleb Natapov <gleb@redhat.com> --- arch/x86/include/asm/kvm_host.h | 1 + arch/x86/kvm/x86.c | 77 ++++++++++++++++++++++++++++++++------- arch/x86/kvm/x86_emulate.c | 18 +++++++--- 3 files changed, 77 insertions(+), 19 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 0ed305e..8a0a3fa 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -632,6 +632,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); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 8764c09..e709bc3 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -2784,12 +2784,63 @@ static struct kvm_io_device *vcpu_find_pio_dev(struct kvm_vcpu *vcpu, return kvm_io_bus_find_dev(&vcpu->kvm->pio_bus, addr, len, is_write); } +bool kvm_check_iopl(struct kvm_vcpu *vcpu) +{ + int iopl; + if (!(vcpu->arch.cr0 & X86_CR0_PE)) + return false; + if (kvm_x86_ops->get_rflags(vcpu) & X86_EFLAGS_VM) + return true; + iopl = (kvm_x86_ops->get_rflags(vcpu) & X86_EFLAGS_IOPL) >> IOPL_SHIFT; + return kvm_x86_ops->get_cpl(vcpu) > iopl; +} + +bool kvm_check_io_port_access_allowed(struct kvm_vcpu *vcpu, 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(vcpu, &tr_seg, VCPU_SREG_TR); + if (tr_seg.unusable) + return false; + if (tr_seg.limit < 103) + return false; + r = kvm_read_guest_virt_system(tr_seg.base + 102, &io_bitmap_ptr, 2, + vcpu, NULL); + if (r != X86EMUL_CONTINUE) + return false; + if (io_bitmap_ptr + port/8 >= tr_seg.limit) + return false; + r = kvm_read_guest_virt_system(tr_seg.base + io_bitmap_ptr + port/8, + &perm, 1, vcpu, NULL); + if (r != X86EMUL_CONTINUE) + return false; + if ((perm >> bit_idx) & mask) + return false; + return true; +} + int kvm_emulate_pio(struct kvm_vcpu *vcpu, struct kvm_run *run, int in, int size, unsigned port) { struct kvm_io_device *pio_dev; unsigned long val; + if (in) + KVMTRACE_2D(IO_READ, vcpu, port, (u32)size, handler); + else + KVMTRACE_2D(IO_WRITE, vcpu, port, (u32)size, handler); + + if (kvm_check_iopl(vcpu)) { + if (!kvm_check_io_port_access_allowed(vcpu, port, size)) { + kvm_inject_gp(vcpu, 0); + return 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; @@ -2801,13 +2852,6 @@ int kvm_emulate_pio(struct kvm_vcpu *vcpu, struct kvm_run *run, int in, vcpu->arch.pio.down = 0; vcpu->arch.pio.rep = 0; - if (vcpu->run->io.direction == KVM_EXIT_IO_IN) - KVMTRACE_2D(IO_READ, vcpu, vcpu->run->io.port, (u32)size, - handler); - else - KVMTRACE_2D(IO_WRITE, vcpu, vcpu->run->io.port, (u32)size, - handler); - val = kvm_register_read(vcpu, VCPU_REGS_RAX); memcpy(vcpu->arch.pio_data, &val, 4); @@ -2829,6 +2873,18 @@ int kvm_emulate_pio_string(struct kvm_vcpu *vcpu, struct kvm_run *run, int in, int ret = 0; struct kvm_io_device *pio_dev; + if (in) + KVMTRACE_2D(IO_READ, vcpu, port, (u32)size, handler); + else + KVMTRACE_2D(IO_WRITE, vcpu, port, (u32)size, handler); + + if (kvm_check_iopl(vcpu)) { + if (!kvm_check_io_port_access_allowed(vcpu, port, size)) { + kvm_inject_gp(vcpu, 0); + return 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; @@ -2840,13 +2896,6 @@ int kvm_emulate_pio_string(struct kvm_vcpu *vcpu, struct kvm_run *run, int in, vcpu->arch.pio.down = down; vcpu->arch.pio.rep = rep; - if (vcpu->run->io.direction == KVM_EXIT_IO_IN) - KVMTRACE_2D(IO_READ, vcpu, vcpu->run->io.port, (u32)size, - handler); - else - KVMTRACE_2D(IO_WRITE, vcpu, vcpu->run->io.port, (u32)size, - handler); - if (!count) { kvm_x86_ops->skip_emulated_instruction(vcpu); return 1; diff --git a/arch/x86/kvm/x86_emulate.c b/arch/x86/kvm/x86_emulate.c index 76278a9..f070769 100644 --- a/arch/x86/kvm/x86_emulate.c +++ b/arch/x86/kvm/x86_emulate.c @@ -1862,13 +1862,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 (kvm_check_iopl(ctxt->vcpu)) + 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 (kvm_check_iopl(ctxt->vcpu)) + 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; -- 1.6.3.rc4.29.g8146