From owner-svn-src-all@FreeBSD.ORG Tue May 27 04:26:24 2014 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 63E50BD; Tue, 27 May 2014 04:26:24 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 446932B17; Tue, 27 May 2014 04:26:24 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.8/8.14.8) with ESMTP id s4R4QOQG066532; Tue, 27 May 2014 04:26:24 GMT (envelope-from neel@svn.freebsd.org) Received: (from neel@localhost) by svn.freebsd.org (8.14.8/8.14.8/Submit) id s4R4QNCZ066526; Tue, 27 May 2014 04:26:23 GMT (envelope-from neel@svn.freebsd.org) Message-Id: <201405270426.s4R4QNCZ066526@svn.freebsd.org> From: Neel Natu Date: Tue, 27 May 2014 04:26:23 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r266724 - in head: sys/amd64/include sys/amd64/vmm usr.sbin/bhyve X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 27 May 2014 04:26:24 -0000 Author: neel Date: Tue May 27 04:26:22 2014 New Revision: 266724 URL: http://svnweb.freebsd.org/changeset/base/266724 Log: Add segment protection and limits violation checks in vie_calculate_gla() for 32-bit x86 guests. Tested using ins/outs executed in a FreeBSD/i386 guest. Modified: head/sys/amd64/include/vmm.h head/sys/amd64/include/vmm_instruction_emul.h head/sys/amd64/vmm/vmm_instruction_emul.c head/sys/amd64/vmm/vmm_ioport.c head/usr.sbin/bhyve/inout.c Modified: head/sys/amd64/include/vmm.h ============================================================================== --- head/sys/amd64/include/vmm.h Tue May 27 02:00:43 2014 (r266723) +++ head/sys/amd64/include/vmm.h Tue May 27 04:26:22 2014 (r266724) @@ -321,6 +321,11 @@ struct seg_desc { uint32_t limit; uint32_t access; }; +#define SEG_DESC_TYPE(desc) ((desc)->access & 0x001f) +#define SEG_DESC_PRESENT(desc) ((desc)->access & 0x0080) +#define SEG_DESC_DEF32(desc) ((desc)->access & 0x4000) +#define SEG_DESC_GRANULARITY(desc) ((desc)->access & 0x8000) +#define SEG_DESC_UNUSABLE(desc) ((desc)->access & 0x10000) enum vm_cpu_mode { CPU_MODE_COMPATIBILITY, /* IA-32E mode (CS.L = 0) */ Modified: head/sys/amd64/include/vmm_instruction_emul.h ============================================================================== --- head/sys/amd64/include/vmm_instruction_emul.h Tue May 27 02:00:43 2014 (r266723) +++ head/sys/amd64/include/vmm_instruction_emul.h Tue May 27 04:26:22 2014 (r266724) @@ -29,6 +29,8 @@ #ifndef _VMM_INSTRUCTION_EMUL_H_ #define _VMM_INSTRUCTION_EMUL_H_ +#include + /* * Callback functions to read and write memory regions. */ @@ -67,8 +69,9 @@ int vie_canonical_check(enum vm_cpu_mode uint64_t vie_size2mask(int size); -int vie_calculate_gla(enum vm_cpu_mode cpu_mode, int addrsize, - enum vm_reg_name seg, struct seg_desc *desc, uint64_t off, uint64_t *gla); +int vie_calculate_gla(enum vm_cpu_mode cpu_mode, enum vm_reg_name seg, + struct seg_desc *desc, uint64_t off, int length, int addrsize, int prot, + uint64_t *gla); #ifdef _KERNEL /* Modified: head/sys/amd64/vmm/vmm_instruction_emul.c ============================================================================== --- head/sys/amd64/vmm/vmm_instruction_emul.c Tue May 27 02:00:43 2014 (r266723) +++ head/sys/amd64/vmm/vmm_instruction_emul.c Tue May 27 04:26:22 2014 (r266724) @@ -608,16 +608,92 @@ vie_size2mask(int size) } int -vie_calculate_gla(enum vm_cpu_mode cpu_mode, int addrsize, enum vm_reg_name seg, - struct seg_desc *desc, uint64_t offset, uint64_t *gla) +vie_calculate_gla(enum vm_cpu_mode cpu_mode, enum vm_reg_name seg, + struct seg_desc *desc, uint64_t offset, int length, int addrsize, + int prot, uint64_t *gla) { - uint64_t segbase; - int glasize; + uint64_t low_limit, high_limit, segbase; + int glasize, type; KASSERT(seg >= VM_REG_GUEST_ES && seg <= VM_REG_GUEST_GS, ("%s: invalid segment %d", __func__, seg)); + KASSERT(length == 1 || length == 2 || length == 4 || length == 8, + ("%s: invalid operand size %d", __func__, length)); + KASSERT((prot & ~(PROT_READ | PROT_WRITE)) == 0, + ("%s: invalid prot %#x", __func__, prot)); - glasize = (cpu_mode == CPU_MODE_64BIT) ? 8 : 4; + if (cpu_mode == CPU_MODE_64BIT) { + KASSERT(addrsize == 4 || addrsize == 8, ("%s: invalid address " + "size %d for cpu_mode %d", __func__, addrsize, cpu_mode)); + glasize = 8; + } else { + KASSERT(addrsize == 2 || addrsize == 4, ("%s: invalid address " + "size %d for cpu mode %d", __func__, addrsize, cpu_mode)); + glasize = 4; + /* + * If the segment selector is loaded with a NULL selector + * then the descriptor is unusable and attempting to use + * it results in a #GP(0). + */ + if (SEG_DESC_UNUSABLE(desc)) + return (-1); + + /* + * The processor generates a #NP exception when a segment + * register is loaded with a selector that points to a + * descriptor that is not present. If this was the case then + * it would have been checked before the VM-exit. + */ + KASSERT(SEG_DESC_PRESENT(desc), ("segment %d not present: %#x", + seg, desc->access)); + + /* + * The descriptor type must indicate a code/data segment. + */ + type = SEG_DESC_TYPE(desc); + KASSERT(type >= 16 && type <= 31, ("segment %d has invalid " + "descriptor type %#x", seg, type)); + + if (prot & PROT_READ) { + /* #GP on a read access to a exec-only code segment */ + if ((type & 0xA) == 0x8) + return (-1); + } + + if (prot & PROT_WRITE) { + /* + * #GP on a write access to a code segment or a + * read-only data segment. + */ + if (type & 0x8) /* code segment */ + return (-1); + + if ((type & 0xA) == 0) /* read-only data seg */ + return (-1); + } + + /* + * 'desc->limit' is fully expanded taking granularity into + * account. + */ + if ((type & 0xC) == 0x4) { + /* expand-down data segment */ + low_limit = desc->limit + 1; + high_limit = SEG_DESC_DEF32(desc) ? 0xffffffff : 0xffff; + } else { + /* code segment or expand-up data segment */ + low_limit = 0; + high_limit = desc->limit; + } + + while (length > 0) { + offset &= vie_size2mask(addrsize); + if (offset < low_limit || offset > high_limit) + return (-1); + offset++; + length--; + } + } /* * In 64-bit mode all segments except %fs and %gs have a segment Modified: head/sys/amd64/vmm/vmm_ioport.c ============================================================================== --- head/sys/amd64/vmm/vmm_ioport.c Tue May 27 02:00:43 2014 (r266723) +++ head/sys/amd64/vmm/vmm_ioport.c Tue May 27 04:26:22 2014 (r266724) @@ -143,36 +143,6 @@ done: static int emulate_inout_str(struct vm *vm, int vcpuid, struct vm_exit *vmexit, bool *retu) { - struct vm_inout_str *vis; - int in; - - vis = &vmexit->u.inout_str; - in = vis->inout.in; - - /* - * ins/outs VM exit takes precedence over the following error - * conditions that would ordinarily be checked by the processor: - * - * - #GP(0) due to segment being unusable. - * - #GP(0) due to memory operand effective address outside the limit - * of the segment. - * - #AC(0) if alignment checking is enabled and an unaligned memory - * reference is made at CPL=3 - */ - - /* - * XXX - * inout string emulation only supported in 64-bit mode. - * - * The #GP(0) fault conditions described above don't apply in - * 64-bit mode. - */ - if (vis->paging.cpu_mode != CPU_MODE_64BIT) { - VCPU_CTR1(vm, vcpuid, "ins/outs not emulated in cpu mode %d", - vis->paging.cpu_mode); - return (EINVAL); - } - *retu = true; return (0); /* Return to userspace to finish emulation */ } Modified: head/usr.sbin/bhyve/inout.c ============================================================================== --- head/usr.sbin/bhyve/inout.c Tue May 27 02:00:43 2014 (r266723) +++ head/usr.sbin/bhyve/inout.c Tue May 27 04:26:22 2014 (r266724) @@ -103,7 +103,7 @@ register_default_iohandler(int start, in int emulate_inout(struct vmctx *ctx, int vcpu, struct vm_exit *vmexit, int strict) { - int addrsize, bytes, flags, in, port, rep; + int addrsize, bytes, flags, in, port, prot, rep; uint32_t val; inout_func_t handler; void *arg; @@ -141,6 +141,7 @@ emulate_inout(struct vmctx *ctx, int vcp vis = &vmexit->u.inout_str; rep = vis->inout.rep; addrsize = vis->addrsize; + prot = in ? PROT_WRITE : PROT_READ; assert(addrsize == 2 || addrsize == 4 || addrsize == 8); /* Index register */ @@ -152,8 +153,8 @@ emulate_inout(struct vmctx *ctx, int vcp while (count) { if (vie_calculate_gla(vis->paging.cpu_mode, - vis->addrsize, vis->seg_name, &vis->seg_desc, - index, &gla)) { + vis->seg_name, &vis->seg_desc, index, bytes, + addrsize, prot, &gla)) { error = vm_inject_exception2(ctx, vcpu, IDT_GP, 0); assert(error == 0); @@ -161,7 +162,7 @@ emulate_inout(struct vmctx *ctx, int vcp } error = vm_gla2gpa(ctx, vcpu, &vis->paging, gla, bytes, - in ? PROT_WRITE : PROT_READ, iov, nitems(iov)); + prot, iov, nitems(iov)); assert(error == 0 || error == 1 || error == -1); if (error) { retval = (error == 1) ? INOUT_RESTART :