[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen-unstable] [HVM] Add/fix access rights and limit checks to INS/OUTS emulation
# HG changeset patch # User kfraser@xxxxxxxxxxxxxxxxxxxxx # Node ID b75574cb80a3ce6a01cc206d61488ab63e7d58e6 # Parent 519a74928bd407fb5f1f3d62f4e28493a0443611 [HVM] Add/fix access rights and limit checks to INS/OUTS emulation Since these instructions are documented to take their intercepts before these checks are being done in hardware, they must be carried out in software. Signed-off-by: Jan Beulich <jbeulich@xxxxxxxxxx> --- xen/arch/x86/hvm/svm/svm.c | 74 ++++++++++++++++++++++++++----- xen/arch/x86/hvm/vmx/vmx.c | 106 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 153 insertions(+), 27 deletions(-) diff -r 519a74928bd4 -r b75574cb80a3 xen/arch/x86/hvm/svm/svm.c --- a/xen/arch/x86/hvm/svm/svm.c Tue Nov 28 11:45:54 2006 +0000 +++ b/xen/arch/x86/hvm/svm/svm.c Tue Nov 28 11:46:39 2006 +0000 @@ -1233,8 +1233,7 @@ static inline int svm_get_io_address( unsigned long *count, unsigned long *addr) { unsigned long reg; - unsigned int asize = 0; - unsigned int isize; + unsigned int asize, isize; int long_mode = 0; segment_selector_t *seg = NULL; struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; @@ -1267,17 +1266,25 @@ static inline int svm_get_io_address( reg = regs->esi; if (!seg) /* If no prefix, used DS. */ seg = &vmcb->ds; + if (!long_mode && (seg->attributes.fields.type & 0xa) == 0x8) { + svm_inject_exception(v, TRAP_gp_fault, 1, 0); + return 0; + } } else { reg = regs->edi; seg = &vmcb->es; /* Note: This is ALWAYS ES. */ + if (!long_mode && (seg->attributes.fields.type & 0xa) != 0x2) { + svm_inject_exception(v, TRAP_gp_fault, 1, 0); + return 0; + } } /* If the segment isn't present, give GP fault! */ if (!long_mode && !seg->attributes.fields.p) { - svm_inject_exception(v, TRAP_gp_fault, 1, seg->sel); + svm_inject_exception(v, TRAP_gp_fault, 1, 0); return 0; } @@ -1294,16 +1301,59 @@ static inline int svm_get_io_address( if (!info.fields.rep) *count = 1; - if (!long_mode) { - if (*addr > seg->limit) - { - svm_inject_exception(v, TRAP_gp_fault, 1, seg->sel); + if (!long_mode) + { + ASSERT(*addr == (u32)*addr); + if ((u32)(*addr + size - 1) < (u32)*addr || + (seg->attributes.fields.type & 0xc) != 0x4 ? + *addr + size - 1 > seg->limit : + *addr <= seg->limit) + { + svm_inject_exception(v, TRAP_gp_fault, 1, 0); return 0; - } - else - { - *addr += seg->base; - } + } + + /* Check the limit for repeated instructions, as above we checked only + the first instance. Truncate the count if a limit violation would + occur. Note that the checking is not necessary for page granular + segments as transfers crossing page boundaries will be broken up + anyway. */ + if (!seg->attributes.fields.g && *count > 1) + { + if ((seg->attributes.fields.type & 0xc) != 0x4) + { + /* expand-up */ + if (!(regs->eflags & EF_DF)) + { + if (*addr + *count * size - 1 < *addr || + *addr + *count * size - 1 > seg->limit) + *count = (seg->limit + 1UL - *addr) / size; + } + else + { + if (*count - 1 > *addr / size) + *count = *addr / size + 1; + } + } + else + { + /* expand-down */ + if (!(regs->eflags & EF_DF)) + { + if (*count - 1 > -(s32)*addr / size) + *count = -(s32)*addr / size + 1UL; + } + else + { + if (*addr < (*count - 1) * size || + *addr - (*count - 1) * size <= seg->limit) + *count = (*addr - seg->limit - 1) / size + 1; + } + } + ASSERT(*count); + } + + *addr += seg->base; } else if (seg == &vmcb->fs || seg == &vmcb->gs) *addr += seg->base; diff -r 519a74928bd4 -r b75574cb80a3 xen/arch/x86/hvm/vmx/vmx.c --- a/xen/arch/x86/hvm/vmx/vmx.c Tue Nov 28 11:45:54 2006 +0000 +++ b/xen/arch/x86/hvm/vmx/vmx.c Tue Nov 28 11:46:39 2006 +0000 @@ -958,12 +958,13 @@ static void vmx_do_invlpg(unsigned long static int vmx_check_descriptor(int long_mode, unsigned long eip, int inst_len, - enum segment seg, unsigned long *base) -{ - enum vmcs_field ar_field, base_field; - u32 ar_bytes; + enum segment seg, unsigned long *base, + u32 *limit, u32 *ar_bytes) +{ + enum vmcs_field ar_field, base_field, limit_field; *base = 0; + *limit = 0; if ( seg != seg_es ) { unsigned char inst[MAX_INST_LEN]; @@ -1020,26 +1021,32 @@ static int vmx_check_descriptor(int long case seg_cs: ar_field = GUEST_CS_AR_BYTES; base_field = GUEST_CS_BASE; + limit_field = GUEST_CS_LIMIT; break; case seg_ds: ar_field = GUEST_DS_AR_BYTES; base_field = GUEST_DS_BASE; + limit_field = GUEST_DS_LIMIT; break; case seg_es: ar_field = GUEST_ES_AR_BYTES; base_field = GUEST_ES_BASE; + limit_field = GUEST_ES_LIMIT; break; case seg_fs: ar_field = GUEST_FS_AR_BYTES; base_field = GUEST_FS_BASE; + limit_field = GUEST_FS_LIMIT; break; case seg_gs: ar_field = GUEST_FS_AR_BYTES; base_field = GUEST_FS_BASE; + limit_field = GUEST_FS_LIMIT; break; case seg_ss: ar_field = GUEST_GS_AR_BYTES; base_field = GUEST_GS_BASE; + limit_field = GUEST_GS_LIMIT; break; default: BUG(); @@ -1047,10 +1054,13 @@ static int vmx_check_descriptor(int long } if ( !long_mode || seg == seg_fs || seg == seg_gs ) + { *base = __vmread(base_field); - ar_bytes = __vmread(ar_field); - - return !(ar_bytes & 0x10000); + *limit = __vmread(limit_field); + } + *ar_bytes = __vmread(ar_field); + + return !(*ar_bytes & 0x10000); } static void vmx_io_instruction(unsigned long exit_qualification, @@ -1090,7 +1100,7 @@ static void vmx_io_instruction(unsigned if ( test_bit(4, &exit_qualification) ) { /* string instruction */ unsigned long addr, count = 1, base; - u32 ar_bytes; + u32 ar_bytes, limit; int sign = regs->eflags & X86_EFLAGS_DF ? -1 : 1; int long_mode = 0; @@ -1101,20 +1111,86 @@ static void vmx_io_instruction(unsigned #endif addr = __vmread(GUEST_LINEAR_ADDRESS); + if ( test_bit(5, &exit_qualification) ) { /* "rep" prefix */ + pio_opp->flags |= REPZ; + count = regs->ecx; + if ( !long_mode && (vm86 || !(ar_bytes & (1u<<14))) ) + count &= 0xFFFF; + } + /* * In protected mode, guest linear address is invalid if the * selector is null. */ if ( !vmx_check_descriptor(long_mode, regs->eip, inst_len, dir == IOREQ_WRITE ? seg_ds : seg_es, - &base) ) + &base, &limit, &ar_bytes) ) { + if ( !long_mode ) { + vmx_inject_hw_exception(current, TRAP_gp_fault, 0); + return; + } addr = dir == IOREQ_WRITE ? base + regs->esi : regs->edi; - - if ( test_bit(5, &exit_qualification) ) { /* "rep" prefix */ - pio_opp->flags |= REPZ; - count = regs->ecx; - if ( !long_mode && (vm86 || !(ar_bytes & (1u<<14))) ) - count &= 0xFFFF; + } + + if ( !long_mode ) { + unsigned long ea = addr - base; + + /* Segment must be readable for outs and writeable for ins. */ + if ( dir == IOREQ_WRITE ? (ar_bytes & 0xa) == 0x8 + : (ar_bytes & 0xa) != 0x2 ) { + vmx_inject_hw_exception(current, TRAP_gp_fault, 0); + return; + } + + /* Offset must be within limits. */ + ASSERT(ea == (u32)ea); + if ( (u32)(ea + size - 1) < (u32)ea || + (ar_bytes & 0xc) != 0x4 ? ea + size - 1 > limit + : ea <= limit ) + { + vmx_inject_hw_exception(current, TRAP_gp_fault, 0); + return; + } + + /* Check the limit for repeated instructions, as above we checked + only the first instance. Truncate the count if a limit violation + would occur. Note that the checking is not necessary for page + granular segments as transfers crossing page boundaries will be + broken up anyway. */ + if ( !(ar_bytes & (1u<<15)) && count > 1 ) + { + if ( (ar_bytes & 0xc) != 0x4 ) + { + /* expand-up */ + if ( !df ) + { + if ( ea + count * size - 1 < ea || + ea + count * size - 1 > limit ) + count = (limit + 1UL - ea) / size; + } + else + { + if ( count - 1 > ea / size ) + count = ea / size + 1; + } + } + else + { + /* expand-down */ + if ( !df ) + { + if ( count - 1 > -(s32)ea / size ) + count = -(s32)ea / size + 1UL; + } + else + { + if ( ea < (count - 1) * size || + ea - (count - 1) * size <= limit ) + count = (ea - limit - 1) / size + 1; + } + } + ASSERT(count); + } } /* _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |